#ifndef __SOCKET_H__
#define __SOCKET_H__

#ifdef __cplusplus
#ifndef BEGIN_DECLS
#define BEGIN_DECLS extern "C" {
#endif
#ifndef END_DECLS
#define END_DECLS }
#endif
#else
#ifndef BEGIN_DECLS
#define BEGIN_DECLS
#endif
#ifndef END_DECLS
#define END_DECLS
#endif
#endif


BEGIN_DECLS

#if defined(_WIN32) && defined(UNICODE)
#include <wchar.h>
#endif


#ifdef _WIN32
#include <ws2tcpip.h>
#include <winsock2.h>
#else
#include <netinet/tcp.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <uuid/uuid.h>
#endif

#include <pthread.h>	
#include <amqp.h>
#include <amqp_tcp_socket.h>
#include <amqp_framing.h>


#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <limits.h>
#include <errno.h>


#include "Common.h"
#include "SecureCodingUtil.h"
#include "StringUtil.h"
#include "Config.h"
#include "Log.h"
#include "DataStructure.h"
#include "SystemUtil.h"

#define SOCK_ERR_SUCCESS                    0       // 정상
#define SOCK_ERR_INVALID_SOCKET             -1      // 소켓 핸들러 비정상
#define SOCK_ERR_SETSOCKOPT                 -2      // 소켓 옵션 변경 오류
#define SOCK_ERR_CONFIG                     -3      // 통신 설정값 오류 (config 파일 확인 필요)
#define SOCK_ERR_CONNECT                    -4      // 연결 실패 또는 연결 끊김
#define SOCK_ERR_NONBLOCK                   -5      // NONBLOCK 모드로 인한 다음 재시도(오류 아님)
#define SOCK_ERR_READWRITE                  -6      // 소켓 읽기, 쓰기 오류
#define SOCK_ERR_BIND                       -8      // 서버모드에서 소켓 바인드 오류
#define SOCK_ERR_MQ_CHANNEL_FULL            -9      // Message Queue 서버 채널 full
#define SOCK_ERR_LISTEN                     -10     // Listening 소켓 오류
#define SOCK_ERR_MEM                        -11     // 메모리 오류
#define SOCK_ERR_MAKE_DATA                  -12     // 전문 데이터 생성 오류
#define SOCK_ERR_WSA_NOTINITIALIZED         -13     // WSA가 초기화


#define SOCK_CONF_CONN_TYPE_KEY             "CONNECTION_TYPE"
#define SOCK_CONF_TARGET_IP_KEY             "TARGET_IP"
#define SOCK_CONF_TARGET_PORT_KEY           "TARGET_PORT"
#define SOCK_CONF_TCP_NODELAY_KEY           "TCP_NODELAY"
#define SOCK_CONF_MAX_CONNECTION_KEY        "MAX_CONNECTION"
#define SOCK_CONF_ERROR_TIMEOUT_KEY         "ERROR_TIMEOUT"         // 지정된 시간동안 응답이 없는 경우 외부에 타임아웃 에러 전송(단위 : 밀리세컨드)
#define SOCK_CONF_TIMEOUT_STORE_TERM_KEY    "TIMEOUT_STORE_TERM"      // 메모리 리스트에서 저장되고 있는 시간(단위 : 밀리세컨드) - 외부에는 이미 타임아웃 오류가 전송된 상태
#define SOCK_CONF_CONNECTION_TIMEOUT_KEY    "CONNECTION_TIMEOUT"    // 데이터 통신이 설정된 값동안 없으면 연결 종료(단위 : 초)
#define SOCK_CONF_USE_GW_TIMEOUT_KEY        "USE_GW_TIMEOUT"

#define SOCK_CONF_CHANNEL_COUNT_KEY         "CHANNEL_COUNT"
#define SOCK_CONF_CHANNEL1_TYPE_KEY         "CHANNEL1_TYPE"
#define SOCK_CONF_CHANNEL2_TYPE_KEY         "CHANNEL2_TYPE"
#define SOCK_CONF_CHANNEL3_TYPE_KEY         "CHANNEL3_TYPE"
#define SOCK_CONF_CHANNEL4_TYPE_KEY         "CHANNEL4_TYPE"

#define MESSAGE_UNIQUEUE_KEY_POSITION_KEY   "MESSAGE_UNIQUEUE_KEY_POSITION"
#define MESSAGE_UNIQUEUE_KEY_LENGTH_KEY     "MESSAGE_UNIQUEUE_KEY_LENGTH"

