/* Move to header file
#include <fcntl.h>
#include <stdio.h>
#include <libsafec/safe_str_lib.h>
#include <libsafec/safe_mem_lib.h>
#include <pthread.h>
#include <ctype.h>
#include "Common.h"
#include "StringUtil.h"
*/
#include "Config.h"

/******************************************************************************
*
*   File Name       : Config.c
*   Desc.           : 설정 파일 처리 공통모듈
*                     설정 파일을 파싱해서 메모리에 로드해 두고 처리하는 방식이다.
                      mci처럼 직접 파일에서 읽어야 될 경우가 있을까?(사실 계속 옵션을 확인할려면 DB가 맞다. 파일에서 계속 읽는 것은 처리 속도가 느리다 - DB도 처음 로딩은 느리다)
*                     기능 추가 여부 결정(다중 Config 파일, Section 기능 추가 여부)
*                     Section을 추가할 경우 Section Index를 배열로 구현하고 각 섹션의 시작 데이터 인덱스를 저장하는 방식으로 처리하자
*   Creator         : Kim Dalhyun
*   Rev. Info.      : 1.0 - 최초작성(2018.06.20 : Kim Dalhyun)
*
******************************************************************************/


/******************************************************************************
*
*   Function Name   :   ReadConfigFile
*   Desc.           :   설정파일 내용을 읽어 키와 값으로 구분하여 메모리에 저장
*   Input           :   bInitialize - 메모리 초기화 여부(설정파일이 하나 이상일 경우 처음만 true, 나머지 파일 읽을 시 false)
*                       szFileName - 설정파일명
*                       메모리 Leak 발생 방지를 위해 설정 파일의 변수 개수는 1000개로 처리하였음
*   Output          :   처리 결과(CONF_ERR_SUCCESS, CONF_ERR_FILEOPEN, CONF_ERR_MEM) 
*   Rev. Info.      :   1.0 - 최초작성(2018.06.20 : Kim Dalhyun)
*
******************************************************************************/

struct CONFIG_VALUE g_saConfigData[MAX_CONFIG_NUMBER];
int g_nConfigDataCount = 0;
pthread_mutex_t g_sConfigMutex = PTHREAD_MUTEX_INITIALIZER;


int ReadConfigFile(int bInitialize, const char *szFileName)
{
    FILE *pFilePoint = NULL;
    char szLineBuffer[MAX_STRING_SIZE + 1];
    char szSplitBuffer[MAX_STRING_SIZE + 1];
    char *pSeparator = NULL;
    int nConfigDataIndex = 0;
    
    pthread_mutex_lock(&g_sConfigMutex);
    if (bInitialize)
    {
        if (SafeMemSet(g_saConfigData, sizeof(g_saConfigData), 0,  sizeof(g_saConfigData)) != SEC_ERR_SUCCESS)
        {
            // 로그 파일 출력 추가 필요
            pthread_mutex_unlock(&g_sConfigMutex);
            return CONF_ERR_MEM;
        }
    }
    else
    {
        while (g_saConfigData[nConfigDataIndex].szKey[0] != '\0')
        {
            nConfigDataIndex++;
        }
    }
       
    if ((pFilePoint = fopen(szFileName, "r")) == NULL)
    {
        // 로그 파일 출력 추가 필요
        pthread_mutex_unlock(&g_sConfigMutex);
        return CONF_ERR_FILEOPEN;
    }
    
    while (!feof(pFilePoint))
    {
        // 파일을 한 라인 단위로 읽어서 처리
        fgets(szLineBuffer, MAX_STRING_SIZE, pFilePoint);
        // 주석인 경우 처리하지 않고 다음줄 처리
        if (szLineBuffer[0] == '#')
            continue;
        
        // Separator문자를 이용하여 키와 값으로 구분하여 처리
        SafeStrChr(szLineBuffer, MAX_STRING_SIZE, CONF_KEY_SEPERATOR, &pSeparator);
        if (pSeparator == (char *)NULL)
        {
            continue;
        }
        SafeStrNCopy(szSplitBuffer, MAX_STRING_SIZE, szLineBuffer, pSeparator - szLineBuffer);
        Trim(g_saConfigData[nConfigDataIndex].szKey, MAX_STRING_SIZE, szSplitBuffer);
        SafeStrNCopy(szSplitBuffer, sizeof(szSplitBuffer), pSeparator + 1, strnlen_s(pSeparator, sizeof(szLineBuffer) - (pSeparator - szLineBuffer)));
        Trim(g_saConfigData[nConfigDataIndex].szValue, MAX_STRING_SIZE, szSplitBuffer);
        nConfigDataIndex++;
    }
    g_nConfigDataCount = nConfigDataIndex;
    fclose(pFilePoint);
    pthread_mutex_unlock(&g_sConfigMutex);
    return CONF_ERR_SUCCESS;
}

