/*
 *========================================================================
 *
 *   시스템명 : 요금징수 시스템
 *   파 일 명 : wrk_data_hnd_main.pc
 *   작 성 자 : 조보용
 *   작 성 일 : 2012.09.13
 *   수 정 일 :
 *   설    명 : 근무자료 처리 프로세스
 *
 *========================================================================
*/

#include "wrk_data_def.h"
#include "MessageProcessor.h"
#include "taglnk_frame.h"

#define _WEBSVR_
//#define _ORACLE_

char g_szProcessname[MAX_STRING_SIZE + 1];
char g_szProcessPath[PATH_MAX + 1];
#if 1
int sKind;
extern int g_nSerailNo = 1;

pthread_mutex_t g_sSerialMutex = PTHREAD_MUTEX_INITIALIZER;
#endif
void    SIG_term(int sig)
{
    int    ii;

    for (ii = 0; ii < 3; ii++)
    {
        //EXEC SQL ROLLBACK WORK RELEASE;
        if (!SQLCODE) break;
    }
    printf_s( " TERMINATE SIGNAL[%d]...... END\n", sig);

    exit(-1);
}

struct    writeque {           /* PACKET 변환 TABLE    */
    int    cmd;                /* command              */
    int    wrcheck;            /* Check                */
} writeque[] = {
   /* cmd */                  /* Check */
 { NS_RQ_SEQ_NO             , 0 },  /* 0x01 전송연번요구 */
 { NS_COMM_CONTINUE_RQ      , 0 },  /* 0x02 교신재개요구 */
 { NS_TM_RQ                 , 0 },  /* 0x04 시각요구 */
 { NS_TBL_ORDER             , 0 },  /* 0x24 테이블 요구 */
 { NS_LANE_STAT             , 0 },  /* 0x75 차로상태(STATUS) */
 { NS_RPR_DATA              , 1 },  /* 0x76 보수데이터 */
 { NS_CLOSE_PASS            , 1 },  /* 0x73 야간계수 데이터 */
 { NS_WRK_START             , 1 },  /* 0x61 근무개시보고 */
 { NS_HND_DATA              , 1 },  /* 0x17 처리 데이터 */
 { NS_CRD_HND_DATA          , 1 },  /* 0x50 카드처리 데이터 */
 { NS_WRK_END               , 1 },  /* 0x28 근무종료데이터 */
 { NS_LANE_LINE_TEST        , 0 },  /* 0x2F 차로측기동회선테스트 테이터 */
 { NS_PLZ_LINE_TEST_REPLY   , 0 },  /* 0xD2 영업소측 기동회선테스트 테이터 응답 */
 { NS_REMOTE_LANE_STAT_REPLY, 0 },  /* 0xD4 원격차로상태응답 */
 { NS_NON_PAY_SRCH          , 1 },  /* 0x21 미납정보조회 */
 { NS_CRD_SRCH              , 1 },  /* 0x21 카드번호조회 */
 { NS_REFUND_DATA           , 1 },  /* 0x40 환불처리 데이터 */
 { -1                       , 0 }
};

/*
 *========================================================================
 *
 *   함 수 명 : main()
 *   작 성 자 : 조보용
 *   작 성 일 : 2018.12.10
 *   수 정 일 :
 *   설    명 : 근무 자료 처리 DAEMON PROCESS(kmpsvr) MAIN
 *
 *========================================================================
*/
int main(int argc, char *argv[])
{
    char szProcessname[PATH_MAX + 1];
    char *pFindPointer;
    //int nPathIndicatorPos;
    int nResult;

    //DaemonStart();
    nResult = SafeStrCopy(szProcessname, sizeof(szProcessname), argv[0]);
    if (nResult != SEC_ERR_SUCCESS)
    {
        printf_s("strcpy_s Error![%d]\n", nResult);
    }
    pFindPointer = szProcessname;
    nResult = SafeStrrChr(szProcessname, strnlen_s(szProcessname, sizeof(szProcessname)), '/', &pFindPointer);

    printf_s("szProcess Name : %s\n", szProcessname);

    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, argv[0]);
    printf_s("g_szProcess Name : %s\n", g_szProcessname);
    if (g_szProcessPath == NULL)
    {
        printf_s("Can't get process path[Cann't load config file]!\nExit Program!");
        return -1;
    }

//    lane_stat_info_read();
    nResult = InitMessageProcessConfig();

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

    proc_log("Process Started![%s]", g_szProcessname);
	
	//EXEC SQL ENABLE THREADS;
    //2019.06.27 InitDoubleLinkedList Main 선언
