/*
#include <stddef.h>
#include <libsafec/safe_lib.h>
#include <libsafec/safe_str_lib.h>
#include <libsafec/safe_mem_lib.h>
#include <stdio.h>
#include <time.h>
#include <pthread.h>
#include <linux/limits.h>
#include <stdlib.h>
#include <ctype.h>
#include "Common.h"
#include "Config.h"
*/
#include "Log.h"

/******************************************************************************
*
*   File Name       : Log.c
*   Desc.           : 로그 파일 처리 공통모듈
*   Creator         : Kim Dalhyun
*   Rev. Info.      : 1.0 - 최초작성(2018.06.20 : Kim Dalhyun)
*
******************************************************************************/


/* 2018.08.27 Kim Dalhyun Config에서 공통함수로 처리하도록 변경 */
/*
struct LOGLEVELINFO g_LogTypeTagStrings[] = 
{
    {0x00, "OFF"},
    {0x01, "TRACE"},
    {0x02, "DEBUG"},
    {0x04, "INFO"},
    {0x08, "WARNING"},
    {0x10, "ERROR"},
    {0x20, "FATAL"}
};
*/
struct VALUE_STRING_PAIR_INFO g_LogTypeTagStrings[] =
{
    {0x00, "OFF"},
    {0x01, "TRACE"},
    {0x02, "DEBUG"},
    {0x04, "INFO"},
    {0x08, "WARNING"},
    {0x10, "ERROR"},
    {0x20, "FATAL"}
};

#ifdef _DEBUG
int g_bPrintLogToScreen = true;
#else
int g_bPrintLogToScreen = false;
#endif

pthread_mutex_t g_sLogMutex = PTHREAD_MUTEX_INITIALIZER;

char g_szLogFilePath[PATH_MAX + 1];
struct LOGFILEINFO g_saLogFileInfo[LogType_Count];

int g_bLogConfigInitialized = false;



/*
 * TODO : 씨큐어 함수가 문자열 최대길이 4096까지만 지원하므로 이로 인한 영향도 확인이 필요함
 *         printf도 %s의 최대 사이즈가 4095이다. 즉 format string 자체의 길이 제한은 없으나 뒤에 전달되는 argument의 개별 사이즈 제한이 있다.
 *         씨큐어 함수의 경우 데이터 길이가 짧아도 변수 길이가 길면 해당 패러미터를 패스 하여 출력되지 않는다.
 *         vsnprintf를 사용할 경우 %s로 변환되는 실제 변수 길이가 4096을 초과하니 해당 데이터가 출력되지 않았다.
 *         Log 함수중 데이터를 포함하여 출력하는 Log 함수의 경우 뒤에 데이터부를 별도로 추가해야 한다.
 *         데이터 변수는 %s를 통하지 않고 바로 출력되는 함수를 이용하여 출력한다.
 */

