#include "ThreadUtil.h"
#include <string.h>

int g_nThreadCount = 0;

struct DOUBLELINKEDLISTINFO g_pThreadInfo;

/*
int g_nProcessType;

struct VALUE_STRING_PAIR_INFO g_sProcTypeInfo[] =
{
    { PROC_TYPE_GW,             "Gateway"           },
    { PROC_TYPE_APP,            "Application"       },
};
*/

int InitThreadConfig()
{
    char szValueTemp[MAX_STRING_SIZE + 1];
    // Read THREAD_COUNT
    if (GetConfigValue(THREAD_CONF_THREAD_COUNT_KEY, szValueTemp, sizeof(szValueTemp)) != CONF_ERR_SUCCESS)
    {
        WriteLog(LogType_ErrorOut, LogLevel_Error, "Thread THREAD_COUNT configuration not found!");
        return THREAD_ERR_CONFIG;
    }
    if (SafeStringToInt(szValueTemp, sizeof(szValueTemp), &g_nThreadCount) != CONF_ERR_SUCCESS)
    {
        WriteLog(LogType_ErrorOut, LogLevel_Error, "Thread THREAD_COUNT configuration error[ConfigFile:%s]!", szValueTemp);
        return THREAD_ERR_CONFIG;
    }

/*
    // Read PROCESS_TYPE
    if (GetConfigValue(THREAD_CONF_PROCESS_TYPE_KEY, szValueTemp, sizeof(szValueTemp)) != CONF_ERR_SUCCESS)
    {
        WriteLog(LogType_ErrorOut, LogLevel_Error, "Socket PROCESS_TYPE configuration not found[Available Value:Gateway or Application]!");
        return THREAD_ERR_CONFIG;
    }
    if (MappingStringToInt(szValueTemp, sizeof(szValueTemp), g_sProcTypeInfo, sizeof(g_sProcTypeInfo)/sizeof(struct VALUE_STRING_PAIR_INFO), &g_nProcessType) != CONF_ERR_SUCCESS)
    {
        WriteLog(LogType_ErrorOut, LogLevel_Error, "Socket PROCESS_TYPE configuration error[ConfigFile:%s,Available Value:Server or Client]!", szValueTemp);
        return THREAD_ERR_CONFIG;
    }
*/

    return THREAD_ERR_SUCCESS;
}

int InitThread_Before()
{
	InitDoubleLinkedList(&g_pThreadInfo);
	return DS_ERR_SUCCESS;
}

int InitThread(void *(*pWorkThreadFunc)(void *))
{
    int nResult;
    int nThreadIndex;
    struct THREAD_INFO *pThreadInfo;
    struct DOUBLELINKEDLISTNODE *pListNode;

    //InitDoubleLinkedList(&g_pThreadInfo);
    //nResult = pthread_tryjoin_np(&pThreadInfo, NULL);
	//if(nResult == '0')
		//RemoveTerminatedThread();
	
    for (nThreadIndex = 0; nThreadIndex < g_nThreadCount; nThreadIndex++)
    {
        pThreadInfo = NULL;
        nResult = SafeMemoryAlloc((void **)&pThreadInfo, sizeof(struct THREAD_INFO));
		//WriteLog(LogType_ErrorOut, LogLevel_Error, "Thread 1 [%s][%d] !", strerror(nResult), nResult);
        //pThreadInfo = SafeAlloc(pThreadInfo, sizeof(struct THREAD_INFO));
        SafeMemSet(pThreadInfo, sizeof(struct THREAD_INFO), 0, sizeof(struct THREAD_INFO));
        pthread_mutex_init(&pThreadInfo->sContextMutex, NULL);
        pthread_mutex_init(&pThreadInfo->sPublishMQ.sMQMutex, NULL);
        pthread_mutex_init(&pThreadInfo->sConsumeMQ.sMQMutex, NULL);
        pThreadInfo->nRunState = THREAD_STATE_INIT;
        //pthread_mutex_lock(&pThreadInfo->sContextMutex);
        pThreadInfo->nThreadIndex = nThreadIndex;
        pThreadInfo->bThreadLoop = true;
        //pthread_mutex_unlock(&pThreadInfo->sContextMutex);
        nResult = DoubleLinkedListAddNodeToHead(&g_pThreadInfo, pThreadInfo, &pListNode);
		//WriteLog(LogType_ErrorOut, LogLevel_Error, "Thread 2 [%s][%d] !", strerror(nResult), nResult);
        /*
         * Client는 Thread 생성 후 Thread에서 연결 시도 하므로 이에 대한 채널은 고정으로 사용 가능하다.
         */
        nResult = pthread_create(&pThreadInfo->nThreadID, NULL, pWorkThreadFunc, pThreadInfo);
		if(nResult != 0)
		{
			WriteLog(LogType_ErrorOut, LogLevel_Error, "Thread Create Fail [%s][%d] !", strerror(nResult), nResult);
		}
        //usleep(1000);
    }
    return 0;
}

