/*
#include <stdio.h>
#include <unistd.h>
#include <libgen.h>
#include <stdlib.h>
#include <limits.h>
#include <libsafec/safe_str_lib.h>
*/
#include "SystemUtil.h"


void GetExecPath (char *szAbsolutePath,size_t nBufferSize, const char *szMainArgument0)
{
#ifdef _WIN32

   DWORD nWinResult;
   DWORD nLastError;

   int nResult;
   char * pFindPointer;

   nWinResult = GetModuleFileName(NULL, szAbsolutePath, nBufferSize - 1);
   nLastError = GetLastError();

   if (nWinResult == 0)
   {
       nResult = SafeMemSet(szAbsolutePath, nBufferSize, 0, nBufferSize);
   }
   else if (nWinResult >= nBufferSize - 1)
   {
       nResult = SafeMemSet(szAbsolutePath, nBufferSize, 0, nBufferSize);
   }
   else
   {
       nResult = SafeStrrChr(szAbsolutePath, strnlen(szAbsolutePath, nBufferSize), PATH_SEPERATOR, &pFindPointer);
       *pFindPointer = '\0';
   }
   return;

#else

   char szBasenameTemp[PATH_MAX + 1];
   char *szBasename = NULL;
   char szPathEnvVariable[PATH_MAX + 1];
   char *szPathEnvToken = NULL;
   char *pTokenTemp;
   size_t nTokenBufferPos;



   if (readlink ("/proc/self/exe", szAbsolutePath, nBufferSize) != -1)
   {
       dirname(szAbsolutePath);
       SafeStrCat(szAbsolutePath, nBufferSize, "/", 1);
#ifdef __DEBUG        
       printf_s("File:%s, Line:%d, proc 정보상 Path 추출", __FILE__, __LINE__);
#endif        
       return;
   }


   // execve등 강제로 환경변수를 NULL로 입력받는 경우
   if (szMainArgument0 == NULL)
   {
       /* we surrender and give current path instead */
       if (getcwd (szAbsolutePath, nBufferSize) == NULL)
       {
#ifdef __DEBUG        
           printf_s("File:%s, Line:%d, proc 정보가 없어 현재 PATH 추출하려고 하였으나 오류 발생", __FILE__, __LINE__);
#endif        
           return;
       }
       SafeStrCat(szAbsolutePath, sizeof(szAbsolutePath), "/", 1);
#ifdef __DEBUG        
       printf_s("File:%s, Line:%d, proc 정보가 없어 현재 PATH 추출", __FILE__, __LINE__);
#endif        
       return;
   }


   /* argv[0] */
   /* if dest_len < PATH_MAX may cause buffer overflow */
   if ((realpath(szMainArgument0, szAbsolutePath)) && (!access (szAbsolutePath, F_OK)))
   {
       dirname (szAbsolutePath);
       SafeStrCat(szAbsolutePath, sizeof(szAbsolutePath), "/", 1);
#ifdef __DEBUG        
       printf_s("File:%s, Line:%d, arg0에 저장된 폴더가 정상 폴더이며 접근권한이 있음", __FILE__, __LINE__);
#endif        
       return;
   }

   /* Current path */
   SafeStrCopy(szBasenameTemp, sizeof(szBasenameTemp), szMainArgument0);
   szBasename = basename(szBasenameTemp);
   if (getcwd(szAbsolutePath, nBufferSize - strnlen_s(szBasename, sizeof(szBasename)) - 1) == NULL)
   {
#ifdef __DEBUG        
       printf_s("File:%s, Line:%d, 현재 PATH 추출 오류 발생", __FILE__, __LINE__);
#endif        
       return;
   }

   SafeStrCat(szAbsolutePath, sizeof(szAbsolutePath), "/", 1);
   SafeStrCat(szAbsolutePath, sizeof(szAbsolutePath), szBasename, strnlen_s(szBasename, sizeof(szBasename)));
   if (access(szAbsolutePath, F_OK) == 0)
   {
       dirname(szAbsolutePath);
       SafeStrCat(szAbsolutePath, sizeof(szAbsolutePath), "/", 1);
#ifdef __DEBUG        
       printf_s("File:%s, Line:%d, 현재 폴더에 실행 파일이 있으면 리턴", __FILE__, __LINE__);
#endif        
       return;
   }

   /* 환경변수 PATH 값 추출 */
   SafeStrCopy(szPathEnvVariable, sizeof(szPathEnvVariable), getenv("PATH"));
   if (szPathEnvVariable != NULL)
   {
       nBufferSize--;
       nTokenBufferPos = sizeof(szPathEnvVariable);
       for (szPathEnvToken = strtok_s(szPathEnvVariable, &nTokenBufferPos, ":", &pTokenTemp); szPathEnvToken != NULL; szPathEnvToken = strtok_s(NULL, &nTokenBufferPos, ":", &pTokenTemp))
       {
           SafeStrNCopy(szAbsolutePath, sizeof(szAbsolutePath), szPathEnvToken, nBufferSize);
           SafeStrCat(szAbsolutePath, sizeof(szAbsolutePath), "/", 1);
           SafeStrCat(szAbsolutePath, sizeof(szAbsolutePath), szBasename, nBufferSize);

           if (access(szAbsolutePath, F_OK) == 0)
           {
               //free(PathEnvVariable);
               dirname(szAbsolutePath);
               SafeStrCat(szAbsolutePath, sizeof(szAbsolutePath), "/", 1);
#ifdef __DEBUG        
               printf_s("File:%s, Line:%d, PATH에 실행파일이 있으면 리턴", __FILE__, __LINE__);
#endif        
               return;
           }
       }
       //free(PathEnvVariable);
       nBufferSize++;
   }

   /* again someone has use execve: we dont knowe the executable name; we surrender and give instead current path */
   if (getcwd(szAbsolutePath, nBufferSize - 1) == NULL)
   {
#ifdef __DEBUG        
       printf_s("File:%s, Line:%d, execve와 같은 함수를 사용하여 실행파일명을 알수 없을 경우 현재 Path 추출 오류", __FILE__, __LINE__);
#endif        
       return;
   }

#ifdef __DEBUG        
   printf_s("File:%s, Line:%d, execve와 같은 함수를 사용하여 실행파일명을 알수 없을 경우 현재 Path 리턴", __FILE__, __LINE__);
#endif        
   SafeStrCat(szAbsolutePath, sizeof(szAbsolutePath), "/", 1);
   return;

#endif

}