int InitLogConfig()
{
    //char szLogFilePath[MAX_STRING_SIZE + 1];
    char szLogFileName[MAX_STRING_SIZE + 1];
    char szStringBuffer[MAX_STRING_SIZE + 1];
    int nLogLevelFlags;
    
    
    pthread_mutex_lock(&g_sLogMutex);
    SafeMemSet(g_saLogFileInfo, sizeof(g_saLogFileInfo), 0, sizeof(g_saLogFileInfo));
    
    if (GetConfigValue(LOG_LOG_FILE_ROOT_KEY, g_szLogFilePath, sizeof(g_szLogFilePath)) != CONF_ERR_SUCCESS)
    {
        printf("Can't find log file path. Check the config file\n");
        return LOG_ERR_CONFIG;
    }
    
    //if (GetConfigValue(LOG_SYS_LOG_FILENAME_KEY, szLogFileName, MAX_STRING_SIZE) == EOK)
    if (GetConfigValue(LOG_SYS_LOG_FILENAME_KEY, g_saLogFileInfo[LogType_SystemOut].szFileName, sizeof(g_saLogFileInfo[LogType_SystemOut].szFileName)) == CONF_ERR_SUCCESS)
    {
        if (GetConfigValue(LOG_SYS_LOG_LOGLEVEL_KEY, szStringBuffer, sizeof(szStringBuffer)) == CONF_ERR_SUCCESS)
        {
            if (ParsingFlagString(szStringBuffer,MAX_STRING_SIZE, g_LogTypeTagStrings, sizeof(g_LogTypeTagStrings) / sizeof(struct VALUE_STRING_PAIR_INFO), &nLogLevelFlags) != CONF_ERR_SUCCESS)
            {
                // 디폴트 값은 결정 필요 : 현재 Fatal, Error, Info 출력
                nLogLevelFlags = LogLevel_Fatal | LogLevel_Info;
            }
        }
        //snprintf(g_saLogFileInfo[LogType_SystemOut].szFileName, MAX_STRING_SIZE, "%s/%s", szLogFilePath, szLogFileName);
        g_saLogFileInfo[LogType_SystemOut].eLogType = LogType_SystemOut; 
        g_saLogFileInfo[LogType_SystemOut].nLogLevelFlags = nLogLevelFlags;
        if (GetConfigValue(LOG_SYS_LOG_FILENAME_USE_PROCNAME_KEY, szStringBuffer, sizeof(szStringBuffer)) == CONF_ERR_SUCCESS)
        {
            if (ParsingYESNO(szStringBuffer, sizeof(szStringBuffer), &g_saLogFileInfo[LogType_SystemOut].bUseProcessnameInFilename) != CONF_ERR_SUCCESS)
            {
                g_saLogFileInfo[LogType_SystemOut].bUseProcessnameInFilename = false;
            }
        }
        else
        {
            g_saLogFileInfo[LogType_SystemOut].bUseProcessnameInFilename = false;
        }

    }

    //if (GetConfigValue(LOG_ERROR_LOG_FILENAME_KEY, szLogFileName, MAX_STRING_SIZE) == EOK)
    if (GetConfigValue(LOG_ERROR_LOG_FILENAME_KEY, g_saLogFileInfo[LogType_ErrorOut].szFileName, sizeof(g_saLogFileInfo[LogType_ErrorOut].szFileName)) == CONF_ERR_SUCCESS)
    {
        if (GetConfigValue(LOG_ERROR_LOG_LEVEL_KEY, szStringBuffer, sizeof(szStringBuffer)) == CONF_ERR_SUCCESS)
        {

            if (ParsingFlagString(szStringBuffer,sizeof(szStringBuffer), g_LogTypeTagStrings, sizeof(g_LogTypeTagStrings) / sizeof(struct VALUE_STRING_PAIR_INFO), &nLogLevelFlags) != CONF_ERR_SUCCESS)
            {
                // 디폴트 값은 결정 필요 : 현재 Fatal, Error, Info 출력
                nLogLevelFlags = LogLevel_Fatal | LogLevel_Error;
            }
        }
        //snprintf(g_saLogFileInfo[LogType_ErrorOut].szFileName, MAX_STRING_SIZE, "%s/%s", szLogFilePath, szLogFileName);
        g_saLogFileInfo[LogType_ErrorOut].eLogType = LogType_ErrorOut; 
        g_saLogFileInfo[LogType_ErrorOut].nLogLevelFlags = nLogLevelFlags;
        if (GetConfigValue(LOG_ERROR_LOG_FILENAME_USE_PROCNAME_KEY, szStringBuffer, sizeof(szStringBuffer)) == CONF_ERR_SUCCESS)
        {
            if (ParsingYESNO(szStringBuffer, sizeof(szStringBuffer), &g_saLogFileInfo[LogType_ErrorOut].bUseProcessnameInFilename) != CONF_ERR_SUCCESS)
            {
                g_saLogFileInfo[LogType_ErrorOut].bUseProcessnameInFilename = true;
            }
        }
        else
        {
            g_saLogFileInfo[LogType_ErrorOut].bUseProcessnameInFilename = true;
        }
    }

    //if (GetConfigValue(LOG_STANDARD_LOG_FILENAME_KEY, szLogFileName, MAX_STRING_SIZE) == EOK)
    if (GetConfigValue(LOG_STANDARD_LOG_FILENAME_KEY, g_saLogFileInfo[LogType_StandardOut].szFileName, sizeof(g_saLogFileInfo[LogType_StandardOut].szFileName)) == CONF_ERR_SUCCESS)
    {
        if (GetConfigValue(LOG_STANDARD_LOG_LEVEL_KEY, szStringBuffer, sizeof(szStringBuffer)) == CONF_ERR_SUCCESS)
        {
            if (ParsingFlagString(szStringBuffer,sizeof(szStringBuffer), g_LogTypeTagStrings, sizeof(g_LogTypeTagStrings) / sizeof(struct VALUE_STRING_PAIR_INFO), &nLogLevelFlags) != CONF_ERR_SUCCESS)
            {
                // 디폴트 값은 결정 필요 : 현재 Fatal, Error, Info 출력
                nLogLevelFlags = LogLevel_Trace | LogLevel_Debug | LogLevel_Warning;
            }
        }
        //snprintf(g_saLogFileInfo[LogType_StandardOut].szFileName, MAX_STRING_SIZE, "%s/%s", szLogFilePath, szLogFileName);
        g_saLogFileInfo[LogType_StandardOut].eLogType = LogType_StandardOut; 
        g_saLogFileInfo[LogType_StandardOut].nLogLevelFlags = nLogLevelFlags;
        if (GetConfigValue(LOG_STANDARD_LOG_FILENAME_USE_PROCNAME_KEY, szStringBuffer, sizeof(szStringBuffer)) == CONF_ERR_SUCCESS)
        {
            if (ParsingYESNO(szStringBuffer, sizeof(szStringBuffer), &g_saLogFileInfo[LogType_StandardOut].bUseProcessnameInFilename) != CONF_ERR_SUCCESS)
            {
                g_saLogFileInfo[LogType_StandardOut].bUseProcessnameInFilename = true;
            }
        }
        else
        {
            g_saLogFileInfo[LogType_StandardOut].bUseProcessnameInFilename = true;
        }
    }

    //if (GetConfigValue(LOG_COMM_LOG_FILENAME_KEY, szLogFileName, MAX_STRING_SIZE) == EOK)
    if (GetConfigValue(LOG_COMM_LOG_FILENAME_KEY, g_saLogFileInfo[LogType_CommOut].szFileName, sizeof(g_saLogFileInfo[LogType_CommOut].szFileName)) == CONF_ERR_SUCCESS)
    {
        if (GetConfigValue(LOG_COMM_LOG_LEVEL_KEY, szStringBuffer, sizeof(szStringBuffer)) == CONF_ERR_SUCCESS)
        {
            if (ParsingFlagString(szStringBuffer, sizeof(szStringBuffer), g_LogTypeTagStrings, sizeof(g_LogTypeTagStrings) / sizeof(struct VALUE_STRING_PAIR_INFO), &nLogLevelFlags) != CONF_ERR_SUCCESS)
            {
                // 디폴트 값은 결정 필요 : 현재 Fatal, Error, Info 출력
                nLogLevelFlags = LogLevel_Info;
            }
        }
        //snprintf(g_saLogFileInfo[LogType_CommOut].szFileName, MAX_STRING_SIZE, "%s/%s", szLogFilePath, szLogFileName);
        g_saLogFileInfo[LogType_CommOut].eLogType = LogType_CommOut; 
        g_saLogFileInfo[LogType_CommOut].nLogLevelFlags = nLogLevelFlags;
        if (GetConfigValue(LOG_COMM_LOG_FILENAME_USE_PROCNAME_KEY, szStringBuffer, sizeof(szStringBuffer)) == CONF_ERR_SUCCESS)
        {
            if (ParsingYESNO(szStringBuffer, sizeof(szStringBuffer), &g_saLogFileInfo[LogType_CommOut].bUseProcessnameInFilename) != CONF_ERR_SUCCESS)
            {
                g_saLogFileInfo[LogType_CommOut].bUseProcessnameInFilename = true;
            }
        }
        else
        {
            g_saLogFileInfo[LogType_CommOut].bUseProcessnameInFilename = true;
        }
    }

    if (GetConfigValue(LOG_MQ_LOG_FILENAME_KEY, g_saLogFileInfo[LogType_MQOut].szFileName, sizeof(g_saLogFileInfo[LogType_MQOut].szFileName)) == CONF_ERR_SUCCESS)
    {
        if (GetConfigValue(LOG_MQ_LOG_LEVEL_KEY, szStringBuffer, sizeof(szStringBuffer)) == CONF_ERR_SUCCESS)
        {
            if (ParsingFlagString(szStringBuffer, sizeof(szStringBuffer), g_LogTypeTagStrings, sizeof(g_LogTypeTagStrings) / sizeof(struct VALUE_STRING_PAIR_INFO), &nLogLevelFlags) != CONF_ERR_SUCCESS)
            {
                // 디폴트 값은 결정 필요 : 현재 Fatal, Error, Info 출력
                nLogLevelFlags = LogLevel_Info;
            }
        }
        //snprintf(g_saLogFileInfo[LogType_CommOut].szFileName, MAX_STRING_SIZE, "%s/%s", szLogFilePath, szLogFileName);
        g_saLogFileInfo[LogType_MQOut].eLogType = LogType_CommOut;
        g_saLogFileInfo[LogType_MQOut].nLogLevelFlags = nLogLevelFlags;
        if (GetConfigValue(LOG_MQ_LOG_FILENAME_USE_PROCNAME_KEY, szStringBuffer, sizeof(szStringBuffer)) == CONF_ERR_SUCCESS)
        {
            if (ParsingYESNO(szStringBuffer, sizeof(szStringBuffer), &g_saLogFileInfo[LogType_MQOut].bUseProcessnameInFilename) != CONF_ERR_SUCCESS)
            {
                g_saLogFileInfo[LogType_MQOut].bUseProcessnameInFilename = true;
            }
        }
        else
        {
            g_saLogFileInfo[LogType_CommOut].bUseProcessnameInFilename = true;
        }
    }
 
    pthread_mutex_unlock(&g_sLogMutex);
    g_bLogConfigInitialized = true;
    return CONF_ERR_SUCCESS;
}