//printf("wrk 1\n");
#if 0 //DB접속 방법 변경(접속 유지 방법)
#ifdef _ORACLE_
    if(c_DbConnect()<0)
    {
        printf_s( "worksvr db connect error\n");
        EXEC SQL ROLLBACK RELEASE;
        return -1;
    }
#endif
#endif
    InitThread_Before();
//printf("wrk 2\n");
    /* Socket 처리 루프 : 종료시 프로그램 종료됨 */
    nResult = InitThread(WorkThread);

    while(true)
    {
        sleep(5);
    }

    proc_log("Process Stopped![%s]", g_szProcessname);
#ifdef _ORACLE_
    EXEC SQL ROLLBACK RELEASE;
#endif
    return 0;
}
/*
 *========================================================================
 *
 *   함 수 명 : proc_data()
 *   작 성 자 : 조보용
 *   작 성 일 : 2012.09.13
 *   수 정 일 :
 *   설    명 : Winfo Parsing & Processing
 *
 *========================================================================
*/

int proc_data(char *buffer, char *rtn)
{
    struct rcv_lane_data *rd;
    int irv = 0;
    int jbcd;
	int chkdb = 0;
//EXEC SQL BEGIN DECLARE SECTION;
	char now[14+1];
//EXEC SQL END DECLARE SECTION;
    char ht[2+1];
    char proc_Data[MAX_STRING_SIZE + 1]; memset(proc_Data, 0x00, sizeof(proc_Data));
	snprintf(proc_Data, MAX_STRING_SIZE-45, "%s", buffer);
	//EXEC SQL ENABLE THREADS;
	
	//sql_context sSQLContext;
	
    rd = (struct rcv_lane_data *)buffer;
//proc_log("수신자료 [%s]\n", rd);
    sprintf(ht, "%.2s%c", rd->kind, '\0');
    jbcd = strtoul(ht, NULL, 16);

	//EXEC SQL CONTEXT ALLOCATE :sSQLContext;
#if 1 //DB접속 건당 접속 후 처리 이후 해제
	//EXEC SQL CONTEXT USE :sSQLContext;
#ifdef _ORACLE_
    if(c_DbConnect()<0)
    {
        printf_s( "worksvr db connect error\n");
        EXEC SQL ROLLBACK RELEASE;
        return -1;
    }
#endif
#else
#ifdef _ORACLE_
	EXEC SQL
    SELECT TO_CHAR(SYSDATE, 'YYYYMMDDHHMISS')
	INTO :now
    FROM DUAL;
	
	if(sqlca.sqlcode != 0)
	{
		chkdb = 1;
		if(c_DbConnect()<0)
        {
            printf_s( "worksvr db connect error\n");
            EXEC SQL ROLLBACK RELEASE;
            return -1;
        }
	}
#endif
#endif
//proc_log("처리종별 [%.2s][%x]\n", rd->kind, jbcd);
    switch (jbcd)
    {
        case NS_CLOSE_PASS:
            printf_s( " < 야간계수 > \n" );
            //irv = proc_close_pass(buffer, rtn);
            irv = proc_close_pass(&proc_Data, rtn);
            break;
        case NS_HND_DATA:
            printf_s( " < 처리 > \n" );
            //irv = proc_hnd_data(buffer, rtn);
            irv = proc_hnd_data(&proc_Data, rtn);
            break;
        case NS_CRD_HND_DATA:
            printf_s( " < 카드처리 > \n" );
            //irv = proc_card_data(buffer, rtn);
            irv = proc_card_data(&proc_Data, rtn);
            break;
        case NS_WRK_END:
            printf_s( " < 근무종료 > \n" );
            //irv = proc_wrk_end(buffer, rtn);
            irv = proc_wrk_end(&proc_Data, rtn);
            break;
		case NS_REFUND_DATA:
            printf_s( " < 환불처리 > \n" );
            //irv = proc_refund(buffer, rtn);
            irv = proc_refund(&proc_Data, rtn);
            break;			
        default:
            irv = -1;
            break;
    }
//proc_log("2222222222 [%s] 2222222222222\n", rtn);
	sKind = jbcd;

    if (irv < 0)
    {
#ifdef _ORACLE_
        EXEC SQL ROLLBACK WORK;
#endif
        printf_s("[%x]irv : %d, Process Fail\n", jbcd, irv);
    }
    else
    {
        printf_s("irv : %d, COMMIT\n", irv);
#ifdef _ORACLE_
        EXEC SQL COMMIT WORK;
#endif
    }
//proc_log("E5\n");
//proc_log("결과자료 [%s]\n", szReplyBuffer);
#if 1 //접속 후 처리 이후 해제 로직
#ifdef _ORACLE_
    EXEC SQL ROLLBACK RELEASE;
#endif
#endif
	//EXEC SQL CONTEXT USE DEFAULT;
	//EXEC SQL CONTEXT FREE :sSQLContext;
    return(irv);
}