#define SOCK_CONF_BYPASS_MODE_KEY           "BYPASS_MODE"

#define SOCK_CONF_MAX_CHANNEL_LIMIT         4

#define SOCK_CONF_MAX_CONNECTION_LIMIT      100

#define SOCK_BUFFER_SIZE                    8192
/*  STX(1) + CHANNEL_NO(1) + CRC(4) + ETX(1)*/

#define SOCK_STX_CHAR                       (char)'\x02'
#define SOCK_ETX_CHAR                       (char)'\x03'

#define SOCK_RESPONSE_CODE_POSITION         (SOCK_PACKET_LEN_MSG_LEN + SOCK_FRAME_LEN_ORG + SOCK_FRAME_LEN_OCCUR_TYPE + SOCK_FRAME_LEN_WORK_TYPE + SOCK_FRAME_LEN_MSG_TYPE + SOCK_FRAME_LEN_SERIAL_NO + SOCK_FRAME_LEN_REQUEST_DTIME)

#define SOCK_RESPONSE_CODE_LENGTH           "0001"
#define SOCK_RESPONSE_CODE_TIMEOUT          "0002"
#define SOCK_RESPONSE_CODE_SYSTEM           "0003"
#define SOCK_RESPONSE_CODE_ORGANIZATION     "0004"
#define SOCK_RESPONSE_CODE_FORMAT           "0005"
#define SOCK_RESPONSE_CODE_ALREADY          "0006"
#define SOCK_RESPONSE_CODE_WORKTYPE         "0007"
#define SOCK_RESPONSE_CODE_FILENAME         "0008"
#define SOCK_RESPONSE_CODE_PROTOCOL         "0009"
#define SOCK_RESPONSE_CODE_FILESENDING      "0010"
#define SOCK_RESPONSE_CODE_MAX_LENGTH       "0011"
#define SOCK_RESPONSE_CODE_OTHER            "0999"

#define MAX_FILE_SEND_SIZE                  4095

#define SOCK_PACKET_SIZE                    (SOCK_PACKET_LEN_STX + SOCK_PACKET_LEN_CHANNEL_NO + SOCK_PACKET_LEN_CRC + SOCK_PACKET_LEN_ETX)
#define SOCK_PACKET_NONCRC_SIZE             (SOCK_PACKET_LEN_STX + SOCK_PACKET_LEN_CRC + SOCK_PACKET_LEN_ETX)
#define SOCK_CRC_LENGTH                     4

#define SOCK_PACKET_LEN_STX                 1
#define SOCK_PACKET_LEN_CHANNEL_NO          1
#define SOCK_PACKET_LEN_MSG_LEN             4
#define SOCK_PACKET_LEN_CRC                 4
#define SOCK_PACKET_LEN_ETX                 1

#define SOCK_FRAME_LEN_ORG                  6
#define SOCK_FRAME_LEN_OCCUR_TYPE           1
#define SOCK_FRAME_LEN_WORK_TYPE            4
#define SOCK_FRAME_LEN_MSG_TYPE             4
#define SOCK_FRAME_LEN_FILENAME            16
#define SOCK_FRAME_LEN_SERIAL_NO            8
#define SOCK_FRAME_LEN_REQUEST_DTIME        14
#define SOCK_FRAME_LEN_RESP_CODE            4

#define SOCK_FRAME_SIZE_FILE                (SOCK_FRAME_LEN_ORG + SOCK_FRAME_LEN_OCCUR_TYPE + SOCK_FRAME_LEN_WORK_TYPE + SOCK_FRAME_LEN_MSG_TYPE + SOCK_FRAME_LEN_FILENAME + SOCK_FRAME_LEN_REQUEST_DTIME + SOCK_FRAME_LEN_RESP_CODE)
#define SOCK_FRAME_SIZE_MSG                 (SOCK_FRAME_LEN_ORG + SOCK_FRAME_LEN_OCCUR_TYPE + SOCK_FRAME_LEN_WORK_TYPE + SOCK_FRAME_LEN_MSG_TYPE + SOCK_FRAME_LEN_SERIAL_NO + SOCK_FRAME_LEN_REQUEST_DTIME + SOCK_FRAME_LEN_RESP_CODE)

#define SOCK_PROCESS_SLEEP                  1000
#define SOCK_MAX_DIRECTORY_SIZE             512

enum CONN_TYPE
{
    SOCK_CONN_TYPE_SERVER = 0,
    SOCK_CONN_TYPE_CLIENT,
    SOCK_CONN_COUNT,
};