/* 2018.08.27 Kim Dal Hyun : Move to Config.c
int ParsingBoolean(const char *szValue, const int nInputValueLength)
{
    char szValueBuffer[MAX_STRING_SIZE + 1];

    int nValueLength;
    int nPosition;
    int nStrCmpResult;
    int bReturnValue;

    bReturnValue = false;
    nValueLength = strnlen_s(szValue, nInputValueLength);

    for (nPosition = 0; nPosition < nValueLength && nPosition < MAX_STRING_SIZE; nPosition++)
    {
        szValueBuffer[nPosition] = (char)toupper(szValue[nPosition]);
    }
    szValueBuffer[nPosition] = (char)NULL;

    SafeStrCmp(szValueBuffer, MAX_STRING_SIZE, "TRUE", &nStrCmpResult);

    if (nStrCmpResult == 0)
    {
        bReturnValue = true;
    }

    return bReturnValue;

}
*/
/* 2018.08.27 Kim Dalhyun : Move to Config.c ParsingLogTypeTag -> ParsingFlagString
int ParsingLogTypeTag(const char *szTags, const int nInputTagLength)
{
    char szTagBuffer[MAX_STRING_SIZE + 1];
    int nLogType = -1;
    int nSeperatorPosition = 0;
    int nPrevSeperatorPosition = 0;
    int nTagLength = 0;
    int nTagBufferPosition = 0;
    int nSearchPosition;
    int nTagStringPosition;
    int nStrCmpResult;
    
    nTagLength = strnlen_s(szTags, nInputTagLength);
    
    for (nSearchPosition = 0; nSearchPosition < nTagLength;nSearchPosition++)
    {
        if (szTags[nSearchPosition] == CONF_LIST_SEPERATOR || nSearchPosition == (nTagLength - 1))
        {
            if (nSearchPosition == (nTagLength - 1))
            {
                if (nTagBufferPosition == MAX_STRING_SIZE)
                {
                    return -1;
                }
                szTagBuffer[nTagBufferPosition++] = (char)toupper(szTags[nSearchPosition]);
            }
            nPrevSeperatorPosition = nSeperatorPosition + 1;
            nSeperatorPosition = nSearchPosition;
            szTagBuffer[nTagBufferPosition] = '\0';
            nTagBufferPosition = 0;

            for (nTagStringPosition = 0; nTagStringPosition < sizeof(g_LogTypeTagStrings) / sizeof(struct LOGLEVELINFO); nTagStringPosition++)
            {
                SafeStrCmp(szTagBuffer, MAX_STRING_SIZE, g_LogTypeTagStrings[nTagStringPosition].szLogLevel, &nStrCmpResult);
                if (nStrCmpResult == 0)
                {
                    if (nLogType == -1)
                        nLogType = 0;
                    nLogType = nLogType | g_LogTypeTagStrings[nTagStringPosition].nBitFlag;
                    break;
                }                
            }
        }
        else
        {
            if (nTagBufferPosition == MAX_STRING_SIZE)
            {
                return -1;
            }
            szTagBuffer[nTagBufferPosition++] = (char)toupper(szTags[nSearchPosition]);

        }
    }
    return nLogType;
}
*/
/******************************************************************************
*
*   Function Name   : WriteLog
*   Desc.           : 1. 지정된 로그 레벨과 파일의 로그 레벨을 비교하여 저장해야 될 경우 처리
*                     2. 저장해야 하는 경우 szFormat과 뒤에 입력되는 가변 매개변수 리스트를 이용하여 문자열생성하여 파일 저장
*                        (가변 매개면수 리스트 : variable argument list - va_start, va_end, vsprintf참고)
*   Input           : nLogType - 로그 타입 값(헤더파일 참고)
                      nLogLevel - 로그 레벨값(헤더파일 참고)
                      szFormat - 로그 출력 문자열 양식(vsprintf와 vs_start 참고), ...(가변 매개변수 리스트)
*   Output          : 
*   Rev. Info.      : 1.0 - 최초작성(2018.06.20 : Kim Dalhyun)
*
******************************************************************************/

