
#include "MessageSender.h"
#include "hi_ctrl.h"
#include "taglnk_frame.h"
#include "tagbl_struc.h"
#include <iconv.h>
#include "iss_comp.h"

char g_szProcessname[MAX_STRING_SIZE + 1];
char g_szProcessPath[PATH_MAX + 1];

int g_nResponseTimeout;
int cmd;
int type;
int job;
char szOrganizationID[6+1];
char szWorkType[4+1];
char szMsgType[4+1];

extern char szRespCode[] = "0000";
//char *szData;
char szData[MAX_STRING_SIZE + 1];
char rtnData[MAX_STRING_SIZE + 1];

extern int g_nSerailNo = 1;
pthread_mutex_t g_sSerialMutex = PTHREAD_MUTEX_INITIALIZER;

static    struct    {
    int    cmd;                /* CONTROL COMMAND          */
    int    act;                /* 0:송신 1:응답 2:3자통신  */
    int    work;
    char  *cmd_nm;
} lane_act[] = {
    { NS_RPR_DATA              ,   0,    0 , " 0x76 보수데이터 "},
    { NS_CLOSE_PASS            ,   1,    5 , " 0x73 야간계수 데이터 "},
    { NS_WRK_START             ,   0,    0 , " 0x61 근무개시보고 "},
    { NS_HND_DATA              ,   1,    2 , " 0x17 처리 데이터 "},
    { NS_CRD_HND_DATA          ,   1,    3 , " 0x50 카드처리 데이터 "},
    { NS_WRK_END               ,   1,    4 , " 0x28 근무종료데이터 "},
    { NS_REFUND_DATA           ,   1,    2 , " 0x40 환불처리 데이터 "},
    { NS_LANE_STAT             ,   1,    1 , " 0x75 차로상태(STATUS) "},
    { -1                       ,  -1,   -1 , "                         "}
};

static struct {
    uchar plz_id[2];
    int   lane_no;
    int   tbl_order;
} lane_info[20] =
{
    {"01", 1, 0},
    {"01", 2, 0},
    {"01", 3, 0},
    {"01", 4, 0},
    {"01", 5, 0},
    {"01", 6, 0},
    {"01", 7, 0},
    {"01", 8, 0},
    {"03", 1, 0},
    {"03", 2, 0},
    {"03", 3, 0},
    {"03", 4, 0},
    {"03", 5, 0},
    {"03", 6, 0},
    {"03", 7, 0},
    {"03", 8, 0},
    {"03", 9, 0},
    {"03", 10, 0},
    {"00", -1, -1}
};

int MessageSender(int kind, char *packet, char *path, char *rtn)
{
    char szProcessname[PATH_MAX + 1];
    char *sDataMalloc;
    struct write_info wi;
    int nResult;
    int i = 0;


que_log("MessageSender Start\n");
/* // 2019.07.04 lane_main에서 1번만 실행하도록 수정
    nResult = InitMessageSenderConfig(path);

    if (nResult != MQ_ERR_SUCCESS)
    {
        printf("Program terminated!!!\n");
        return -1;
    }
*/

    memset(szData, 0x00, sizeof(szData));
    memset(rtnData, 0x00, sizeof(rtnData));
    sprintf(wi.plz_id, "%.2s", PLZ_CODE2);

    while(1)
    {
        if(lane_act[i].cmd == kind || lane_act[i].cmd == -1) break;
        i++;
    }
    cmd = lane_act[i].cmd;
que_log("lane_act[%d].cmd[%02x][%d]\n", i, lane_act[i].cmd, lane_act[i].act);
    snprintf(szOrganizationID, 6+1, "%06d", lb_data2atoi(wi.plz_id, 2));
    sprintf(szData, "%s", packet);
    
    if(lane_act[i].act == 2)
        snprintf(szWorkType, 4+1, "%.4s", "9998");
    else if(lane_act[i].act == 1)
        snprintf(szWorkType, 4+1, "%04d", lane_act[i].work);
    else if(lane_act[i].act == 0)
        snprintf(szWorkType, 4+1, "%.4s", "9999");
    else{
        //SafeFree((void **)&sDataMalloc);
        return -1;
    }
    type = lane_act[i].act;
    job = lane_act[i].work;
que_log("szWorkType[%s] [%s]\n", szWorkType, lane_act[i].cmd_nm);
    snprintf(szMsgType, 4+1, "%.4s", "0010");

    //2019.06.27 InitDoubleLinkedList Main 선언
    InitThread_Before();
    nResult = InitThread(ProcThread);
que_log("종료[%d]?[%s][%d]\n", type, rtnData, nResult);
    sprintf(rtn, "%s", rtnData);

    return 0;
}