/******************************************************************************
*
*   Function Name   :   GetConfigValue
*   Desc.           :   메모리에 저장된 설정파일 내용중 Key에 대한 Value 값을 리턴한다.
*   Input           :   szKey - 검색을 위한 key값 지정
*                       szValue - 리던될 값을 저장할 메모리 주소
*                       nValueLength - Value값의 메모리 길이                        
*   Output          :   처리 결과(CONF_ERR_SUCCESS, CONF_ERR_FILEOPEN, CONF_ERR_MEM) 
*   Rev. Info.      :   1.0 - 최초작성(2018.06.20 : Kim Dalhyun)
*
******************************************************************************/

int GetConfigValue(const char *szKey, char *szValue, int nValueLength)
{
    int nConfigDataIndex = 0;
    int nErrNo;
    int nStringCompareResult;
    
    if (szValue == NULL)
    {
        return CONF_ERR_NULL_PARAMETER;
    }
    
    if (nValueLength == 0)
    {
        return CONF_ERR_ZERO_LENGTH;
    }
    
    SafeMemSet(szValue, nValueLength, 0, nValueLength);
    
    pthread_mutex_lock(&g_sConfigMutex);
    while (g_saConfigData[nConfigDataIndex].szKey[0] != '\0' && nConfigDataIndex < MAX_CONFIG_NUMBER)
    {
        nErrNo = SafeStrCmp(szKey, MAX_STRING_SIZE, g_saConfigData[nConfigDataIndex].szKey, &nStringCompareResult);
        if (nErrNo == SEC_ERR_SUCCESS && nStringCompareResult == 0)
        {
            SafeStrNCopy(szValue, nValueLength, g_saConfigData[nConfigDataIndex].szValue, strnlen_s(g_saConfigData[nConfigDataIndex].szValue, sizeof(g_saConfigData[nConfigDataIndex].szValue)));
            pthread_mutex_unlock(&g_sConfigMutex);
            return CONF_ERR_SUCCESS;
        }
        nConfigDataIndex++;
    }
    pthread_mutex_unlock(&g_sConfigMutex);
    return CONF_ERR_NOT_FOUND;
}