enum CHANNEL_TYPES
{
    SOCK_CHANNEL_MESSAGE_SEND   = 0x01,
    SOCK_CHANNEL_MESSAGE_RECV   = 0x02,
    SOCK_CHANNEL_FILE_SEND      = 0x04,
    SOCK_CHANNEL_FILE_RECV      = 0x08,
};

enum CHANNELSTATE
{
    SOCK_CHANNELSTATE_NOCOMM = 0,
    SOCK_CHANNELSTATE_RECV,
    SOCK_CHANNELSTATE_SEND,
    SOCK_CHANNELSTATE_BOTH,
    SOCK_CHANNELSTATE_COUNT,
};

enum SOCK_THREAD_STATE
{
    SOCK_THREAD_STATE_INIT = 0,
    SOCK_THREAD_STATE_RUNNING,
    SOCK_THREAD_STATE_SUSPEND,
    SOCK_THREAD_STATE_TERMINATED,
    SOCK_THREAD_STATE_COUNT,
};

enum SOCK_RESP_CODE
{
    RESP_NORMAL = 0,
    RESP_LENGTH_ERROR,
    RESP_TIMEOUT,
    RESP_SYSTEM_FAIL,
    RESP_ORG_ID_FAIL,
    RESP_FORMAT_ERROR,
    RESP_ETC,
    RESP_ALREADY_RECV,
    RESP_UNKNOWN_WORKTYPE,
    RESP_ABNORMAL_FILENAME,
    RESP_LENGTH_ABNORMAL,
    RESP_PROTOCOL_ERROR,
    RESP_FILE_TRANSFERING,
    RESP_COUNT,
};

struct CONN_TYPE_INFO
{
	char szConnType[MAX_STRING_SIZE + 1];
	enum CONN_TYPE eConnType;
};
/*
struct CHANNEL_BUFFER
{
    char aRecvPacketBuffer[SOCK_BUFFER_SIZE + 1];
    char aSendPacketBuffer[SOCK_BUFFER_SIZE + 1];
    int nRecvPacketBufferOffset;
    int nSendPacketBufferOffset;
    pthread_mutex_t sRecvBufferMutex;
    pthread_mutex_t sSendBufferMutex;
    struct DOUBLELINKEDLISTINFO sChannelRecvFrameBuffer[SOCK_CONF_MAX_CHANNEL_LIMIT];
    struct DOUBLELINKEDLISTINFO sChannelSendFrameBuffer[SOCK_CONF_MAX_CHANNEL_LIMIT];
    pthread_mutex_t sChannelRecvMutex[SOCK_CONF_MAX_CHANNEL_LIMIT];
    pthread_mutex_t sChannelSendMutex[SOCK_CONF_MAX_CHANNEL_LIMIT];
    int nCurrentSendChannel;
    void (*FrameParsingFunction[SOCK_CONF_MAX_CHANNEL_LIMIT])(struct DOUBLELINKEDLISTINFO);
    int nChannelState[SOCK_CONF_MAX_CHANNEL_LIMIT];
    struct tm sLastRecvTime[SOCK_CONF_MAX_CHANNEL_LIMIT];
    struct tm sLastSendTime[SOCK_CONF_MAX_CHANNEL_LIMIT];
    int nLastRecvTimeMilliSec[SOCK_CONF_MAX_CHANNEL_LIMIT];
    int nLastSendTimeMilliSec[SOCK_CONF_MAX_CHANNEL_LIMIT];

};

struct THREAD_INFO
{
    bool bThreadLoop;
    int nRunState;
    pthread_mutex_t sContextMutex;
    pthread_t nThreadID;
    int nSocket;
    int nSocketIndex;
    amqp_socket_t *psMQSocket;
    amqp_connection_state_t sMQConnectionState;
};

*/

struct MESSAGE_INFO
{
    int nMessageLength;
    uuid_t aUUID;
    char *paMessageData;
};

struct MESSAGE_RESPONSE_INFO
{
    int nMessageLength;
    char *paMessageData;
    //void *pHeaderInfo;
    uuid_t aUUID;
    char szResponseQueuename[MAX_STRING_SIZE + 1];
    bool bResponse;             // 최초 데이터 수신측의 응답 전송 여부(외부)
    bool bTimeout;              // 최초 데이터 수신측의 타임아웃 여부 (외부)
    bool bResponseRecv;         // 전달 측의 응답 수신 여부(내부)
    uint64_t nDeliveryTag;
    struct tm sTransferTime;
    int nTransferMilliSecond;
};

#ifndef __STRUCT_MQ_CONNECTION__
#define __STRUCT_MQ_CONNECTION__