/*
 *
 * TODO :   1. 파일명에 프로세스명 사용여부 옵션화(System 로그의 경우 로그 양이 적으므로 일반적으로 하나의 로그에 기록함
 *           2. 통신 로그일 경우 로그에 통신 채널 표시되도록 하거나 파일명에 채널 추가
 *
 */

void WriteTraceLog(enum LogType eLogType, enum LogLevel eLogLevel, const char *szFilename, long nLinenumber, const char *szFunctionname, const char *szFormat, ...)
{
    char szTimeString[LOG_TIME_STRING_SIZE + 1];
    char szTraceInfo[MAX_STRING_SIZE + 1];
    char szBuffer[LOG_MAX_BUFFER_SIZE + 1];
    char szFileName[PATH_MAX + 1];
    struct timespec sTimeSpec;
    time_t sNow;
    struct tm tNow;
    //char szTimeString[LOG_TIME_STRING_SIZE + 1];
    long nMillisecond;
    va_list ArgList;
    FILE *pLogFile;
    FILE *pAddr2LineResult;
    int nSafeLibReturn;
    int nStackTracesCount;
    int nStackTraceIndex;
    void *pStackTraces[LOG_MAX_TRACE_STACK_FRAMES];
    char **pMessages;
    int nTraceSize;
    
    pthread_mutex_lock(&g_sLogMutex);

    if (g_bLogConfigInitialized == false)
    {
        pthread_mutex_unlock(&g_sLogMutex);
        return;
    }

    // 로그 파일 설정에 해당 로그 레벨이 설정되어 있지 않으면 로그 파일에 저장하지 않는다.
    if ((eLogLevel & g_saLogFileInfo[eLogType].nLogLevelFlags) == 0)
    {
#ifdef _DEBUG
        if (g_bPrintLogToScreen)
        {
            printf("Function:%s : LogLevel Flag is not set[LogType:%02X,LogLevel-config:%02X,input:%02X]\n", __FUNCTION__, eLogType, g_saLogFileInfo[eLogType].nLogLevelFlags, eLogLevel);
        }
#endif
        pthread_mutex_unlock(&g_sLogMutex);
        return;
    }
    
    clock_gettime(CLOCK_REALTIME, &sTimeSpec);
    sNow = sTimeSpec.tv_sec;
    nMillisecond = sTimeSpec.tv_nsec / 1.0e6; // nanoseconds to milliseconds
#ifdef __WIN32
    localtime_s(&tNow, &sNow);
#else
    localtime_r(&sNow, &tNow);
#endif
    
    nSafeLibReturn = snprintf(szFileName, sizeof(szFileName), "%s%c%s_%s_%04d%02d%02d.log", g_szLogFilePath, PATH_SEPERATOR, g_szProcessname, g_saLogFileInfo[eLogType].szFileName, tNow.tm_year + YEAR_OFFSET, tNow.tm_mon + MONTH_OFFSET, tNow.tm_mday);
    
    nSafeLibReturn = snprintf(szTimeString, sizeof(szTimeString), "%02d:%02d:%02d.%03ld] ", tNow.tm_hour, tNow.tm_min, tNow.tm_sec, nMillisecond);
    
    if ((eLogLevel & (LogLevel_Error | LogLevel_Fatal | LogLevel_Trace | LogLevel_Debug)) != 0)
    {
        nSafeLibReturn = snprintf(szTraceInfo, sizeof(szTraceInfo), "File:%s, Line:%ld, Function:%s ", szFilename, nLinenumber, szFunctionname);
        nTraceSize = strnlen_s(szTraceInfo, sizeof(szTraceInfo));
    }
    else
    {
        szTraceInfo[0] = '\0';
        nTraceSize = 0;
    }
    
    va_start(ArgList, szFormat);
    vsnprintf(szBuffer, sizeof(szBuffer), szFormat, ArgList);
    va_end(ArgList);

    if (g_bPrintLogToScreen)
    {
        printf(szTimeString);
        printf(szTraceInfo);
        printf(szBuffer);
        printf("\n");
        fflush(stdout);
    }
#ifdef __SECURE_CODING
    nSafeLibReturn = fopen_s(&pLogFile, szFileName, "a");
    if (nSafeLibReturn != EOK)
    {
#ifdef _DEBUG
        if (g_bPrintLogToScreen)
        {
            strerror_s(szTraceInfo, sizeof(szTraceInfo), nSafeLibReturn);
            printf("Log file open error[finename:%s, Msg:%s]!\n", szFileName, szTraceInfo);
        }
#endif
        pthread_mutex_unlock(&g_sLogMutex);
        return;
    }
#else
    pLogFile = fopen(szFileName, "a");
    if (pLogFile == NULL)
    {
#ifdef _DEBUG
        if (g_bPrintLogToScreen)
        {
            printf("Log file open error[finename:%s, Msg:%s]!\n", szFileName, strerror(errno));
        }
#endif
        pthread_mutex_unlock(&g_sLogMutex);
        return;
    }

#endif
    fputs(szTimeString, pLogFile);
    fputs(szTraceInfo, pLogFile);
    fputs(szBuffer, pLogFile);
    fputs("\n", pLogFile);

    // backtrace기능 윈도우즈용 찾아서 추가 필요
#ifdef __linux__
    nStackTracesCount = backtrace(pStackTraces, LOG_MAX_TRACE_STACK_FRAMES);
    pMessages = backtrace_symbols(pStackTraces, nStackTracesCount);
    if (pMessages == NULL)
    {
        fputs("Can't get symbols!\n", pLogFile);
    }
    else
    {
        for (nStackTraceIndex = 0; nStackTraceIndex < nStackTracesCount; nStackTraceIndex++)
        {
            if (pStackTraces[nStackTraceIndex] != NULL)
            {
                snprintf(szBuffer, sizeof(szBuffer) - 1, "[TRACE:%02d]%s\n", nStackTraceIndex, pMessages[nStackTraceIndex]);
                szBuffer[sizeof(szBuffer - 1)] = '\0';
                fputs(szBuffer, pLogFile);
                if (g_bPrintLogToScreen)
                {
                    printf(szBuffer);
                }

                snprintf(szBuffer, sizeof(szBuffer) - 1, "addr2line %p -s -e %s/%s", pStackTraces, g_szProcessPath, g_szProcessname);
                pAddr2LineResult = popen(szBuffer, "r");
                if (pAddr2LineResult == NULL)
                {
                    fputs("-- no source could be retrieved - \n", pLogFile);
                    if (g_bPrintLogToScreen)
                    {
                        printf("-- no source could be retrieved - \n");
                    }
                    continue;
                }
                while (fgets(szBuffer, sizeof(szBuffer) - 1, pAddr2LineResult) != NULL)
                {
                    fputs(szTimeString, pLogFile);
                    fputs(szBuffer, pLogFile);
                    fputs("\n", pLogFile);
                    if (g_bPrintLogToScreen)
                    {
                        printf(szTimeString);
                        printf(szBuffer);
                        printf("\n");
                        fflush(stdout);
                    }
                }
                pclose(pAddr2LineResult);
            }
            else
            {
                fputs("-- no source could be retrieved - \n", pLogFile);
                if (g_bPrintLogToScreen)
                {
                    printf("-- no source could be retrieved - \n");
                }
            }
        }
    }
    SafeFree((void ***)&pMessages);
#endif
    fclose(pLogFile);
    pthread_mutex_unlock(&g_sLogMutex);
}