/******************************************************************************
*
*   Function Name   :   ParsingBoolean
*   Desc.           :   문자열이 Boolean 형태로 저장된 경우 숫자로 parsing
*   Input           :   szValue - 저장된 문자열 변수
*                       nInputValueLength - 메모리 경계 체크를 위한 문자열 변수의 최대 길이
*   Output          :   문자열에 대한 실제 true, false 값
*   Rev. Info.      :   1.0 - 최초작성(2018.06.20 : Kim Dalhyun)
*
******************************************************************************/
int ParsingBoolean(const char *pszValue, const int nInputValueLength, int *pbBoolean)
{
    char szValueBuffer[MAX_STRING_SIZE + 1];

    int nValueLength;
    int nIndex;
    int nStrCmpResult;
    int nReturnValue;


    nReturnValue = CONF_ERR_TOKEN_ERR;
    *pbBoolean = false;
    nValueLength = strnlen_s(pszValue, nInputValueLength);

    for (nIndex = 0; nIndex < nValueLength && nIndex < MAX_STRING_SIZE; nIndex++)
    {
        if (!isspace(pszValue[nIndex]))
        {
            szValueBuffer[nIndex] = (char)toupper(pszValue[nIndex]);
        }
    }
    szValueBuffer[nIndex] = '\0';

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

    if (nStrCmpResult == 0)
    {
        *pbBoolean = true;
        nReturnValue = CONF_ERR_SUCCESS;
    }

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

    if (nStrCmpResult == 0)
    {
        *pbBoolean = false;
        nReturnValue = CONF_ERR_SUCCESS;
    }


    return nReturnValue;
}

/******************************************************************************
*
*   Function Name   :   ParsingYESNO
*   Desc.           :   문자열이 Yes or No 형태로 저장된 경우 숫자로 parsing
*   Input           :   szValue - 저장된 문자열 변수
*                       nInputValueLength - 메모리 경계 체크를 위한 문자열 변수의 최대 길이
*   Output          :   문자열에 대한 실제 숫자 값(false : No, true : Yes)
*   Rev. Info.      :   1.0 - 최초작성(2018.08.27 : Kim Dalhyun)
*
******************************************************************************/
int ParsingYESNO(const char *pszValue, const int nInputValueLength, int *pbYESNOValue)
{
    char szValueBuffer[MAX_STRING_SIZE + 1];

    int nValueLength;
    int nIndex;
    int nStrCmpResult;
    int nReturnValue;

    nReturnValue = CONF_ERR_TOKEN_ERR;
    *pbYESNOValue = false;
    nValueLength = strnlen_s(pszValue, nInputValueLength);

    for (nIndex = 0; nIndex < nValueLength && nIndex < MAX_STRING_SIZE; nIndex++)
    {
        if (!isspace(pszValue[nIndex]))
        {
            szValueBuffer[nIndex] = (char)toupper(pszValue[nIndex]);
        }
    }
    szValueBuffer[nIndex] = '\0';

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

    if (nStrCmpResult == 0)
    {
        *pbYESNOValue = false;
        nReturnValue = CONF_ERR_SUCCESS;
    }

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

    if (nStrCmpResult == 0)
    {
        *pbYESNOValue = true;
        nReturnValue = CONF_ERR_SUCCESS;
    }

    return nReturnValue;
}
/*
int ParsingConfigFlagString(const char *szStringFlags, int nInputTagStringLength, const struct VALUESTRINGPAIRINFO *pPairInfo, int nMaxFlagInfoSize)
{
    char szTagBuffer[MAX_STRING_SIZE + 1];
    int nFlags = -1;
    int nSeperatorIndex = 0;
    int nPrevSeperatorIndex = 0;
    int nTagStringLength = 0;
    int nTagBufferIndex = 0;
    int nSearchIndex;
    int nTagStringIndex;
    int nStrCmpResult;

    nTagStringLength = strnlen_s(szStringFlags, nInputTagStringLength);

    for (nSearchIndex = 0; nSearchIndex < nTagStringLength;nSearchIndex++)
    {
        if (szStringFlags[nSearchIndex] == CONF_LIST_SEPERATOR || nSearchIndex == (nTagStringLength - 1))
        {
            if (nSearchIndex == (nTagStringLength - 1))
            {
                if (nTagBufferIndex == MAX_STRING_SIZE)
                {
                    return -1;
                }
                szTagBuffer[nTagBufferIndex++] = (char)toupper(szStringFlags[nSearchIndex]);
            }
            nPrevSeperatorIndex = nSeperatorIndex + 1;
            nSeperatorIndex = nSearchIndex;
            szTagBuffer[nTagBufferIndex] = '\0';
            nTagBufferIndex = 0;

            for (nTagStringIndex = 0; nTagStringIndex < nMaxFlagInfoSize; nTagStringIndex++)
            {
                SafeStrCmp(szTagBuffer, MAX_STRING_SIZE, pPairInfo[nTagStringIndex].szFlagString, &nStrCmpResult);
                if (nStrCmpResult == 0)
                {
                    if (nFlags == -1)
                        nFlags = 0;
                    nFlags = nFlags | pPairInfo[nTagStringIndex].nFlag;
                    break;
                }
            }
        }
        else
        {
            if (nTagBufferIndex == MAX_STRING_SIZE)
            {
                return -1;
            }
            szTagBuffer[nTagBufferIndex++] = (char)toupper(szStringFlags[nSearchIndex]);

        }
    }
    return nFlags;
}
*/