#ifdef _ETCS_
int InitThreadWait(void *(*pWorkThreadFunc)(void *))
{
    int nResult;
    int nThreadIndex = 0;
    struct THREAD_INFO *pThreadInfo;
    struct DOUBLELINKEDLISTNODE *pListNode;

    //InitDoubleLinkedList(&g_pThreadInfo);

    pThreadInfo = NULL;
    nResult = SafeMemoryAlloc((void **)&pThreadInfo, sizeof(struct THREAD_INFO));
	WriteLog(LogType_ErrorOut, LogLevel_Error, "Thread 1-1 [%s][%d] !", strerror(nResult), nResult);
    //pThreadInfo = SafeAlloc(pThreadInfo, sizeof(struct THREAD_INFO));
    SafeMemSet(pThreadInfo, sizeof(struct THREAD_INFO), 0, sizeof(struct THREAD_INFO));
    pthread_mutex_init(&pThreadInfo->sContextMutex, NULL);
    pthread_mutex_init(&pThreadInfo->sPublishMQ.sMQMutex, NULL);
    pthread_mutex_init(&pThreadInfo->sConsumeMQ.sMQMutex, NULL);
    pThreadInfo->nRunState = THREAD_STATE_INIT;
    //pthread_mutex_lock(&pThreadInfo->sContextMutex);
    pThreadInfo->nThreadIndex = nThreadIndex;
    pThreadInfo->bThreadLoop = true;
    //pthread_mutex_unlock(&pThreadInfo->sContextMutex);
    
    // g_pThreadInfo 는 추후 종료된 프로세스를 관리하기 위한 것으로 Wait 함수에서는 사용할 필요가 없다.
    // join후 바로 삭제 프로세스로 진행하면 된다.
    //DoubleLinkedListAddNodeToHead(&g_pThreadInfo, pThreadInfo, &pListNode);
    /*
     * Client는 Thread 생성 후 Thread에서 연결 시도 하므로 이에 대한 채널은 고정으로 사용 가능하다.
     */
    pthread_create(&pThreadInfo->nThreadID, NULL, pWorkThreadFunc, pThreadInfo);
    pthread_join(pThreadInfo->nThreadID, (void **)&nThreadIndex);
    
    //pthread_mutex_destroy(&pThreadInfo->sContextMutex);
    //pthread_mutex_destroy(&pThreadInfo->sPublishMQ.sMQMutex);
    //pthread_mutex_destroy(&pThreadInfo->sConsumeMQ.sMQMutex);
    //SafeFree((void **)&pThreadInfo);
    //usleep(1000);

    return 0;
}
#endif

bool GetThreadLoopState(struct SOCKET_THREAD_INFO *pThreadInfo)
{
    bool bThreadProcessLoop;

    pthread_mutex_lock(&pThreadInfo->sContextMutex);
    bThreadProcessLoop = pThreadInfo->bThreadLoop;
    pthread_mutex_unlock(&pThreadInfo->sContextMutex);
    return bThreadProcessLoop;
}