int InitMessageSenderConfig(char *path)
{
    char szConfigfilename[PATH_MAX + 1];
    char szConfigfilenameTemp[PATH_MAX + 1];
    char szValueTemp[MAX_STRING_SIZE + 1];
    char szProcessname[PATH_MAX + 1];
    char *pFindPointer;
    int nResult;

    nResult = SafeStrCopy(szProcessname, sizeof(szProcessname), path);

    if (nResult != SEC_ERR_SUCCESS)
    {
        printf("strcpy_s Error![%d]\n", nResult);
    }

    pFindPointer = szProcessname;
    nResult = SafeStrrChr(szProcessname, strnlen_s(szProcessname, sizeof(szProcessname)), PATH_SEPERATOR, &pFindPointer);

    if (pFindPointer == NULL)
    {
        nResult = SafeStrCopy(g_szProcessname, sizeof(g_szProcessname), szProcessname);
    }
    else
    {
        nResult = SafeStrCopy(g_szProcessname, sizeof(g_szProcessname), pFindPointer + 1);
    }

    GetExecPath(g_szProcessPath, sizeof(g_szProcessPath) - 1, path);

    if (g_szProcessPath == NULL)
    {
        printf("Can't get process path[Cann't load config file]!\nExit Program!");
        return -1;
    }

    snprintf(szConfigfilenameTemp, sizeof(szConfigfilenameTemp), "%s%c%s%c", g_szProcessPath, PATH_SEPERATOR, CONF_FILE_PATH, PATH_SEPERATOR);

    realpath(szConfigfilenameTemp, szConfigfilename);

    snprintf(szValueTemp, sizeof(szValueTemp), "%c", PATH_SEPERATOR);
    nResult = SafeStrCat(szConfigfilename, sizeof(szConfigfilename), szValueTemp, 1);
    nResult = SafeStrCat(szConfigfilename, sizeof(szConfigfilename), g_szProcessname, strnlen_s(g_szProcessname, sizeof(g_szProcessname)));
    nResult = SafeStrCat(szConfigfilename, sizeof(szConfigfilename), CONF_FILE_EXT, strnlen_s(CONF_FILE_EXT, sizeof(CONF_FILE_EXT)));

    printf("Process Name : %s\n", g_szProcessname);
    printf("Process Path : %s\n", g_szProcessPath);
    printf("Config Filename : %s[%d]\n", szConfigfilename, nResult);

    ReadConfigFile(true, szConfigfilename);

    // Read MS_RESPONSE_TIMEOUT_KEY
    if (GetConfigValue(MS_RESPONSE_TIMEOUT_KEY, szValueTemp, sizeof(szValueTemp)) != CONF_ERR_SUCCESS)
    {
        WriteLog(LogType_ErrorOut, LogLevel_Error,"Socket MS_RESPONSE_TIMEOUT_KEY configuration not found!");
        return SOCK_ERR_CONFIG;
    }

    if (SafeStringToInt(szValueTemp, sizeof(szValueTemp), &g_nResponseTimeout) != CONF_ERR_SUCCESS)
    {
        WriteLog(LogType_ErrorOut, LogLevel_Error,"Socket MS_RESPONSE_TIMEOUT_KEY configuration error[ConfigFile:%s]!", szValueTemp);
        return SOCK_ERR_CONFIG;
    }

    // Read CONNECTION_TYPE
    if (GetConfigValue(SOCK_CONF_CONN_TYPE_KEY, szValueTemp, sizeof(szValueTemp)) != CONF_ERR_SUCCESS)
    {
        WriteLog(LogType_ErrorOut, LogLevel_Error,"Socket CONNECTION_TYPE configuration not found[Available Value:Server or Client]!");
        return SOCK_ERR_CONFIG;
    }

    if (MappingStringToInt(szValueTemp, sizeof(szValueTemp), g_sConnTypeInfo, sizeof(g_sConnTypeInfo)/sizeof(struct VALUE_STRING_PAIR_INFO), &g_eConnectionType) != CONF_ERR_SUCCESS)
    {
        WriteLog(LogType_ErrorOut, LogLevel_Error,"Socket Connection Type configuration error[ConfigFile:%s,Available Value:Server or Client]!", szValueTemp);
        return SOCK_ERR_CONFIG;
    }

    if (InitLogConfig() != LOG_ERR_SUCCESS)
    {
        return MS_ERR_CONFIG;
    }

    if (InitSenderMQConfig() != MQ_ERR_SUCCESS)
    {
        return MS_ERR_CONFIG;
    }

    if (InitSender2MQConfig() != MQ_ERR_SUCCESS)
    {
        return MS_ERR_CONFIG;
    }

    if (InitThreadConfig() != THREAD_ERR_SUCCESS)
    {
        return MS_ERR_CONFIG;
    }

    return MS_ERR_SUCCESS;
}