struct MQ_CONNECTION
{
    amqp_socket_t *psMQSocket;
    amqp_connection_state_t sMQConnectionState;
    pthread_mutex_t sMQMutex;
    bool bMQConnection;
    bool bMQChannelOpen;
};

#endif

struct SOCKET_THREAD_INFO
{
    bool bThreadLoop;
    int nRunState;
    pthread_mutex_t sContextMutex;
    pthread_t nThreadID;
    int nSocket;
    int nSocketIndex;
    /*
        amqp_socket_t *psMQSocket;
        amqp_connection_state_t sMQConnectionState;
        pthread_mutex_t sMessageQueueMutex;
        bool bMQConnection;
        bool bChannelOpen;
        pthread_mutex_t sMessageQueueMutex;
    */
    struct MQ_CONNECTION sPublishMQ;
    struct MQ_CONNECTION sConsumeMQ;

    // CHANNEL_BUFFER -> THREAD_INFO
    char aRecvPacketBuffer[SOCK_BUFFER_SIZE + 1];
    char aSendPacketBuffer[SOCK_BUFFER_SIZE + 1];
    int nRecvPacketBufferOffset;
    int nSendPacketBufferOffset;
    pthread_mutex_t sRecvBufferMutex;
    pthread_mutex_t sSendBufferMutex;
    struct DOUBLELINKEDLISTINFO sChannelRecvFrameBuffer[SOCK_CONF_MAX_CHANNEL_LIMIT];
    struct DOUBLELINKEDLISTINFO sChannelSendFrameBuffer[SOCK_CONF_MAX_CHANNEL_LIMIT];
    struct DOUBLELINKEDLISTINFO sChannelRecvResponseBuffer[SOCK_CONF_MAX_CHANNEL_LIMIT];
    struct DOUBLELINKEDLISTINFO sChannelSendResponseBuffer[SOCK_CONF_MAX_CHANNEL_LIMIT];

    pthread_mutex_t sChannelRecvMutex[SOCK_CONF_MAX_CHANNEL_LIMIT];
    pthread_mutex_t sChannelSendMutex[SOCK_CONF_MAX_CHANNEL_LIMIT];
    pthread_mutex_t sChannelRecvResponseMutex[SOCK_CONF_MAX_CHANNEL_LIMIT];
    pthread_mutex_t sChannelSendResponseMutex[SOCK_CONF_MAX_CHANNEL_LIMIT];
    int nCurrentSendChannel;
    void (*FrameParsingFunction[SOCK_CONF_MAX_CHANNEL_LIMIT])(struct DOUBLELINKEDLISTINFO);
    int nChannelState[SOCK_CONF_MAX_CHANNEL_LIMIT];
    struct tm sLastRecvTime[SOCK_CONF_MAX_CHANNEL_LIMIT];
    struct tm sLastSendTime[SOCK_CONF_MAX_CHANNEL_LIMIT];
    int nLastRecvTimeMilliSec[SOCK_CONF_MAX_CHANNEL_LIMIT];
    int nLastSendTimeMilliSec[SOCK_CONF_MAX_CHANNEL_LIMIT];
    char szRemoteIP[MAX_STRING_SIZE + 1];
    int nRemotePort;
    //char szRecvReplayQueueName[MAX_STRING_SIZE + 1];
};


/*
 * Merge SOCKET_THREAD_INFO & CHANNEL_BUFFER
 */
extern char g_szProcessname[MAX_STRING_SIZE + 1];

extern int g_nListeningSocket;
extern int g_eConnectionType;
extern char g_szConnectionIP[MAX_STRING_SIZE + 1];
extern int  g_nServicePort;
extern int g_nMaxConnections;
extern bool g_bUseTCPNoDelay;
extern int g_nChannelCount;
extern int g_nChannelTypeFlags[SOCK_CONF_MAX_CHANNEL_LIMIT];
extern int g_nListeningSocket;
extern bool g_bSocketProcessLoop;
extern int g_nErrorTimeout;
extern int g_nTimeoutStoreTerm;
extern int g_nConnectionTimeout;
extern bool g_bUseGWTimeout;

extern int g_nMessageUniqueueKeyPos;
extern int g_nMessageUniqueueKeyLen;

extern struct VALUE_STRING_PAIR_INFO g_sRespCodeInfo[RESP_COUNT];


extern struct SOCKET_THREAD_INFO **g_pChannelMQThreadInfo;

extern struct DOUBLELINKEDLISTINFO g_sConnectedSocketThreadInfo;

