//---------------------------------------------------------------------------


#pragma hdrstop

#include "ClientSessionF.h"
#include "PacketHandllingF.h"
#include "VMSCommLibF.h"
//---------------------------------------------------------------------------

#pragma package(smart_init)

TClientSession::TClientSession(SOCKET ASock, SOCKADDR_IN* ASockAddr)
{
    Create(ASock, ASockAddr);
}
//---------------------------------------------------------------------------
TClientSession::TClientSession()
{
    FSocket = INVALID_SOCKET;
    FState  = eSS_none;

    FIpAddress = "";
    FPort      = 0;

    Init();
}
//---------------------------------------------------------------------------
void TClientSession::Create(SOCKET ASock, SOCKADDR_IN* ASockAddr)
{
    FSocket = ASock;
    FState  = eSS_OnConnect;

    memcpy(&FSocketAddr, ASockAddr, sizeof(SOCKADDR_IN));
    FIpAddress.printf("%s", inet_ntoa(FSocketAddr.sin_addr));
    FPort = ntohs(FSocketAddr.sin_port);

    Init();
}
//---------------------------------------------------------------------------
void TClientSession::Init()
{
    FConnected  = false;
    FRecvIdx    = 0;
    FSendIdx    = 0;
    FPacketLen  = 0;
    FPacketSent = 0;
    FPacketPtr  = NULL;
    FCDSCtlr    = NULL;
    FCDSLogCtlr = NULL;
    FCloseTick  = 0;
    FChkRecvTO  = false;
    FTmrSend    = Now();
    FTmrRecv    = Now();
}
//---------------------------------------------------------------------------
TClientSession::~TClientSession()
{
}
//---------------------------------------------------------------------------
int TClientSession::SocketError()
{
    FLastErr = WSAGetLastError();

    LPVOID lpMsgBuf = NULL;
    try
    {
        try
        {
            FormatMessage(
                FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
                NULL,
                FLastErr,
                //MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
                MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT),
                (LPTSTR) &lpMsgBuf,
                0,
                NULL
            );

            char *pData = (char*)lpMsgBuf;
            if (strlen(pData) > 2)
            {
                pData[strlen(pData)-2] = 0x00;
                pData[strlen(pData)-1] = 0x00;
            }
            FErrorString.printf("%s", pData);
        }
        catch(Exception &e)
        {
            FErrorString.printf("%s, %s", AnsiString(e.ClassName()).c_str(), AnsiString(e.Message).c_str());
        }
    }
    __finally
    {
        if (lpMsgBuf) LocalFree(lpMsgBuf);
        lpMsgBuf = NULL;
    }
    return FLastErr;
}
//---------------------------------------------------------------------------
bool TClientSession::OnConnect()
{
	FConnected = true;

    if (!CheckLoginClient())
    {
        Disconnect();
        return false;
    }

	// 소켓을 넌블러킹으로 바꾸고
	u_long arg = 1 ;
	ioctlsocket(FSocket, FIONBIO, &arg) ;

	/// nagle 알고리즘 끄기
	int opt = 1 ;
	setsockopt(FSocket, IPPROTO_TCP, TCP_NODELAY, (const char*)&opt, sizeof(int));
    FTmrConn = Now();
    return true;
}
//---------------------------------------------------------------------------
int TClientSession::GetFreeSpaceSize()
{
	return (SESSION_RECV_BUFFER - FRecvIdx);
}
//---------------------------------------------------------------------------
char* TClientSession::GetBuffer()
{
    return (char*)FRecvBuff+FRecvIdx;
}
//---------------------------------------------------------------------------
void TClientSession::RecvBuffCommit(int ARecvLen)
{
	FRecvIdx += ARecvLen;
}
//---------------------------------------------------------------------------
 void TClientSession::Disconnect()
{
    OnDisconnected();

    if (!IsConnected) return;

    // 즉각 해제
    LINGER linger;
    linger.l_onoff  = 1;
    linger.l_linger = 0;

    // no TCP TIME_WAIT
    if (SOCKET_ERROR == setsockopt(FSocket, SOL_SOCKET, SO_LINGER, (char*)&linger, sizeof(LINGER)))
    {
    //  	printf_s("[DEBUG] setsockopt linger option error: %d\n", GetLastError());
    //		return;
    }

    closesocket(FSocket);

    //FSocket    = INVALID_SOCKET;   ==> 여기서 초기화 하면 ClientSessionManager에서 삭제할때 오류발생 소켓을 키로 사용함
    FConnected = false ;
    FState     = eSS_OnDisconnect;

    FCDSCtlr   = NULL;
    FCloseTick = 0;
    FState     = eSS_none;
    FSocket    = INVALID_SOCKET;
}
//---------------------------------------------------------------------------
void TClientSession::OnWriteComplete(int ASendLen, int AReqLen)
{
	/// 보내기 완료한 데이터는 버퍼에서 제거
    if (FSendIdx >= ASendLen) FSendIdx -= ASendLen;
}
//---------------------------------------------------------------------------
bool TClientSession::SendRequest(char *AData, int ADataLen)
{
	if (!IsConnected)
    {
        MERROR("+SVR Session  SendRequest: %s[%d], already closed", FIpAddress.c_str(), FSocket);
        Disconnect();
        return false;
    }

    if (ADataLen > (SESSION_SEND_BUFFER - FSendIdx))
    {
		/// 버퍼 용량 부족인 경우는 끊어버림
        MERROR("+SVR Session  SendRequest: %s[%d], Send Buffer Overflow, will be closed.", FIpAddress.c_str(), FSocket);
		Disconnect();
		return false;
    }

    FPacketLen = ADataLen;
    FPacketSent= 0;
    FPacketPtr = FSendBuff+FSendIdx;

    memcpy(FSendBuff+FSendIdx, AData, ADataLen);
    FSendIdx += ADataLen;
	return true;
}
//---------------------------------------------------------------------------
bool TClientSession::SendFlush()
{
	if (!IsConnected)
    {
        MERROR("+SVR Session  SendFlush: %s[%d], already closed", FIpAddress.c_str(), FSocket);
        Disconnect();
        return false;
    }

	/// 보낼 데이터가 없으면 그냥 리턴
	if (FSendIdx == 0)
		return true;

	DWORD sendbytes = 0;
	DWORD flags = 0;

	int   nLeft = FSendIdx;
	char *buf   = (char*)FSendBuff;//+FSendIdx;

    int nSendedBytes = send(FSocket, buf, nLeft, 0);
    if (nSendedBytes <= 0)
    {
        MERROR("+SVR Session  SendFlush: %s[%d]: send error: %d, %d", FIpAddress.c_str(), FSocket, nSendedBytes, WSAGetLastError());
        Disconnect();
        return false;
    }
    MDEBUG("+SVR Sessionclient send: %s[%d], %4d bytes send", FIpAddress.c_str(), FSocket, nSendedBytes);

	OnWriteComplete(nSendedBytes, nLeft);

    FPacketSent += nSendedBytes;
    if (FPacketSent >= FPacketLen)
    {
        OnWrite(FPacketPtr, FPacketLen);
        FPacketSent -= FPacketLen;
        FPacketPtr   = FSendBuff + FPacketSent;
    }
	return true;
}
//---------------------------------------------------------------------------