void *ProcThread(void *pData)
{
    char cOccurType;
    struct THREAD_INFO *pThreadInfo;
    struct THREAD_INFO SendThreadInfo;
    char szMessageBuffer[MAX_STRING_SIZE + 1];
    struct MESSAGE_INFO sMessageInfo;
    struct MESSAGE_INFO sndMessageInfo;
    int nResult;
    int nRslt = 0, retry = 0;
    int *pnThreadExitCode;
    amqp_queue_purge_ok_t *pQueuePurgeResult;
    char szTopic[MAX_STRING_SIZE + 1];
    char szTargetTopic[MAX_STRING_SIZE + 1];
    char szReplyQueuename[MAX_STRING_SIZE + 1];
    char szExchangername[MAX_STRING_SIZE + 1];
    uuid_t sUUIDTemp;
    char szSendUUIDString[UUID_STRING_SIZE + 1];
    char szRecvUUIDString[UUID_STRING_SIZE + 1];
    char orgCarNo[255+1];
    char szCarNo[255+1];
    char putCarNp[16];
    char nowTbl[2+1];
    
    char procData[MAX_STRING_SIZE + 1]; memset(procData, 0x00, sizeof(procData));
    char webData[MAX_STRING_SIZE + 1]; memset(webData, 0x00, sizeof(webData));
    int nMessageLength;
    //int nPacketLength;
    int nSerialNo;
    int nCompareResult, size, ii;

    //time_t sNow;
    struct tm tNow;
    int nMillisecond;

    //struct timespec sRecvTimeSpec;
    //time_t sRecvNow;
    struct tm tRecvNow;
    int nRecvMillisecond;

    struct timeval sResponseTimeout;

    uint64_t nDeliveryTag;

    int nDiffSec;
    int nDiffMilliSec;
    long nDiffTime;

    bool bMessageMatch;
    bool bTimeout;
    struct hi_lane_stat *hs;
    struct hi_wrk_end  *we;
    struct hi_wrk_start *ws;
    struct hi_crd_hnd_data  *crd;
    struct hi_hnd_data *hnd;
    size_t in_size;
    size_t out_size;
    size_t out_buf_left;
    char* in_buf;
    char* out_buf;
    char in_fare[2];
    char in_booth[2];
    char tmp_chg[20];

    memcpy(procData, szData, sizeof(procData));

    //memcpy(nowTbl, tbl_order, sizeof(nowTbl));
    memset(orgCarNo, 0x00, sizeof(orgCarNo));
    memset(szCarNo, 0x00, sizeof(szCarNo));
que_log("MessageSender 1\n");
    pThreadInfo = (struct THREAD_INFO *)pData;
    SafeMemSet(&SendThreadInfo, sizeof(SendThreadInfo), 0, sizeof(SendThreadInfo));
    pthread_mutex_init(&SendThreadInfo.sContextMutex, NULL);
    pthread_mutex_init(&SendThreadInfo.sPublishMQ.sMQMutex, NULL);
    pthread_mutex_init(&SendThreadInfo.sConsumeMQ.sMQMutex, NULL);
    pnThreadExitCode = NULL;
    nResult = SafeMemoryAlloc((void **)&pnThreadExitCode, sizeof(int));
    SendThreadInfo.nThreadIndex = pThreadInfo->nThreadIndex;
    SendThreadInfo.bThreadLoop = true;
    //pnThreadExitCode = SafeAlloc(pnThreadExitCode, sizeof(int));
    *pnThreadExitCode = 0;
    //nSerialNo = 1;
que_log("MessageSender 2\n");
    if (g_eConnectionType == SOCK_CONN_TYPE_SERVER)
    {
        cOccurType = 'S';
    }
    else
    {
        cOccurType = 'C';
    }
que_log("MessageSender 2-1\n");
    GetCurrTime(&tNow, &nMillisecond);
que_log("MessageSender 2-2\n");
    ConnectSenderMQ(pThreadInfo, true);

que_log("MessageSender 3[%s]\n", procData);
        if (pThreadInfo->sPublishMQ.bMQConnection == false || pThreadInfo->sPublishMQ.bMQChannelOpen == false
            || pThreadInfo->sConsumeMQ.bMQConnection == false || pThreadInfo->sConsumeMQ.bMQChannelOpen == false)
        {
#if 0
            while(1)
            {
#endif
                //que_log("[Thread:%02d]Connect SenderMQ Connecting [%d]\n", pThreadInfo->nThreadIndex, retry);
                if (ConnectSenderMQ(pThreadInfo, true) != MQ_ERR_SUCCESS)
                {
                    WriteLog(LogType_ErrorOut, LogLevel_Error,"[Thread:%02d]Connect SenderMQ is failed!\n", pThreadInfo->nThreadIndex);

                    pThreadInfo->nRunState = SOCK_THREAD_STATE_TERMINATED;
                    pthread_mutex_destroy(&pThreadInfo->sContextMutex);
                    pthread_mutex_destroy(&pThreadInfo->sPublishMQ.sMQMutex);
                    pthread_mutex_destroy(&pThreadInfo->sConsumeMQ.sMQMutex);
                    SafeFree((void **)&pThreadInfo);
                    SafeFree((void **)&pnThreadExitCode);
                    return(-1);
                }else
                {
                    WriteLog(LogType_StandardOut, LogLevel_Info, "[Thread:%02d]Connect SenderMQ Reconnecting is Success!\n", pThreadInfo->nThreadIndex);
                    //break;
                }
#if 0
            }
#endif
            //WriteLog(LogType_StandardOut, LogLevel_Info, "[Thread:%02d]Connect SenderMQ is succeeded!!", pThreadInfo->nThreadIndex);
        }
que_log("Type [%d]\n", type);
            snprintf(szReplyQueuename, sizeof(szReplyQueuename), "%s%s.%02d", g_szMQMessageSendRespQueuename, szOrganizationID, pThreadInfo->nThreadIndex);
que_log("Reply Que 1 [%s][%s]\n", szReplyQueuename,procData);
//sleep(4);
        nSerialNo = GetSerialNo();
que_log("Serial Que 2 [%d]\n", nSerialNo);
//sleep(4);
        snprintf(szMessageBuffer, sizeof(szMessageBuffer), "%s%c%s%s%08d%04d%02d%02d%02d%02d%02d%s%s", szOrganizationID, cOccurType, szWorkType,
                 szMsgType, nSerialNo++, tNow.tm_year + 1900, tNow.tm_mon + 1, tNow.tm_mday, tNow.tm_hour, tNow.tm_min, tNow.tm_sec, szRespCode, procData);
        //free(szData);
        nMessageLength = strnlen_s(szMessageBuffer, sizeof(szMessageBuffer));
        sMessageInfo.nMessageLength = nMessageLength;
        sMessageInfo.paMessageData = NULL;
que_log("Aloc Before\n");
//sleep(4);
        SafeMemoryAlloc((void **)&sMessageInfo.paMessageData, sMessageInfo.nMessageLength + 5);
que_log("Aloc After\n");
//sleep(4);
        snprintf(sMessageInfo.paMessageData, sMessageInfo.nMessageLength + 5, "%04X", sMessageInfo.nMessageLength);
        SafeStrCat(sMessageInfo.paMessageData + 4, sMessageInfo.nMessageLength + 1, szMessageBuffer, strnlen_s(szMessageBuffer, sizeof(szMessageBuffer)));
que_log("MessageSender 5\n");
//sleep(4);
        snprintf(szTopic, sizeof(szTopic), "%s.%s%s", szOrganizationID, szWorkType, szMsgType);
#ifdef _WIN32
        uuid_generate(&sUUIDTemp);
        uuid_unparse(&sUUIDTemp, szSendUUIDString);
#else
//que_log("A2\n");
//sleep(4);
        uuid_generate(sUUIDTemp);
//que_log("A3\n");
//sleep(4);
        uuid_unparse(sUUIDTemp, szSendUUIDString);
#endif
que_log("TEST 2 Exchange[%s]\n", g_szMQMessageSendExchangername);
que_log("TEST 3 Topic[%s]\n", szTopic);
//que_log("TEST 4 Reply[%s][%d]\n", szReplyQueuename, pThreadInfo->sPublishMQ.bMQChannelOpen);
//sleep(4);
que_log("MessageSender 6\n");

        nResult = PutMessageToQueue(pThreadInfo->sPublishMQ.sMQConnectionState, &pThreadInfo->sPublishMQ.bMQConnection, &pThreadInfo->sPublishMQ.bMQChannelOpen, pThreadInfo->nThreadIndex,
                                    g_szMQMessageSendExchangername, szReplyQueuename, szTopic, &sMessageInfo, sMessageInfo.paMessageData, sMessageInfo.nMessageLength,
                                    szSendUUIDString, sizeof(szSendUUIDString), false, NULL);
//que_log("Put After\n");
//sleep(2);
        WriteLog(LogType_StandardOut, LogLevel_Info, "SendData[%d] : [Thread:%02d]%s\n", nResult, pThreadInfo->nThreadIndex, sMessageInfo.paMessageData);
        if (nResult != MS_ERR_SUCCESS)
        {
            WriteLog(LogType_ErrorOut, LogLevel_Error,"[Thread:%02d]MQ Data Publish Error[Data:%s]!", pThreadInfo->nThreadIndex, sMessageInfo.paMessageData);
            // 메시지 전송 오류인 경우 처리
            // 오류인 경우 오류 로그에 추가하는 형태의 샘플이지만 DB 처리라면 재전송 여부를 결정해서 재전송 몇번 이내면 재전송 하고 그렇지 않으면 실패 처리하는 로직이 필요하다.
            WriteLog(LogType_ErrorOut, LogLevel_Error,"[Thread:%02d]MQ Data Publish Error[Data:%s]!", pThreadInfo->nThreadIndex, sMessageInfo.paMessageData);
        }
        else
        {
            //snprintf((char *)tbl_order, 2+1, "%.2s", szData+12);
            WriteLog(LogType_StandardOut, LogLevel_Info, "[Thread:%02d]MQ Data Publish[Data:%s]!", pThreadInfo->nThreadIndex, sMessageInfo.paMessageData);
            bMessageMatch = false;
            bTimeout = false;
        }

    CloseMQ(pThreadInfo->nThreadIndex, pThreadInfo->sPublishMQ.sMQConnectionState, &pThreadInfo->sPublishMQ.bMQConnection, &pThreadInfo->sPublishMQ.bMQChannelOpen, &pThreadInfo->sPublishMQ.sMQMutex);
    CloseMQ(pThreadInfo->nThreadIndex, pThreadInfo->sConsumeMQ.sMQConnectionState, &pThreadInfo->sConsumeMQ.bMQConnection, &pThreadInfo->sConsumeMQ.bMQChannelOpen, &pThreadInfo->sConsumeMQ.sMQMutex);
    pThreadInfo->nRunState = SOCK_THREAD_STATE_TERMINATED;
#if 1 //2019.07.12 메모리 증가로 Mutex Unlock 후 삭제 부분 추가
    pthread_mutex_destroy(&pThreadInfo->sContextMutex);
    pthread_mutex_destroy(&pThreadInfo->sPublishMQ.sMQMutex);
    pthread_mutex_destroy(&pThreadInfo->sConsumeMQ.sMQMutex);
#endif
    SafeFree((void **)&pThreadInfo);
    SafeFree((void **)&sMessageInfo.paMessageData);
    SafeFree((void **)&pnThreadExitCode);
    return 0;
}

int GetSerialNo()
{
    int nSerialNo;
    pthread_mutex_lock(&g_sSerialMutex);
    nSerialNo = g_nSerailNo++;
    pthread_mutex_unlock(&g_sSerialMutex);
    return nSerialNo;
}