/******************************************************************************
*
*   Function Name   : WriteLog
*   Desc.           : 1. 지정된 로그 레벨과 파일의 로그 레벨을 비교하여 저장해야 될 경우 처리
*                     2. 저장해야 하는 경우 szFormat과 뒤에 입력되는 가변 매개변수 리스트를 이용하여 문자열생성하여 파일 저장
*                        (가변 매개면수 리스트 : variable argument list - va_start, va_end, vsprintf참고)
*   Input           : nLogType - 로그 타입 값(헤더파일 참고)
*                     nLogLevel - 로그 레벨값(헤더파일 참고)
*                     szFormat - 로그 출력 문자열 양식(vsprintf와 vs_start 참고), ...(가변 매개변수 리스트)
*   Output          :
*   Rev. Info.      : 1.0 - 최초작성(2018.06.20 : Kim Dalhyun)
*
******************************************************************************/

/*
 *
 * TODO :   1. 파일명에 프로세스명 사용여부 옵션화(System 로그의 경우 로그 양이 적으므로 일반적으로 하나의 로그에 기록함
 *           2. 통신 로그일 경우 로그에 통신 채널 표시되도록 하거나 파일명에 채널 추가
 *           3. 통실로그 관련 정리 필요
 *              - socket별 통신 로그 관리여부 결정
 *              - 채널별 통신 로그 관리여부 결정
 *              - 둘다 관리
 *              - 로그 폴더에는 전체 수신 및 송신 로그를 남기고
 *           4. 최종결정
 *              - 전체 송수신 로그는 파일명에 socket fd 번호를 추가하여 log 폴더에 저장한다.
 *              - log 폴더 아래에 comm 폴더를 생성하여  socket별 채널별 로그를 저장한다.
 *              - 실제 로그 검색은 ELK Stack을 이용하여 처리할 예정이므로 크게 문제 되지는 않을 것 같다.
 *              - 안정화되면 둘중 하나만 저장하게 설정으로 변경 가능해야 함
 */