extern struct VALUE_STRING_PAIR_INFO g_sConnTypeInfo[SOCK_CONN_COUNT];



extern int (*g_fnProcessMessageSocketData)(struct SOCKET_THREAD_INFO *pThreadInfo, int nChannelNo);
extern int (*g_fnProcessFileSocketData)(struct SOCKET_THREAD_INFO *pThreadInfo, int nChannelNo);
extern int (*g_fnProcessMessageMQSendData)(struct SOCKET_THREAD_INFO *pThreadInfo, int nChannelNo, char *pData, int nDataLength, char *szCorrelationID, int nCorrelationIDLength, char *szResponseQueuename, int nReplyQueuenameLength, bool *bMQConnection, bool *bChannelOpen, uint64_t nDeliveryTag);
extern int (*g_fnProcessMessageMQRespData)(struct SOCKET_THREAD_INFO *pThreadInfo, int nChannelNo, char *pData, int nDataLength, char *szCorrelationID, int nCorrelationIDLength, char *szResponseQueuename, int nReplyQueuenameLength, bool *bMQConnection, bool *bChannelOpen, uint64_t nDeliveryTag);
extern int (*g_fnProcessFileMQData)(struct SOCKET_THREAD_INFO *pThreadInfo, int nChannelNo, char *pData, int nDataLength, char *szCorrelationID, int nCorrelationIDLength, char *szResponseQueuename, int nReplyQueuenameLength, bool *bMQConnection, bool *bChannelOpen, uint64_t nDeliveryTag);
extern void (*g_fnInitializeFileTransferData)(int nSocketIndex);

int ConnectTo(const char *szHostname, const int nPort, int *pnSocket);

int InitSocketConfig();
int InitSocket();
int RecvSocket(int nSocket, struct SOCKET_THREAD_INFO *pCHANNEL_BUFFER, bool *bConnected);
int SendSocket(int nSocket, struct SOCKET_THREAD_INFO *pCHANNEL_BUFFER, bool *bConnected);
int CheckConnection(int nSocket, bool *pbConnected);
void *SocketProcessLoop(void *pArg);
uint16_t CalculateCRC16(uint16_t nSeed, void *pvData, size_t nLength);
uint32_t CalculateCRC32(uint32_t nSeed, void *pvData, size_t nLength);

bool SetSocketBlockingEnabled(int nSocket, bool bBlocking);
void InitCHANNEL_BUFFER(struct SOCKET_THREAD_INFO *pCHANNEL_BUFFER);
void DestroyCHANNEL_BUFFER(struct SOCKET_THREAD_INFO *pThreadInfo);
int ServerMQChannelAllocation(struct SOCKET_THREAD_INFO *pThreadInfo, int *pnChannelNo);
int MakePacket(int nChannelNo, const char *pszFrameData, int nFrameLen, char *pszPacket, int *nPacketLen);
int MakeFileFrame(char *pszOrganizationID, int nOrganizationIDLen, char cOccurType, char *pszWorkType, int nWorkTypeLen, char *pszMsgType, int nMsgTypeLength, char *pszFilename, int nFilenameLen, char *pszRequestDTime, int nRequestDTimeLen, char *pszResponseCode, int nResponseCodeLen, char *pData, int nDataLen, char *pszFrameData, int *nFrameDataLen);
int MakeMSGFrame(char *pszOrganizationID, int nOrganizationIDLen, char cOccurType, char *pszWorkType, int nWorkTypeLen, char *pszMsgType, int nMsgTypeLength, char *pszSerialID, int nSerialIDLen, char *pszRequestDTime, int nRequestDTimeLen, char *pszResponseCode, int nResponseCodeLen, char *pData, int nDataLen, char *pszFrameData, int *nFrameDataLen);
void ProcessSendTimeout(struct SOCKET_THREAD_INFO *pThreadInfo, int nChannelNo);
void ProcessResponseTimeout(struct SOCKET_THREAD_INFO *pThreadInfo, int nChannelNo);
int SendPacket(struct SOCKET_THREAD_INFO *pThreadInfo, int nChannelNo, uuid_t *pUUID, const char *aMessageBody, int nMessageBodySize);
int GetChannelNo(int nChannelType);
bool GetSocketThreadLoopState(struct SOCKET_THREAD_INFO *pThreadInfo);
bool GetApplicationLoopState();
#ifdef _WIN32
int inet_pton(int af, const char *src, void *dst);
const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);
#endif
char *GetSocketRespCode(int nValue);


#include "MessageQueue.h"

END_DECLS

#endif
