#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>

#include "UdpListener.h"

#define DEFAULT_BUFFER_SIZE   9216 		

#define MaxSizeOfTID          52
#define MaxSizeOfIssuer       128
#define MaxSizeOfSID          128
#define MaxSizeOfRCID         32

        
int CUdpListener::SendMsg(char * szSndBuffer,int size, struct sockaddr_in * cliaddr, socklen_t nAddrSize)
{
    int SndBlockSize = 8196;
    char szLineBuffer[SndBlockSize];
    int nLeft = size;
    char * pTmp = szSndBuffer;
    int nWrite = 0;
    do
    {
        int offset = 0;
        ::memset(szLineBuffer, 0, SndBlockSize);
        if( nLeft < SndBlockSize )
            SndBlockSize = nLeft;
        while( offset < SndBlockSize )
            szLineBuffer[offset++] = *pTmp++;
        szLineBuffer[offset] = '\0';

        //DEBUG_LOG("LINE : [%s]", szLineBuffer);
        nWrite = ::sendto(m_nSocket, szLineBuffer, offset + 1, 0, (struct sockaddr *)cliaddr, nAddrSize);
        if(nWrite < 0)
        { // error
            LOG(LERR, "sendto socket(%d) error (%s)", m_nSocket, strerror(errno));
            break;
        }
        else if(nWrite == 0)
        { // socket closed
            break;
        }
        else
        {
            LOG(LDBG, "SEND MESSAGE %d, %d, %s", nWrite, offset+1, szLineBuffer);
        }
        nLeft -= nWrite;
    } while ( nLeft > 0 );
    return 0;
}


/// @brief 클라이언트로 부터 받아온 스트링 프로토콜을 정수 프로토콜로 변환한다.
/// @param [in] szProtocol 클라이언트로부터 전송받은 스트링 프로토콜.
/// @return 성공: 양의 정수의 프로토콜 값. 실패: -1
int  CUdpListener::ParsingPacket(const char * szProtocol)
{
	int nRet = -1;

	if( szProtocol == NULL )
	{
		LOG(LERR, "Invalid Parameter");
		return -1;
	}
	
	// 구버전 프로토콜을 유지한다.
	if(strcmp(szProtocol, "I'M YOUR FATHER. BASTARD !") == 0) 
	{
		LOG(LDBG, "-------- (OLD)CMD_EVENT_INFO_ONE Called!-----------");
		nRet = CMD_EVENT_INFO_ONE;
	}
	else if(strcmp(szProtocol, "I'M YOUR GOD. BASTARD !") == 0) 
	{
		LOG(LDBG, "-------- (OLD)CMD_EVENT_INFO_ALL Called! ---------");
		nRet = CMD_EVENT_INFO_ALL;
	}
	else if(strcmp(szProtocol, "EVENT:INFO:ONE") == 0) 
	{
		LOG(LDBG, "-------- CMD_EVENT_INFO_ONE Called!-----------");
		nRet = CMD_EVENT_INFO_ONE;
	}
	else if(strcmp(szProtocol, "EVENT:INFO:ALL") == 0) 
	{
		LOG(LDBG, "-------- CMD_EVENT_INFO_ALL Called! ---------");
		nRet = CMD_EVENT_INFO_ALL;
	}
	// 프로토콜을 추가하려면 여기에 아래의 형태로 추가한다.
	// else if(strcmp(szProtocol, "String...........") == 0) 
	// {
	//		nRet = CMD_XXXXXXXXX;
	// }
	else // Invalid Protocol
	{
		nRet = CMD_UDP_ERROR;
	}

	return nRet;
}


CUdpListener::CUdpListener(CEventDataManager* pEventDataManager)
:  m_pEventDataManager( pEventDataManager )
{
	m_nSocket = -1;
	LOG(LDBG, "Constructor has been called.");
}

CUdpListener::~CUdpListener()
{
	if ( m_nSocket != -1)
	{
		::close(m_nSocket);
		m_nSocket = -1;
	}
	pthread_cancel(m_threadHandle);
}