void WriteLog(enum LogType eLogType, enum LogLevel eLogLevel, const char *szFormat, ...)
{
    char szTimeString[LOG_TIME_STRING_SIZE + 1];
    char szBuffer[LOG_MAX_BUFFER_SIZE + 1];
    char szFileName[PATH_MAX + 1];
    char szErrorMsg[MAX_STRING_SIZE + 1];
    struct timespec sTimeSpec;
    time_t sNow;
    struct tm tNow;
    //char szTimeString[LOG_TIME_STRING_SIZE + 1];
    long nMillisecond;
    va_list ArgList;
    FILE *pLogFile;
    int nSafeLibReturn;
    
    pthread_mutex_lock(&g_sLogMutex);
    // 로그 파일 설정에 해당 로그 레벨이 설정되어 있지 않으면 로그 파일에 저장하지 않는다.
    if ((eLogLevel & g_saLogFileInfo[eLogType].nLogLevelFlags) == 0)
    {
#ifdef _DEBUG
        if (g_bPrintLogToScreen)
        {
            printf("Function:%s : LogLevel Flag is not set[LogType:%02X,LogLevel-config:%02X,input:%02X]\n", __FUNCTION__, eLogType, g_saLogFileInfo[eLogType].nLogLevelFlags, eLogLevel);
        }
#endif
        pthread_mutex_unlock(&g_sLogMutex);
        return;
    }
    
    clock_gettime(CLOCK_REALTIME, &sTimeSpec);
    sNow = sTimeSpec.tv_sec;
    nMillisecond = sTimeSpec.tv_nsec / 1.0e6; // nanoseconds to milliseconds
#ifdef _WIN32
    localtime_s(&tNow, &sNow);
#else
    localtime_r(&sNow, &tNow);
#endif
    nSafeLibReturn = snprintf(szFileName, sizeof(szFileName), "%s%c%s_%s_%04d%02d%02d.log", g_szLogFilePath, PATH_SEPERATOR, g_szProcessname, g_saLogFileInfo[eLogType].szFileName, tNow.tm_year + YEAR_OFFSET, tNow.tm_mon + MONTH_OFFSET, tNow.tm_mday);
    
    
    
    nSafeLibReturn = snprintf(szTimeString, sizeof(szTimeString), "%02d:%02d:%02d.%03ld] ", tNow.tm_hour, tNow.tm_min, tNow.tm_sec, nMillisecond);
    
    va_start(ArgList, szFormat);
    nSafeLibReturn = vsnprintf(szBuffer, sizeof(szBuffer) - 1 , szFormat, ArgList);
    if (nSafeLibReturn < 0 && g_bPrintLogToScreen)
    {
        printf("vsnprintf error[Msg:%s]\n", strerror(errno));
    }
    va_end(ArgList);
    szBuffer[LOG_MAX_BUFFER_SIZE] = '\0';
    //SafeStrCat(szBuffer, sizeof(szBuffer), "\n", 1);
    
    if (g_bPrintLogToScreen)
    {
        printf("%s", szTimeString);
        printf("%s", szBuffer);
        printf("\n");
        fflush(stdout);
    }
    
//    nSafeLibReturn = fopen_s(&pLogFile, szFileName, "a");
//    if (nSafeLibReturn != EOK)
//    {
//#ifdef _DEBUG
//        if (g_bPrintLogToScreen)
//        {
//            printf("Log file open error[finename:%s]!\n", szFileName);
//        }
//#endif
//        pthread_mutex_unlock(&g_sLogMutex);
//        return;
//    }
#ifdef __SECURE_CODING
    nSafeLibReturn = fopen_s(&pLogFile, szFileName, "a");
    if (nSafeLibReturn != EOK)
    {
#ifdef _DEBUG
        if (g_bPrintLogToScreen)
        {
            strerror_s(szTraceInfo, sizeof(szTraceInfo), nSafeLibReturn);
            printf("Log file open error[finename:%s, Msg:%s]!\n", szFileName, szTraceInfo);
        }
#endif
        pthread_mutex_unlock(&g_sLogMutex);
        return;
    }
#else
    pLogFile = fopen(szFileName, "a");
    if (pLogFile == NULL)
    {
#ifdef _DEBUG
        if (g_bPrintLogToScreen)
        {
            printf("Log file open error[finename:%s, Msg:%s]!\n", szFileName, strerror(errno));
        }
#endif
        pthread_mutex_unlock(&g_sLogMutex);
        return;
    }

#endif
    fputs(szTimeString, pLogFile);
    fputs(szBuffer, pLogFile);
    fputs("\n", pLogFile);
    fclose(pLogFile);
    pthread_mutex_unlock(&g_sLogMutex);
}