int MappingIntToString(const struct VALUE_STRING_PAIR_INFO *pPairInfo, int nPairInfoCount, int nValue, char *pStringValue, int nStringValue)
{
    int nPairInfoIndex;

    pStringValue[0] = '\0';

    for (nPairInfoIndex = 0; nPairInfoIndex < nPairInfoCount; nPairInfoIndex++)
    {
        if (pPairInfo[nPairInfoIndex].nValue == nValue)
        {
            SafeStrNCopy(pStringValue, nStringValue, pPairInfo[nPairInfoIndex].szValueString, strnlen_s(pPairInfo[nPairInfoIndex].szValueString, sizeof(pPairInfo[nPairInfoIndex].szValueString)));
            return CONF_ERR_SUCCESS;
        }
    }
    return CONF_ERR_NOT_FOUND;
}

int MappingStringToInt(const char *szStringFlags, int nInputTagStringLength, const struct VALUE_STRING_PAIR_INFO *pPairInfo, int nMaxFlagInfoSize, int *pnValue)
{
    char szTagBuffer[MAX_STRING_SIZE + 1];
    int nSeperatorIndex = 0;
    int nPrevSeperatorIndex = 0;
    int nTagStringLength = 0;
    int nTagBufferIndex = 0;
    int nSearchIndex;
    int nTagStringIndex;
    int nStrCmpResult;

    nTagStringLength = strnlen_s(szStringFlags, nInputTagStringLength);

    for (nSearchIndex = 0; nSearchIndex < nTagStringLength;nSearchIndex++)
    {
        if (nSearchIndex == (nTagStringLength - 1))
        {
            if (nTagBufferIndex == MAX_STRING_SIZE)
            {
                //*pnValue = nFlags;
                return CONF_ERR_TOKEN_ERR;
            }
            if (!isspace(szStringFlags[nSearchIndex]))
            {
                szTagBuffer[nTagBufferIndex++] = szStringFlags[nSearchIndex];
            }
            nPrevSeperatorIndex = nSeperatorIndex + 1;
            nSeperatorIndex = nSearchIndex;
            szTagBuffer[nTagBufferIndex] = '\0';
            nTagBufferIndex = 0;

            for (nTagStringIndex = 0; nTagStringIndex < nMaxFlagInfoSize; nTagStringIndex++)
            {
                SafeStrCmp(szTagBuffer, MAX_STRING_SIZE, pPairInfo[nTagStringIndex].szValueString, &nStrCmpResult);
                if (nStrCmpResult == 0)
                {
                    *pnValue = pPairInfo[nTagStringIndex].nValue;
                    return CONF_ERR_SUCCESS;
                }
            }
            return CONF_ERR_NOT_FOUND;
        }
        else
        {
            if (nTagBufferIndex == MAX_STRING_SIZE)
            {
                //*pnValue = nFlags;
                return CONF_ERR_TOKEN_ERR;
            }
            if (!isspace(szStringFlags[nSearchIndex]))
            {
                szTagBuffer[nTagBufferIndex++] = szStringFlags[nSearchIndex];
            }
        }
    }
    *pnValue = 0;
    return CONF_ERR_SUCCESS;
}