bool CUdpListener::Init(int nPort)
{
	LOG(LDBG, "Initializer has been called.");
	int fRet = 0;
	struct sockaddr_in SocAddr;
	int nVal=1;

	if(m_pEventDataManager == NULL )
	{
		LOG(LERR, "Invalid Object.");
		return false;
	}
	if( nPort <= 0 )
	{
		LOG(LERR, "Invalid Parameter. Port=%d", nPort);
		return false;
	}
	SocAddr.sin_family = AF_INET;
	SocAddr.sin_port = htons(nPort);
	SocAddr.sin_addr.s_addr = htonl(INADDR_ANY);

	// UDP 소켓 생성.
	m_nSocket = ::socket(AF_INET, SOCK_DGRAM, 0);
	if (m_nSocket < 0)
	{
		LOG(LERR, "Socket Create: errno:%d errmsg:%s", errno, strerror(errno));	
		return false;
	}

	if (setsockopt(m_nSocket, SOL_SOCKET, SO_REUSEADDR, (char *)&nVal, sizeof(int)) < 0)
	{
		LOG(LERR, "setsockopt(SO_REUSEADDR) failed.");	
		return false;
	}

	// UDP 소켓 바인드.
	fRet = ::bind(m_nSocket, (struct sockaddr *)&SocAddr, sizeof(SocAddr));
	if (fRet != 0)
	{
		LOG(LERR, "Socket Bind: errno:%d errmsg:%s", errno, strerror(errno));	
		return false;
	}

	LOG(LDBG, "Initializer has been succeed.");
	return true;
}