long TimeDiff(struct tm *pStartTM, int nStartMilliSeconds, struct tm *pEndTM, int nEndMilliSeconds)
{
    time_t sStartT;
    time_t sEndT;
    double fTimeDiff;

    sStartT = mktime(pStartTM);
    sEndT = mktime(pEndTM);

    fTimeDiff = difftime(sEndT, sStartT);
    fTimeDiff = fTimeDiff * 1000 + nEndMilliSeconds - nStartMilliSeconds;
    return (long)fTimeDiff;
}

#ifdef _WIN32
void uuid_generate(uuid_t *pUUID)
{
    GUID Guid;
    TCHAR szErrorMsg[MAX_STRING_SIZE + 1];
    if ((UuidCreate(&Guid)) != S_OK)
    {
        // use FormatMessage & GetLastError
        FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
                       NULL,
                       GetLastError(),
                       MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
                       szErrorMsg,
                       sizeof(szErrorMsg),
                       NULL);
        WriteTraceLog(LogType_ErrorOut, LogLevel_Debug, __FILE__, __LINE__, __FUNCTION__, "MoveFile error[%s]", szErrorMsg);
    }
    else
    {
        SafeMemCopy(pUUID, sizeof(*pUUID), &Guid, sizeof(GUID));
    }
}
int uuid_parse(const char *in, uuid_t *pUUID)
{
    if (UuidFromString(in ,pUUID) != RPC_S_OK)
    {
        return SYS_ERR_UUID_FORMAT;
    }
    else
    {
        return SYS_ERR_SUCCESS;
    }
}
void uuid_unparse(uuid_t *pUUID, char *pUUIDString)
{
    //RPC_CSTR pUUIDStringTemp;
    TCHAR szErrorMsg[MAX_STRING_SIZE + 1];
    RPC_CSTR pUUIDStringTemp;

    if (UuidToString(pUUID, &pUUIDStringTemp) != RPC_S_OK)
    {
        FormatMessage( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
                       NULL,
                       GetLastError(),
                       MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
                       szErrorMsg,
                       sizeof(szErrorMsg),
                       NULL);
        WriteTraceLog(LogType_ErrorOut, LogLevel_Debug, __FILE__, __LINE__, __FUNCTION__, "MoveFile error[%s]", szErrorMsg);
    }
    else
    {
        SafeStrCopy(pUUIDString, UUID_STRING_SIZE + 1, (char *)pUUIDStringTemp);
        RpcStringFree(&pUUIDStringTemp);
    }
}