int InitMessageProcessConfig()
{
    char szConfigfilename[PATH_MAX + 1];
    char szConfigfilenameTemp[PATH_MAX + 1];

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

    realpath(szConfigfilenameTemp, szConfigfilename);

    SafeStrCat(szConfigfilename, sizeof(szConfigfilename), "/", 1);
    SafeStrCat(szConfigfilename, sizeof(szConfigfilename), g_szProcessname, strnlen_s(g_szProcessname, sizeof(g_szProcessname)));
    SafeStrCat(szConfigfilename, sizeof(szConfigfilename), CONF_FILE_EXT, strnlen_s(CONF_FILE_EXT, sizeof(CONF_FILE_EXT)));

    printf_s("Process Name : %s\n", g_szProcessname);
    printf_s("Process Path : %s\n", g_szProcessPath);
    printf_s("Config Filename : %s\n", szConfigfilename);

    ReadConfigFile(true, szConfigfilename);

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

    if (InitProcessorMQConfig() != MQ_ERR_SUCCESS)
    {
        return MP_ERR_CONFIG;
    }

#ifdef _WEBSVR_
    if (InitProcessor2MQConfig() != MQ_ERR_SUCCESS)
    {
        return MP_ERR_CONFIG;
    }
#endif
    if (InitThreadConfig() != THREAD_ERR_SUCCESS)
    {
        return MP_ERR_CONFIG;
    }

    return MP_ERR_SUCCESS;
}