char* TClientSession::GetPacket()
{
    return FRecvBuff;
}
//---------------------------------------------------------------------------
bool TClientSession::ResetPacket(int APktLen)
{
    if (APktLen > FRecvIdx) return false;

    FRecvIdx -= APktLen;
    if (FRecvIdx > 0)
    {
        memmove(FRecvBuff, FRecvBuff+APktLen, FRecvIdx);
        //FRecvIdx = 0;
    }
    return true;
}
//---------------------------------------------------------------------------

int TClientSession::LogData(char *ASndRcv, BYTE *AData, int ALen)
{
    // Hex Data 파일에 저장하고
    // FrmCommLog 창에 뿌리는거
    TCDSCtlr* pCDSCtlr = FCDSLogCtlr;
    if (!pCDSCtlr)
    {
        ITSLog->LogData(ASndRcv, AData, ALen);
        return -1;
    }

    if (!pCDSCtlr) return -1;
    if (!pCDSCtlr->FSLog->FLogCfg.Data) return -1;

    try
    {
        if (pCDSCtlr->FSLog->FLogCfg.Data) pCDSCtlr->FSLog->LogData(ASndRcv, AData, ALen);

        if (!pCDSCtlr->FDispLog) return -1;

        IPC_LOG_MESSAGE *pLog = g_logBuff.GetBuff();
        if (pLog)
        {
            memset(pLog->Msg, 0x00, sizeof(pLog->Msg));

            pLog->From   = from_srvTcp;
            pLog->Target = log_com;
            pLog->Kind   = eLOG_DATA;
            pLog->Flag   = ASndRcv[0] == 'S' ? 1 : 2;
            pLog->Write  = false;
            pLog->Tm     = Now();
            pLog->Type   = 'S';
            pLog->Obj    = pCDSCtlr->FSLog;
            pLog->Ctlr   = pCDSCtlr;
            pLog->Len    = ALen > MAX_LOG_BUFFER ? MAX_LOG_BUFFER-1 : ALen;
            memcpy(pLog->Msg, (char*)AData, pLog->Len);

            g_logQ.PushTimeout((DWORD)pLog, 2000);
        }
    }
    catch(Exception &e)
    {
    }
    return 0;
}
//---------------------------------------------------------------------------