char *realpath(const char *path, char resolved_path[PATH_MAX])
{
    char *return_path = 0;

    if (path) //Else EINVAL
    {
        if (resolved_path)
        {
            return_path = resolved_path;
        }
        else
        {
            //Non standard extension that glibc uses
            return_path = malloc(PATH_MAX);
        }

        if (return_path) //Else EINVAL
        {
            //This is a Win32 API function similar to what realpath() is supposed to do
            size_t size = GetFullPathNameA(path, PATH_MAX, return_path, 0);

            //GetFullPathNameA() returns a size larger than buffer if buffer is too small
            if (size > PATH_MAX)
            {
                if (return_path != resolved_path) //Malloc'd buffer - Unstandard extension retry
                {
                    size_t new_size;

                    free(return_path);
                    return_path = malloc(size);

                    if (return_path)
                    {
                        new_size = GetFullPathNameA(path, size, return_path, 0); //Try again

                        if (new_size > size) //If it's still too large, we have a problem, don't try again
                        {
                            free(return_path);
                            return_path = 0;
                            errno = ENAMETOOLONG;
                        }
                        else
                        {
                            size = new_size;
                        }
                    }
                    else
                    {
                    //I wasn't sure what to return here, but the standard does say to return EINVAL
                    //if resolved_path is null, and in this case we couldn't malloc large enough buffer
                        errno = EINVAL;
                    }
                }
                else //resolved_path buffer isn't big enough
                {
                    return_path = 0;
                    errno = ENAMETOOLONG;
                }
            }

            //GetFullPathNameA() returns 0 if some path resolve problem occured
            if (!size)
            {
                if (return_path != resolved_path) //Malloc'd buffer
                {
                    free(return_path);
                }

                return_path = 0;

                //Convert MS errors into standard errors
                switch (GetLastError())
                {
                case ERROR_FILE_NOT_FOUND:
                    errno = ENOENT;
                    break;

                case ERROR_PATH_NOT_FOUND: case ERROR_INVALID_DRIVE:
                    errno = ENOTDIR;
                    break;

                case ERROR_ACCESS_DENIED:
                    errno = EACCES;
                    break;

                default: //Unknown Error
                    errno = EIO;
                    break;
                }
            }

            //If we get to here with a valid return_path, we're still doing good
            if (return_path)
            {
                struct stat stat_buffer;

                //Make sure path exists, stat() returns 0 on success
                if (stat(return_path, &stat_buffer))
                {
                    if (return_path != resolved_path)
                    {
                        free(return_path);
                    }

                    return_path = 0;
                    //stat() will set the correct errno for us
                }
                //else we succeeded!
            }
        }
        else
        {
          errno = EINVAL;
        }
    }
    else
    {
        errno = EINVAL;
    }
}

void ASCIIToUTF8(char *pASCII, int nASCSize, char *pUTF8, int nUTF8Size)
{
    WCHAR szUniCode[MAX_STRING_SIZE + 1];

    memset(szUniCode, 0, sizeof(szUniCode));
    memset(pUTF8, 0, nUTF8Size);

    MultiByteToWideChar(CP_ACP, 0, pASCII, -1, szUniCode, sizeof(szUniCode));
    WideCharToMultiByte(CP_UTF8, 0, szUniCode, -1, pUTF8, nUTF8Size, NULL, NULL);
}

void UTF8ToASCII(char *pUTF8, int nUTF8Size, char *pASCII, int nASCSize)
{
    WCHAR szUniCode[MAX_STRING_SIZE + 1];

    memset(szUniCode, 0, sizeof(szUniCode));
    memset(pASCII, 0, nASCSize);

    MultiByteToWideChar(CP_UTF8, 0, pUTF8, -1, szUniCode, sizeof(szUniCode));
    WideCharToMultiByte(CP_ACP, 0, szUniCode, -1, pASCII, nASCSize, NULL, NULL);
}

#endif