void *WorkThread(void *pData)
{
    struct THREAD_INFO *pThreadInfo;
    char szMessageBuffer[MAX_STRING_SIZE + 1];
	char szMessageBuffer2[MAX_STRING_SIZE + 1];
	char szReplyBuffer[MAX_STRING_SIZE + 1];
    int nResult;
    int *pnThreadExitCode;
    char szCorrelationID[MAX_STRING_SIZE + 1];
    char szReplyQueuename[MAX_STRING_SIZE + 1];
    char szExchangername[MAX_STRING_SIZE + 1];
    int nMessageLength;
    uint64_t nDeliveryTag;

#ifdef _WEBSVR_
	struct MESSAGE_INFO sMessageInfo;
	struct tm tNow;

	char plz_id[2+1];
	char szWorkType[4+1];
	char szTopic[MAX_STRING_SIZE + 1];
	char szSendUUIDString[UUID_STRING_SIZE + 1];
	int  mkind;
	int  nSerialNo;
	int  nMillisecond;
	
	uuid_t sUUIDTemp;
	
	GetCurrTime(&tNow, &nMillisecond);

#ifdef _WIN32
        uuid_generate(&sUUIDTemp);
        uuid_unparse(&sUUIDTemp, szSendUUIDString);
#else
        uuid_generate(sUUIDTemp);
        uuid_unparse(sUUIDTemp, szSendUUIDString);
#endif

#endif
//printf("1\n");
    pThreadInfo = (struct THREAD_INFO *)pData;
    pnThreadExitCode = NULL;
    nResult = SafeMemoryAlloc((void **)&pnThreadExitCode, sizeof(int));
    //pnThreadExitCode = SafeAlloc(pnThreadExitCode, sizeof(int));
    *pnThreadExitCode = 0;
    while (pThreadInfo->bThreadLoop)
    {
        if (pThreadInfo->sPublishMQ.bMQConnection == false || pThreadInfo->sPublishMQ.bMQChannelOpen == false
            || pThreadInfo->sConsumeMQ.bMQConnection == false || pThreadInfo->sConsumeMQ.bMQChannelOpen == false)
        {
            if (ConnectProcessorMQ(pThreadInfo, true) != MQ_ERR_SUCCESS)
            {
                WriteLog(LogType_StandardOut, LogLevel_Debug, "[Thread:%02d]ConnectProcessMQ is failed!", pThreadInfo->nThreadIndex);
#if 0 //2019.07.10 초기화 추가
	            pThreadInfo->nRunState = SOCK_THREAD_STATE_TERMINATED;
			    pthread_mutex_destroy(&pThreadInfo->sContextMutex);
				pthread_mutex_destroy(&pThreadInfo->sPublishMQ.sMQMutex);
				pthread_mutex_destroy(&pThreadInfo->sConsumeMQ.sMQMutex);
#endif
                continue;
            }
            WriteLog(LogType_StandardOut, LogLevel_Debug, "[Thread:%02d]ConnectProcessMQ is succeeded!!", pThreadInfo->nThreadIndex);
        }
//printf("Reply Que[%s]\n", szReplyQueuename);
        //amqp_maybe_release_buffers(pThreadInfo->sMQConnectionState);
        nResult = GetMessageFromQueue(pThreadInfo->sConsumeMQ.sMQConnectionState, szExchangername, sizeof(szExchangername), szMessageBuffer, sizeof(szMessageBuffer),
									szCorrelationID, sizeof(szCorrelationID), szReplyQueuename, sizeof(szReplyQueuename),
									&pThreadInfo->sConsumeMQ.bMQConnection, &pThreadInfo->sConsumeMQ.bMQChannelOpen, &nDeliveryTag, NULL);

        nMessageLength = strnlen_s(szMessageBuffer, sizeof(szMessageBuffer));
//printf_s("DB Proc Before[%d][%s][%s]\n", nResult, szMessageBuffer, szReplyQueuename);
        if (nResult == MQ_ERR_SUCCESS)
        {
            printf_s("Message Received : %s\n", szMessageBuffer);
            //WriteRawCommLog(LogType_CommOut, LogLevel_Info, pThreadInfo->nThreadIndex, 'C', "%s", szMessageBuffer);
            WriteQueueLog(LogType_MQOut, LogLevel_Info, pThreadInfo->nThreadIndex, 'C', g_szMQMessageRecvQueuename, "", szCorrelationID, nMessageLength, "%s", szMessageBuffer);
            WriteLog(LogType_StandardOut, LogLevel_Info, "[Thread:%02d]MQ Data Consume[Data:%s]!", pThreadInfo->nThreadIndex, szMessageBuffer);
            memset(szReplyBuffer, 0x00, sizeof(szReplyBuffer));
#ifdef _WEBSVR_
			pthread_mutex_lock(&g_sSerialMutex);
#endif
            nResult = proc_data(szMessageBuffer+45, &szReplyBuffer);
//proc_log("####### [%s] #######\n", szReplyBuffer);
#ifdef _WEBSVR_
			mkind = sKind;
			pthread_mutex_unlock(&g_sSerialMutex);
#endif
//printf_s("DB 처리 결과[%d]\nMessage[%s]\nReply[%s]\n", nResult, szMessageBuffer, szReplyBuffer);
/*            pThreadInfo->nRunState = SOCK_THREAD_STATE_TERMINATED;
            pthread_mutex_destroy(&pThreadInfo->sContextMutex);
            pthread_mutex_destroy(&pThreadInfo->sPublishMQ.sMQMutex);
            pthread_mutex_destroy(&pThreadInfo->sConsumeMQ.sMQMutex);
            return(nResult);*/
#ifdef _WEBSVR_
			if(mkind == NS_HND_DATA || mkind == NS_REFUND_DATA || mkind == NS_CRD_HND_DATA || mkind == NS_WRK_END || mkind == NS_CLOSE_PASS)
			{

			memset(szMessageBuffer2, 0x00, sizeof(szMessageBuffer2));
			memset(szWorkType, 0x00, sizeof(szWorkType));
			switch(mkind)
			{
				case NS_HND_DATA:
				case NS_REFUND_DATA:
					sprintf((char *)szWorkType, "0002");
					szWorkType[4] = '\0';
					break;
				case NS_CRD_HND_DATA:
					sprintf((char *)szWorkType, "0003");
					szWorkType[4] = '\0';
					break;
				case NS_WRK_END:
					sprintf((char *)szWorkType, "0004");
					szWorkType[4] = '\0';
					break;
				case NS_CLOSE_PASS:
					sprintf((char *)szWorkType, "0005");
					szWorkType[4] = '\0';
					break;
				default :
					amqp_basic_ack(pThreadInfo->sConsumeMQ.sMQConnectionState, 1, nDeliveryTag, 0);
					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);
					return pnThreadExitCode;
			}
			sprintf(plz_id, "%.2s", szMessageBuffer+47);
			plz_id[2] = '\0';

			nSerialNo = GetSerialNo();

			snprintf(szMessageBuffer2, sizeof(szMessageBuffer2), "%06dC%s0010%08d%04d%02d%02d%02d%02d%02d0000%s", lb_data2atoi(plz_id,2), szWorkType,
					 nSerialNo++, tNow.tm_year + 1900, tNow.tm_mon + 1, tNow.tm_mday, tNow.tm_hour, tNow.tm_min, tNow.tm_sec, szReplyBuffer);

			nMessageLength = strnlen_s(szMessageBuffer2, sizeof(szMessageBuffer2));
			sMessageInfo.nMessageLength = nMessageLength;
			sMessageInfo.paMessageData = NULL;
			SafeMemoryAlloc((void **)&sMessageInfo.paMessageData, sMessageInfo.nMessageLength + 5);
			snprintf(sMessageInfo.paMessageData, sMessageInfo.nMessageLength + 5, "%04X", sMessageInfo.nMessageLength);
			SafeStrCat(sMessageInfo.paMessageData + 4, sMessageInfo.nMessageLength + 1, szMessageBuffer2, strnlen_s(szMessageBuffer2, sizeof(szMessageBuffer2)));
			snprintf(szTopic, sizeof(szTopic), "%06d.%s0010", lb_data2atoi(plz_id,2), szWorkType);
			
			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);

			if (nResult != MQ_ERR_SUCCESS)
			{
				//proc_log("[Thread:%02d]MQ Data Publish Error[Data:%s]!", pThreadInfo->nThreadIndex, sMessageInfo.paMessageData);
				// 메시지 전송 오류인 경우 처리
				// 오류인 경우 오류 로그에 추가하는 형태의 샘플이지만 DB 처리라면 재전송 여부를 결정해서 재전송 몇번 이내면 재전송 하고 그렇지 않으면 실패 처리하는 로직이 필요하다.
				WriteLog(LogType_StandardOut, LogLevel_Info, "[Thread:%02d]MQ Data Publish Error[Data:%s]!", pThreadInfo->nThreadIndex, sMessageInfo.paMessageData);
			}
			
			}
			SafeFree((void **)&sMessageInfo.paMessageData);
#endif
            sprintf(szMessageBuffer+45, "%s", szReplyBuffer);
            //nResult = PutReplyMessageToQueue(pThreadInfo->sPublishMQ.sMQConnectionState, szCorrelationID, szReplyQueuename, szMessageBuffer, strnlen_s(szMessageBuffer, sizeof(szMessageBuffer)), &pThreadInfo->sPublishMQ.bMQConnection, &pThreadInfo->sPublishMQ.bMQChannelOpen);
            amqp_basic_ack(pThreadInfo->sConsumeMQ.sMQConnectionState, 1, nDeliveryTag, 0);
            //nResult = PutReplyMessageToQueue(pThreadInfo->sPublishMQ.sMQConnectionState, szCorrelationID, szReplyQueuename, szMessageBuffer, strnlen_s(szMessageBuffer, sizeof(szMessageBuffer)), &pThreadInfo->sPublishMQ.bMQConnection, &pThreadInfo->sPublishMQ.bMQChannelOpen);
//printf_s("\n응답처리 결과[%d][%d]\nMessage[%s]\nReplyQue[%s]\n", nResult, strnlen_s(szMessageBuffer, sizeof(szMessageBuffer)), szMessageBuffer, szReplyQueuename);
            WriteQueueLog(LogType_MQOut, LogLevel_Info, pThreadInfo->nThreadIndex, 'P', "", szReplyQueuename, szCorrelationID, nMessageLength, "%s", szMessageBuffer);
            WriteLog(LogType_StandardOut, LogLevel_Info, "[Thread:%02d]Response Publish[Queue:%s,UUID:%s,Data:%s]!", pThreadInfo->nThreadIndex, szReplyQueuename, szCorrelationID, szMessageBuffer);
        }
        else
        {
            WriteLog(LogType_StandardOut, LogLevel_Debug, "[Thread:%02d]MQ Data Consume Error!", pThreadInfo->nThreadIndex);
        }
    }
    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);

#if 0 //2019.07.10 초기화 추가
	pThreadInfo->nRunState = SOCK_THREAD_STATE_TERMINATED;
	pthread_mutex_destroy((char *)&pThreadInfo->sContextMutex);
	pthread_mutex_destroy((char *)&pThreadInfo->sPublishMQ.sMQMutex);
	pthread_mutex_destroy((char *)&pThreadInfo->sConsumeMQ.sMQMutex);
#endif
    return pnThreadExitCode;
}

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