///@brief 이 함수에서 UDP 통신을 receive 한다.
void CUdpListener::Execute()
{
	char szRcvBuffer[DEFAULT_BUFFER_SIZE];
	char szSndBuffer[DEFAULT_BUFFER_SIZE];
	int nRead = 0;
	struct sockaddr_in cliaddr;
	int addr_len = sizeof(cliaddr);
	int bMode = 0;
    int nRet = -1;
    bool bRet = false;
    string szTmp;

    ::memset(szRcvBuffer, 0, DEFAULT_BUFFER_SIZE);
    ::memset(szSndBuffer, 0, DEFAULT_BUFFER_SIZE);
    //DEBUG_LOG("DEBUG: %d , %lu %lu, %lu", m_nSocket, szRcvBuffer, &cliaddr, &addr_len);
    nRead = ::recvfrom(m_nSocket, (char *)&szRcvBuffer, (unsigned int)DEFAULT_BUFFER_SIZE, 0, (struct sockaddr *)&cliaddr, (socklen_t *)&addr_len);
    if ( nRead <= 0 )
    {
        LOG(LERR, "recvfrom() error. %s.",strerror(errno));
        return;
    }
    LOG(LDBG, "Request Buffer = %s", szRcvBuffer);
    LOG(LDBG, "From = %s", inet_ntoa(cliaddr.sin_addr));


    bMode = ParsingPacket(szRcvBuffer);

    switch (bMode)
    {
        case CMD_EVENT_INFO_ONE:
			{
				CEventData resData;

				nRet = m_pEventDataManager->GetLatestData(resData);
				if ( nRet ==  -1 )
				{
					LOG(LERR, "GeLatestData() failed.");
					::snprintf(szSndBuffer, DEFAULT_BUFFER_SIZE, "1:%ld:Initializing Now.", ::time(NULL));
				} else
				{
					if( nRet == 0 )
						::snprintf(szSndBuffer, DEFAULT_BUFFER_SIZE, "%d:%ld:%s", nRet,::time(NULL), resData.m_szEventData.c_str());
					else
						::snprintf(szSndBuffer, DEFAULT_BUFFER_SIZE, "%d:%ld:%s", nRet,::time(NULL), resData.m_szEventData.c_str());
				}
				// 클라이언트 메시지 송신
				SendMsg((char*)szSndBuffer, strlen(szSndBuffer),&cliaddr, (socklen_t) sizeof(cliaddr));
				LOG(LDBG, "SEND MESSAGE %s.", szSndBuffer);
			}
            break;
        case CMD_EVENT_INFO_ALL:
			{
				list<CEventData> listRet;
				listRet.clear();

				bRet = m_pEventDataManager->GetAllData(listRet);
				if( nRet == false || listRet.empty() )
				{
					LOG(LERR, "GetAllData() failed.");
					::snprintf(szSndBuffer, DEFAULT_BUFFER_SIZE, "%d:%ld:No EVENT IS FOUND.", 1,(long)::time(NULL));
				} else
				{
					// 2013-10-17
					// 기존에는 모든 메세지를 보냈으나, sms메세지를 만들어주는 web page에서 처리가안되는것을 확인했다.
					// 이에 따라 최근 100개 메세지를 전송하는 것으로 처리한다.
					//for (list<CEventData>::iterator it = listRet.begin(); it != listRet.end(); it++)
					int nCount = 0;
					
					for (list<CEventData>::reverse_iterator it = listRet.rbegin(); it != listRet.rend(); it++)
					{
						// 2016.01.22 dadamin
						// 96 => 1024 : buffer overflow 발생하여 메세지 전송 실패 발생하여 변경함
						char timeBuf[1024] = {0};
						tm* ptm = localtime(&it->m_evtTime);
						::sprintf(timeBuf, "[%04d-%02d-%02d %02d:%02d:%02d] %s\n",
								ptm->tm_year + 1900, ptm->tm_mon+1, ptm->tm_mday, ptm->tm_hour, ptm->tm_min, ptm->tm_sec,
								it->m_szEventData.c_str());
						szTmp += timeBuf;
						nCount++;
					
						if( nCount >= 100 )
						{
							break;
						}

						// upd send size limit
						if(szTmp.length() > 1100 ) {
							break;
						}
					}

					// upd send size limit
					::snprintf(szSndBuffer, 1400, "%d:%ld:%s", 1,(long)::time(NULL) , szTmp.c_str());
				}
				SendMsg((char*)szSndBuffer, strlen(szSndBuffer),&cliaddr, (socklen_t) sizeof(cliaddr));
				LOG(LDBG, "SEND MESSAGE %s.", szSndBuffer);
			}
            break;
        default:
            LOG(LDBG, "----------------- INVALID COMMAND REQUEST!!! ----------------------------------");
            LOG(LERR, "Invalid Protocol.");
            ::memset(szSndBuffer, 0, DEFAULT_BUFFER_SIZE);
            ::snprintf(szSndBuffer, DEFAULT_BUFFER_SIZE, "1:%ld:Invalid Arguments!", (long)::time(NULL));

            LOG(LDBG, "SENDING Buffer = [%s]", szSndBuffer);

            //::sendto(m_nSocket, szSndBuffer, strlen(szSndBuffer), 0, (struct sockaddr *)&cliaddr, sizeof(cliaddr));
            SendMsg((char*)szSndBuffer, strlen(szSndBuffer),&cliaddr, (socklen_t) sizeof(cliaddr));
            // TODO: error check
            return;
            break;
    }//~switch (bMode)
}


void* CUdpListener::EntryPoint(void* arg)
{
	CUdpListener* pObject = reinterpret_cast<CUdpListener *>(arg);    
	if( pObject == NULL )
	{
		// static Function이기 때문에 로깅할 수 없다...
		//ERROR_LOG("Critical Memory Error, Invalid Object.");
		//exit(EXIT_FAILURE);
	}
	pthread_detach( pthread_self() );

	while(1)
	{
		pthread_testcancel();
		pObject->Execute();
		pthread_testcancel();
	}

	// pthread_exit();
	return (void *)NULL;
}

bool CUdpListener::Start()
{
	int nRet = ::pthread_create(&m_threadHandle, 0, CUdpListener::EntryPoint, this);

	if( nRet != 0 )
	{
		LOG(LERR, "Thread create failed: errno:%d errmsg:%s", errno, strerror(errno));
		return false;
	}
	LOG(LDBG, "Thread create succeed.");
	sleep(0);
	return true;
}