void DaemonStart()
{

#ifndef _DEBUG

#ifdef _WIN32
    ShowWindow(FindWindowA("ConsoleWindowClass", NULL), false);

#else
    int nPID;
    int nLockFD;
    int nFileDescriptor;
    char szStringBuffer[MAX_STRING_SIZE + 1];

    // 이미 데몬프로세스임
    if (getppid() == 1)
    {
        return;
    }
    nPID = fork();
    // fork Error
    if (nPID < 0)
    {
        exit(1);
    }
    // fork success(in Parent Process)
    if (nPID > 0)
    {
        exit(0);
    }
    // nPID가 0인경우 차일드 프로세스
    // sid 할당
    setsid();

    // getdtablesize
    for (nFileDescriptor = sysconf(_SC_OPEN_MAX); nFileDescriptor >= 0; nFileDescriptor--)
    {
        close(nFileDescriptor);
    }
    // Standard IO Handle
    //nFileDescriptor = open("/dev/null" O_RDWR);
    //dup(nFileDescriptor);
    //dup(nFileDescriptor);
    // 신규 생성 파일의 파일 퍼미션 설정
    umask(027);
    // 실행 폴더 변경
    //chdir(RUNNING_DIR);
/*
    nLockFD = open(LOCK_FILE_, O_RDWR | O_CREATE, 0640);
    // can't open file
    if (nLockFD < 0)
    {
        exit(1);
    }
    // can't lock
    if (lockf(nLockFD, F_TLOCK, 0) < 0)
    {
        exit(0);
    }
    snprintf(szStringBuffer, sizeof(szStringBuffer), "%d\n", getpid());
    write(nLockFD, szStringBuffer, strnlen_s(szStringBuffer, sizeof(szStringBuffer)));
*/
#endif
#endif

#ifdef _WIN32
//    signal(SIGABRT, SignalHandler); //비정상적인 종료
//    signal(SIGFPE , SIG_IGN); //부동 소수점 오류
//    signal(SIGILL , SIG_IGN); //잘못된 명령
//    //signal(SIGINT, ); //Ctrl+C 신호 -> 윈도우에서는 지원되지 않음
//    signal(SIGSEGV, SIG_IGN); //잘못된 저장소 엑세스
//    signal(SIGTERM, SignalHandler); //종료 요청
#define SIGHUP 9990 //숫자 임의설정 - 추후 나오는 CASE문 때문에 사용

#else
    signal(SIGPIPE, SIG_IGN);
    signal(SIGCHLD, SIG_IGN);
    signal(SIGTSTP, SIG_IGN);
    signal(SIGTTOU, SIG_IGN);
    signal(SIGTTIN, SIG_IGN);
    signal(SIGHUP, SignalHandler);
    signal(SIGTERM, SignalHandler);

#endif
}

void SignalHandler(int nSignal)
{
    struct DOUBLELINKEDLISTNODE *pNode;
    struct SOCKET_THREAD_INFO *pSocketThreadInfo;
    struct THREAD_INFO *pThreadInfo;
    int *pnThreadResult;
    int nResult;

    switch (nSignal)
    {
    case SIGHUP:
        break;
    case SIGTERM:
        switch (g_nProcessType)
        {
        case MQ_PROC_TYPE_GW:
            nResult = DoubleLinkedListMoveToHead(&g_sConnectedSocketThreadInfo, &pNode);
            while (nResult != DS_ERR_SUCCESS)
            {
                // Mutex 추가 필요 : thread문 내부에 있는 while loop에 대한 mutex 처리는 어떻게 할 것인지?
                pSocketThreadInfo = (struct SOCKET_THREAD_INFO *)pNode->pNodeData;
                pthread_mutex_lock(&pSocketThreadInfo->sContextMutex);
                pSocketThreadInfo->bThreadLoop = false;
                pthread_mutex_unlock(&pSocketThreadInfo->sContextMutex);
                nResult = pthread_join(pSocketThreadInfo->nThreadID, (void **)&pnThreadResult);
                if (nResult != 0)
                {
                    // pthread_join 오류 -> 로그 저장

                }
                nResult = DoubleLinkedListMoveToNext(&g_sConnectedSocketThreadInfo, &pNode);
            }
            pthread_mutex_lock(&pSocketThreadInfo->sContextMutex);
            g_bSocketProcessLoop = false;
            pthread_mutex_unlock(&pSocketThreadInfo->sContextMutex);
            break;
        case MQ_PROC_TYPE_PROCESSOR:
            nResult = DoubleLinkedListMoveToHead(&g_pThreadInfo, &pNode);
            while (nResult != DS_ERR_SUCCESS)
            {
                // Mutex 추가 필요
                pThreadInfo = (struct THREAD_INFO *)pNode->pNodeData;
                pthread_mutex_lock(&pThreadInfo->sContextMutex);
                pThreadInfo->bThreadLoop = false;
                pthread_mutex_lock(&pThreadInfo->sContextMutex);
                nResult = pthread_join(pThreadInfo->nThreadID, (void **)&pnThreadResult);
                if (nResult != 0)
                {
                    // pthread_join 오류 -> 로그 저장

                }
                nResult = DoubleLinkedListMoveToNext(&g_pThreadInfo, &pNode);
            }
            pthread_mutex_lock(&pThreadInfo->sContextMutex);
            g_bSocketProcessLoop = false;
            pthread_mutex_unlock(&pThreadInfo->sContextMutex);
            break;
        }
        break;
    }
}