void WriteRawCommLog(enum LogType eLogType, enum LogLevel eLogLevel, int nSocket, char cSendRecv, const char *szFormat, ...)
{
    char szBuffer[LOG_MAX_BUFFER_SIZE + 1];
    char szFileName[PATH_MAX + 1];
    struct timespec sTimeSpec;
    time_t sNow;
    struct tm tNow;
    //char szTimeString[LOG_TIME_STRING_SIZE + 1];
    long nMillisecond;
    va_list ArgList;
    FILE *pLogFile;
    int nSafeLibReturn;

    pthread_mutex_lock(&g_sLogMutex);
    // 로그 파일 설정에 해당 로그 레벨이 설정되어 있지 않으면 로그 파일에 저장하지 않는다.
    if ((eLogLevel & g_saLogFileInfo[eLogType].nLogLevelFlags) == 0)
    {
#ifdef _DEBUG
        if (g_bPrintLogToScreen)
        {
            printf("Function:%s : LogLevel Flag is not set[LogType:%02X,LogLevel-config:%02X,input:%02X]\n", __FUNCTION__, eLogType, g_saLogFileInfo[eLogType].nLogLevelFlags, eLogLevel);
        }
#endif
        pthread_mutex_unlock(&g_sLogMutex);
        return;
    }

    clock_gettime(CLOCK_REALTIME, &sTimeSpec);
    sNow = sTimeSpec.tv_sec;
    nMillisecond = sTimeSpec.tv_nsec / 1.0e6; // nanoseconds to milliseconds
#ifdef _WIN32
    localtime_s(&tNow, &sNow);
#else
    localtime_r(&sNow, &tNow);
#endif

    nSafeLibReturn = snprintf(szFileName, sizeof(szBuffer), "%s%c%s%c%s_%s_%04d%02d%02d_%08d.log", g_szLogFilePath, PATH_SEPERATOR, LOG_COMM_LOG_SUBFOLDER, PATH_SEPERATOR, g_szProcessname, g_saLogFileInfo[eLogType].szFileName, tNow.tm_year + YEAR_OFFSET, tNow.tm_mon + MONTH_OFFSET, tNow.tm_mday, nSocket);



    nSafeLibReturn = snprintf(szBuffer, sizeof(szBuffer), "%02d:%02d:%02d.%03ld] %c ", tNow.tm_hour, tNow.tm_min, tNow.tm_sec, nMillisecond, cSendRecv);


    va_start(ArgList, szFormat);
    vsnprintf(szBuffer + LOG_COMM_TIME_STRING_SIZE, sizeof(szBuffer) - LOG_COMM_TIME_STRING_SIZE, szFormat, ArgList);
    va_end(ArgList);
    SafeStrCat(szBuffer, sizeof(szBuffer), "\n", 1);

    if (g_bPrintLogToScreen)
    {
        printf("%s", szBuffer);
        fflush(stdout);
    }

//    nSafeLibReturn = fopen_s(&pLogFile, szFileName, "a");
//    if (nSafeLibReturn != EOK)
//    {
//#ifdef _DEBUG
//        if (g_bPrintLogToScreen)
//        {
//            printf("Log file open error[finename:%s]!\n", szFileName);
//        }
//#endif
//        pthread_mutex_unlock(&g_sLogMutex);
//        return;
//    }
#ifdef __SECURE_CODING
    nSafeLibReturn = fopen_s(&pLogFile, szFileName, "a");
    if (nSafeLibReturn != EOK)
    {
#ifdef _DEBUG
        if (g_bPrintLogToScreen)
        {
            strerror_s(szTraceInfo, sizeof(szTraceInfo), nSafeLibReturn);
            printf("Log file open error[finename:%s, Msg:%s]!\n", szFileName, szTraceInfo);
        }
#endif
        pthread_mutex_unlock(&g_sLogMutex);
        return;
    }
#else
    pLogFile = fopen(szFileName, "a");
    if (pLogFile == NULL)
    {
#ifdef _DEBUG
        if (g_bPrintLogToScreen)
        {
            printf("Log file open error[finename:%s, Msg:%s]!\n", szFileName, strerror(errno));
        }
#endif
        pthread_mutex_unlock(&g_sLogMutex);
        return;
    }

#endif
    fputs(szBuffer, pLogFile);
    fclose(pLogFile);
    pthread_mutex_unlock(&g_sLogMutex);
}