int TClientSession::LogWrite(int ALogKind, char *AFmt, ...)
{
    va_list ap;
    int     cnt = 0;
    char    szFmtData[MAX_LOG_BUFFER];

    // FrmCommLog 창에 뿌리는거
    TCDSCtlr* pCDSCtlr = FCDSLogCtlr;

    if (!pCDSCtlr) return -1;
//    if (!pCDSCtlr->FDispLog) return -1;

    bool bLog = false;
    switch(ALogKind)
    {
    case eLOG_INFO   : bLog = pCDSCtlr->FSLog->FLogCfg.Info;    break;
    case eLOG_DATA   : bLog = pCDSCtlr->FSLog->FLogCfg.Data;    break;
    case eLOG_ERROR  : bLog = pCDSCtlr->FSLog->FLogCfg.Error;   break;
    case eLOG_WARNING: bLog = pCDSCtlr->FSLog->FLogCfg.Warning; break;
    case eLOG_DEBUG  : bLog = pCDSCtlr->FSLog->FLogCfg.Debug;   break;
    case eLOG_DETAIL : bLog = pCDSCtlr->FSLog->FLogCfg.Detail;  break;
    }
    if (!bLog) return -1;

    try
    {
        IPC_LOG_MESSAGE *pLog = g_logBuff.GetBuff();
        if (pLog)
        {
            va_start(ap, AFmt);
            cnt = vsprintf(szFmtData, AFmt, ap);
            va_end(ap);

            memset(pLog->Msg,  0x00, sizeof(pLog->Msg));

            pLog->From   = from_srvTcp;
            pLog->Target = log_com;
            pLog->Kind   = ALogKind;
            pLog->Flag   = 0;
            pLog->Write  = true;
            pLog->Tm     = Now();
            pLog->Type   = 'S';
            pLog->Obj    = pCDSCtlr->FSLog;
            pLog->Ctlr   = pCDSCtlr;
            sprintf(pLog->Msg, "+SVR %s, %s", pCDSCtlr->CTLR_NMBR.c_str(), szFmtData);
            pLog->Len    = strlen(pLog->Msg);

            g_logQ.PushTimeout((DWORD)pLog, 2000);
        }
    }
    catch(Exception &e)
    {
    }
    return cnt;
}
//---------------------------------------------------------------------------

int TClientSession::SysLogWrite(int ALogKind, char *AFmt, ...)
{
    va_list ap;
    int     cnt = 0;
    char    szFmtData[MAX_LOG_BUFFER];

    // FrmSysLog 창에 뿌리는거
    TCDSCtlr* pCDSCtlr = FCDSLogCtlr;

    if (!pCDSCtlr) return -1;
    if (!pCDSCtlr->FSLog) return -1;

    bool bLog = false;
    switch(ALogKind)
    {
    case eLOG_INFO   : bLog = pCDSCtlr->FSLog->FLogCfg.Info;    break;
    case eLOG_DATA   : bLog = pCDSCtlr->FSLog->FLogCfg.Data;    break;
    case eLOG_ERROR  : bLog = pCDSCtlr->FSLog->FLogCfg.Error;   break;
    case eLOG_WARNING: bLog = pCDSCtlr->FSLog->FLogCfg.Warning; break;
    case eLOG_DEBUG  : bLog = pCDSCtlr->FSLog->FLogCfg.Debug;   break;
    case eLOG_DETAIL : bLog = pCDSCtlr->FSLog->FLogCfg.Detail;  break;
    }
    if (!bLog) return -1;

    try
    {
        IPC_LOG_MESSAGE *pLog = g_logBuff.GetBuff();
        if (pLog)
        {
            va_start(ap, AFmt);
            cnt = vsprintf(szFmtData, AFmt, ap);
            va_end(ap);

            memset(pLog->Msg,  0x00, sizeof(pLog->Msg));

            pLog->From   = from_srvTcp;
            pLog->Target = log_sys;
            pLog->Kind   = ALogKind;
            pLog->Flag   = 0;
            pLog->Write  = true;
            pLog->Tm     = Now();
            pLog->Type   = 'S';
            pLog->Obj    = pCDSCtlr->FSLog;
            pLog->Ctlr   = pCDSCtlr;
            sprintf(pLog->Msg, "+SVR %s, %s", pCDSCtlr->CTLR_NMBR.c_str(), szFmtData);
            pLog->Len    = strlen(szFmtData);

            g_logQ.PushTimeout((DWORD)pLog, 2000);
        }
    }
    catch(Exception &e)
    {
    }
    return cnt;
}
//---------------------------------------------------------------------------