int ParsingFlagString(const char *szStringFlags, int nInputTagStringLength, const struct VALUE_STRING_PAIR_INFO *pPairInfo, int nMaxFlagInfoSize, int *pnValue)
{
    char szTagBuffer[MAX_STRING_SIZE + 1];
    int nFlags = 0;
    int nSeperatorIndex = 0;
    int nPrevSeperatorIndex = 0;
    int nTagStringLength = 0;
    int nTagBufferIndex = 0;
    int nSearchIndex;
    int nTagStringIndex;
    int nStrCmpResult;

    *pnValue = 0;
    nTagStringLength = strnlen_s(szStringFlags, nInputTagStringLength);

    for (nSearchIndex = 0; nSearchIndex < nTagStringLength;nSearchIndex++)
    {
        if (szStringFlags[nSearchIndex] == CONF_LIST_SEPERATOR || nSearchIndex == (nTagStringLength - 1))
        {
            if (nSearchIndex == (nTagStringLength - 1))
            {
                if (nTagBufferIndex == MAX_STRING_SIZE)
                {
                    *pnValue = nFlags;
                    return CONF_ERR_TOKEN_ERR;
                }
                if (!isspace(szStringFlags[nSearchIndex]))
                {
                    szTagBuffer[nTagBufferIndex++] = (char)toupper(szStringFlags[nSearchIndex]);
                }
            }
            nPrevSeperatorIndex = nSeperatorIndex + 1;
            nSeperatorIndex = nSearchIndex;
            szTagBuffer[nTagBufferIndex] = '\0';
            nTagBufferIndex = 0;

            for (nTagStringIndex = 0; nTagStringIndex < nMaxFlagInfoSize; nTagStringIndex++)
            {
                SafeStrCmp(szTagBuffer, MAX_STRING_SIZE, pPairInfo[nTagStringIndex].szValueString, &nStrCmpResult);
                if (nStrCmpResult == 0)
                {
                    if (nFlags == -1)
                        nFlags = 0;
                    nFlags = nFlags | pPairInfo[nTagStringIndex].nValue;
                    break;
                }
            }
        }
        else
        {
            if (nTagBufferIndex == MAX_STRING_SIZE)
            {
                *pnValue = nFlags;
                return CONF_ERR_TOKEN_ERR;
            }
            if (!isspace(szStringFlags[nSearchIndex]))
            {
                szTagBuffer[nTagBufferIndex++] = (char)toupper(szStringFlags[nSearchIndex]);
            }

        }
    }
    *pnValue = nFlags;
    return CONF_ERR_SUCCESS;
}

char *GetPairString(const struct VALUE_STRING_PAIR_INFO *pPairInfo, int nPairInfoCount, int nValue)
{
    int nPairInfoIndex;

    for (nPairInfoIndex = 0; nPairInfoIndex < nPairInfoCount; nPairInfoIndex++)
    {
        if (pPairInfo[nPairInfoIndex].nValue == nValue)
        {
            return pPairInfo[nPairInfoIndex].szValueString;
        }
    }
    return NULL;

}

int GetPairValue(const struct VALUE_STRING_PAIR_INFO *pPairInfo, int nPairInfoCount, char *pStringValue)
{
    int nPairInfoIndex;
    int nCmpResult;
    int nResult;

    for (nPairInfoIndex = 0; nPairInfoIndex < nPairInfoCount; nPairInfoIndex++)
    {
        nResult = SafeStrCmp(pPairInfo[nPairInfoIndex].szValueString, sizeof(pPairInfo[nPairInfoIndex].szValueString), pStringValue, nCmpResult);
        if (nCmpResult == 0)
            return pPairInfo[nPairInfoIndex].nValue;
    }

    return -1;
}