void WriteCommLog(enum LogType eLogType, enum LogLevel eLogLevel, int nSocket, int nChannelNo, char cSendRecv, const char *szFormat, ...)
{
    char szBuffer[LOG_MAX_BUFFER_SIZE + 1];
    char szFileName[PATH_MAX + 1];
    struct timespec sTimeSpec;
    time_t sNow;
    struct tm tNow;
    //char szTimeString[LOG_TIME_STRING_SIZE + 1];
    long nMillisecond;
    va_list ArgList;
    FILE *pLogFile;
    int nSafeLibReturn;

    pthread_mutex_lock(&g_sLogMutex);
    // 로그 파일 설정에 해당 로그 레벨이 설정되어 있지 않으면 로그 파일에 저장하지 않는다.
    if ((eLogLevel & g_saLogFileInfo[eLogType].nLogLevelFlags) == 0)
    {
#ifdef _DEBUG
        if (g_bPrintLogToScreen)
        {
            printf("Function:%s : LogLevel Flag is not set[LogType:%02X,LogLevel-config:%02X,input:%02X]\n", __FUNCTION__, eLogType, g_saLogFileInfo[eLogType].nLogLevelFlags, eLogLevel);
        }
#endif
        pthread_mutex_unlock(&g_sLogMutex);
        return;
    }

    clock_gettime(CLOCK_REALTIME, &sTimeSpec);
    sNow = sTimeSpec.tv_sec;
    nMillisecond = sTimeSpec.tv_nsec / 1.0e6; // nanoseconds to milliseconds
#ifdef _WIN32
    localtime_s(&tNow, &sNow);
#else
    localtime_r(&sNow, &tNow);
#endif

    nSafeLibReturn = snprintf(szFileName, sizeof(szBuffer), "%s%c%s%c%s_%s_%04d%02d%02d_%08d_%1d.log", g_szLogFilePath, PATH_SEPERATOR, LOG_COMM_LOG_SUBFOLDER, PATH_SEPERATOR, g_szProcessname, g_saLogFileInfo[eLogType].szFileName, tNow.tm_year + YEAR_OFFSET, tNow.tm_mon + MONTH_OFFSET, tNow.tm_mday, nSocket, nChannelNo);



    nSafeLibReturn = snprintf(szBuffer, sizeof(szBuffer), "%02d:%02d:%02d.%03ld] %c ", tNow.tm_hour, tNow.tm_min, tNow.tm_sec, nMillisecond, cSendRecv);


    va_start(ArgList, szFormat);
    vsnprintf(szBuffer + LOG_COMM_TIME_STRING_SIZE, sizeof(szBuffer) - LOG_COMM_TIME_STRING_SIZE, szFormat, ArgList);
    va_end(ArgList);
    SafeStrCat(szBuffer, sizeof(szBuffer), "\n", 1);

    if (g_bPrintLogToScreen)
    {
        printf("%s", szBuffer);
        fflush(stdout);
    }

//    nSafeLibReturn = fopen_s(&pLogFile, szFileName, "a");
//    if (nSafeLibReturn != EOK)
//    {
//#ifdef _DEBUG
//        if (g_bPrintLogToScreen)
//        {
//            printf("Log file open error[finename:%s]!\n", szFileName);
//        }
//#endif
//        pthread_mutex_unlock(&g_sLogMutex);
//        return;
//    }
#ifdef __SECURE_CODING
    nSafeLibReturn = fopen_s(&pLogFile, szFileName, "a");
    if (nSafeLibReturn != EOK)
    {
#ifdef _DEBUG
        if (g_bPrintLogToScreen)
        {
            strerror_s(szTraceInfo, sizeof(szTraceInfo), nSafeLibReturn);
            printf("Log file open error[finename:%s, Msg:%s]!\n", szFileName, szTraceInfo);
        }
#endif
        pthread_mutex_unlock(&g_sLogMutex);
        return;
    }
#else
    pLogFile = fopen(szFileName, "a");
    if (pLogFile == NULL)
    {
#ifdef _DEBUG
        if (g_bPrintLogToScreen)
        {
            printf("Log file open error[finename:%s, Msg:%s]!\n", szFileName, strerror(errno));
        }
#endif
        pthread_mutex_unlock(&g_sLogMutex);
        return;
    }

#endif
    fputs(szBuffer, pLogFile);
    fclose(pLogFile);
    pthread_mutex_unlock(&g_sLogMutex);

}
void WriteQueueLog(enum LogType eLogType, enum LogLevel eLogLevel, int nSocket, char cType, const char *szExchanger, const char *szTopic, const char *szUUID, int nLength, const char *szFormat, ...)
{
    char szBuffer[LOG_MAX_BUFFER_SIZE + 1];
    char szFileName[PATH_MAX + 1];
    struct timespec sTimeSpec;
    time_t sNow;
    struct tm tNow;
    //char szTimeString[LOG_TIME_STRING_SIZE + 1];
    long nMillisecond;
    va_list ArgList;
    FILE *pLogFile;
    int nSafeLibReturn;
    int nBufferOffset;

    pthread_mutex_lock(&g_sLogMutex);
    // 로그 파일 설정에 해당 로그 레벨이 설정되어 있지 않으면 로그 파일에 저장하지 않는다.
    if ((eLogLevel & g_saLogFileInfo[eLogType].nLogLevelFlags) == 0)
    {
#ifdef _DEBUG
        if (g_bPrintLogToScreen)
        {
            printf("Function:%s : LogLevel Flag is not set[LogType:%02X,LogLevel-config:%02X,input:%02X]\n", __FUNCTION__, eLogType, g_saLogFileInfo[eLogType].nLogLevelFlags, eLogLevel);
        }
#endif
        pthread_mutex_unlock(&g_sLogMutex);
        return;
    }

    clock_gettime(CLOCK_REALTIME, &sTimeSpec);
    sNow = sTimeSpec.tv_sec;
    nMillisecond = sTimeSpec.tv_nsec / 1.0e6; // nanoseconds to milliseconds
#ifdef _WIN32
    localtime_s(&tNow, &sNow);
#else
    localtime_r(&sNow, &tNow);
#endif

    nSafeLibReturn = snprintf(szFileName, sizeof(szBuffer), "%s%c%s%c%s_%s_%04d%02d%02d_%08d.log", g_szLogFilePath, PATH_SEPERATOR, LOG_COMM_LOG_SUBFOLDER, PATH_SEPERATOR, g_szProcessname, g_saLogFileInfo[eLogType].szFileName, tNow.tm_year + YEAR_OFFSET, tNow.tm_mon + MONTH_OFFSET, tNow.tm_mday, nSocket);



    nSafeLibReturn = snprintf(szBuffer, sizeof(szBuffer), "%02d:%02d:%02d.%03ld] %c,%20s,%20s,%36s,%05d,", tNow.tm_hour, tNow.tm_min, tNow.tm_sec, nMillisecond, cType, szExchanger, szTopic, szUUID, nLength);
    nBufferOffset = strnlen_s(szBuffer, sizeof(szBuffer));

    va_start(ArgList, szFormat);
    vsnprintf(szBuffer + nBufferOffset, sizeof(szBuffer) - nBufferOffset, szFormat, ArgList);
    va_end(ArgList);
    SafeStrCat(szBuffer, sizeof(szBuffer), "\n", 1);

    if (g_bPrintLogToScreen)
    {
        printf("%s", szBuffer);
        fflush(stdout);
    }

//    nSafeLibReturn = fopen_s(&pLogFile, szFileName, "a");
//    if (nSafeLibReturn != EOK)
//    {
//#ifdef _DEBUG
//        if (g_bPrintLogToScreen)
//        {
//            printf("Log file open error[finename:%s]!\n", szFileName);
//        }
//#endif
//        pthread_mutex_unlock(&g_sLogMutex);
//        return;
//    }
#ifdef __SECURE_CODING
    nSafeLibReturn = fopen_s(&pLogFile, szFileName, "a");
    if (nSafeLibReturn != EOK)
    {
#ifdef _DEBUG
        if (g_bPrintLogToScreen)
        {
            strerror_s(szTraceInfo, sizeof(szTraceInfo), nSafeLibReturn);
            printf("Log file open error[finename:%s, Msg:%s]!\n", szFileName, szTraceInfo);
        }
#endif
        pthread_mutex_unlock(&g_sLogMutex);
        return;
    }
#else
    pLogFile = fopen(szFileName, "a");
    if (pLogFile == NULL)
    {
#ifdef _DEBUG
        if (g_bPrintLogToScreen)
        {
            printf("Log file open error[finename:%s, Msg:%s]!\n", szFileName, strerror(errno));
        }
#endif
        pthread_mutex_unlock(&g_sLogMutex);
        return;
    }

#endif
    fputs(szBuffer, pLogFile);
    fclose(pLogFile);
    pthread_mutex_unlock(&g_sLogMutex);

}
