/***************************************************************************
                   Interface Server ( InterfaceServer.cpp )
                -----------------------------------------

	begin      : 2013/09/16
	copyright  : (C) 2005 Solbox Inc.
	author     : Development 1 Team
					- 2013/09/16
	email      : dev1@solbox.com
	version    : 3.2.0

	CopyRight(C) 2005 Solbox Inc. All Rights reserved.
	Redistribution and use in source and binary forms, with or with out
	modification, are not permitted in outside of Solbox Inc.
***************************************************************************/
#include "cc_tsd.h"
#include "InterfaceServer.h"
#include "Logger.h"
#include "InterfaceProcessor.h"
#define DEFAULT_ACCEPT_WAIT_COUNT	30

#define ACCEPT_TIMEOUT				60

CInterfaceServer::CInterfaceServer()
	: m_ipv6(false),  m_listenfd(-1), m_threadid(0),  m_done(false), m_port(0)
{
}

CInterfaceServer::~CInterfaceServer( )
{
}

int CInterfaceServer::bind( int nPort )
{
	ostringstream msg;
	m_port = nPort;

#ifdef AF_INET6
	m_listenfd = ::socket( AF_INET6, SOCK_STREAM, 0 );
	if(m_listenfd >= 0)
	{
		msg << "Enable IPv6 Socket.";
		_LOG(LINF, "%s", msg.str().c_str());
		m_ipv6 = true;
	}
#endif // AF_INET6
	if(m_ipv6 == false)
		m_listenfd = ::socket( AF_INET, SOCK_STREAM, 0 );

	if( m_listenfd == -1 )
	{
		msg << "Listen socket create failed.[" << errno << "][" 
			<< strerror(errno)  << "]";
		LOG( LERR, "%s", msg.str().c_str());
		cerr << msg.str() << endl;
		return -1;
	}

	int result = 0;

	// Socket Port Reuse Option Set
	int opt = 1;
	result = ::setsockopt( m_listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt) );
	if( result != 0 )
	{
		msg << "Listen socket option[SO_REUSEADDR] set failed. [" << errno 
			<< "][" << strerror(errno)  << "]";
		LOG( LERR, "%s", msg.str().c_str());
		cerr << msg.str() << endl;
		return -1;
	}

	// Keep Alive Option set
	opt = 1;
	result = ::setsockopt( m_listenfd, SOL_SOCKET, SO_KEEPALIVE, &opt, sizeof(opt) );
	if( result != 0 )
	{
		msg << "Listen socket option[SO_KEEPALIVE] set failed. [" << errno 
			<< "][" << strerror(errno)  << "]";
		LOG( LERR, "%s", msg.str().c_str());
		cerr << msg.str() << endl;
		return -1;
	}
#ifdef AF_INET6
	if(m_ipv6)
	{
		struct sockaddr_in6 listenSockAddrv6;
		socklen_t listenSockLen = 0;
		memset(&listenSockAddrv6, 0x00, sizeof(listenSockAddrv6));

		listenSockAddrv6.sin6_family = AF_INET;
		listenSockAddrv6.sin6_flowinfo = 0;
		listenSockAddrv6.sin6_port = htons( m_port );
		listenSockAddrv6.sin6_addr = in6addr_any;

		listenSockLen = sizeof(listenSockAddrv6);

		// Socket Bind
		result = ::bind( m_listenfd, (struct sockaddr *)&listenSockAddrv6, listenSockLen);
		if( result != 0 )
		{
			msg << "Listen socket bind failed. [" << errno << "][" << strerror(errno)  
				<< "][Port : " << m_port << "]";
			LOG(LERR, "%s", msg.str().c_str());
			return -1;
		}
	}
	else 
#endif  //AF_INET6
	{		
		struct sockaddr_in listenSockAddr;
		socklen_t listenSockLen = 0;
		bzero(&listenSockAddr,sizeof(listenSockAddr));

		listenSockAddr.sin_family = AF_INET;
		listenSockAddr.sin_port = htons( m_port);
		listenSockAddr.sin_addr.s_addr = htonl( INADDR_ANY );

		listenSockLen = sizeof(listenSockAddr);

		// Socket Bind
		result = ::bind( m_listenfd, (struct sockaddr *)&listenSockAddr, listenSockLen);
		if( result != 0 )
		{
			msg << "Listen socket bind failed. [" << errno << "][" << strerror(errno)  
				<< "][Port : " << m_port<< "]";
			LOG(LERR, "%s", msg.str().c_str());
			return -1;
		}
	}

	// Socket Listen
	result = ::listen( m_listenfd, DEFAULT_ACCEPT_WAIT_COUNT );
	if( result != 0 )
	{
		msg << "Listen socket listen failed. [" << errno << "][" << strerror(errno)  << "]";
		LOG( LERR, "%s", msg.str().c_str());
		cerr << msg.str() << endl;
		return -1;
	}

	return 0;
}


int CInterfaceServer::Accept()
{
	ostringstream msg;


	// 접속 요청을 변수 생성 및 초기화.
	int nClientfd;
	struct sockaddr_in clientSockAddr;
	socklen_t clientSockLen = sizeof(clientSockAddr);

	while(!m_done)
	{
		msg << "Client Waiting...IPv4 [Port:" << m_port << "]";
		LOG(LDBG, "%s", msg.str().c_str());
		nClientfd = ::accept( m_listenfd, (struct sockaddr *) &clientSockAddr, &clientSockLen );

		if( nClientfd == -1 )
		{
			if( errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK )
			{
				msg << "Service : client accept Warning. [" << errno << "][" 
					<< strerror(errno) << "][Port : " << m_port << "]";

				LOG(LDBG, "%s", msg.str().c_str());
				continue;
			}

			// 오류발생시 해당 내역 로깅처리.
			msg << "Service : client accept failed. [" << errno << "][" 
				<< strerror(errno) << "][Port : " << m_port << "]";

			LOG(LERR, "%s", msg.str().c_str());
		}
		else
		{
			// 정상적인 Client 인 경우 
#ifdef _DEBUG
			cout << "Connection from " <<  nClientfd << "," <<
				clientSockAddr.sin_addr.s_addr << ", Port " << clientSockAddr.sin_port<< endl;
#endif // _DEBUG
			string szClientIP = inet_ntoa(clientSockAddr.sin_addr);
			_LOG( LDBG, "Client IP : %s", szClientIP.c_str() );
			RunClient(nClientfd, szClientIP);
		}			
	}

	// exit
	if(m_listenfd >= 0)
	{
		::close(m_listenfd);
		m_listenfd = -1;
	}

	msg << "Service end.[Port : " << m_port << ",Type: " ;
	LOG(LDBG, "%s", msg.str().c_str());
	return 0;
}

int	CInterfaceServer::Acceptv6()
{
	ostringstream msg;
#ifdef AF_INET6

	// 접속 요청을 변수 생성 및 초기화.
	int nClientfd;
	struct sockaddr_in6 clientSockAddr;
	socklen_t clientSockLen = sizeof(clientSockAddr);

	while(!m_done)
	{
		LOG(LDBG, "Client(v6) Waiting... IPv6[Port:%d]", m_port);
		nClientfd = ::accept( m_listenfd, (struct sockaddr *) &clientSockAddr, &clientSockLen );

		if( nClientfd == -1 )
		{
			if( errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK )
			{
				msg << "Service(v6) : client accept Warning. [" << errno << "][" 
					<< strerror(errno) << "][Port : " << m_port << "]";

				LOG(LDBG, "%s", msg.str().c_str());
				continue;
			}

			// 오류발생시 해당 내역 로깅처리.
			msg << "Service(v6) : client accept failed. [" << errno << "][" 
				<< strerror(errno) << "][Port : " << m_port << "]";

			LOG(LERR, "%s", msg.str().c_str());
		}
		else
		{
			// 정상적인 Client 인 경우 
#ifdef _DEBUG
			cout << "Connection(v6) from " <<  nClientfd;
#endif // _DEBUG
			char buffer[5*8];
			memset(buffer, 0x00, 40);
    			/*inet_ntop(AF_INET6,
              			&clientSockAddr.sin6_addr.s6_addr[0],
              			buffer,
              			sizeof(clientSockAddr.sin6_addr.s6_addr));*/
			inet_ntop(AF_INET6,
                                &clientSockAddr.sin6_addr,
                                buffer,
				INET6_ADDRSTRLEN);
			string szClientIP = buffer;
			_LOG( LDBG, "Client IP : %s", szClientIP.c_str());

			RunClient(nClientfd, szClientIP);
		}			
	}

	// exit
	if(m_listenfd >= 0)
	{
		::close(m_listenfd);
		m_listenfd = -1;
	}

	msg << "Service(v6) end.[Port : " << m_port ;
	LOG(LDBG, "%s", msg.str().c_str());
#endif  //AF_INET6
	return 0;
}

void *CInterfaceServer::_entry_func(void *arg) 
{
	void *r = ((CInterfaceServer*)arg)->entry();
	return r;
}

void CInterfaceServer::Init(CQueue<CLoginInfo> *pQueueLoginInfo, CServiceInfoUpdater *pobjServiceInfoUpdater)
{
	m_pQueueLoginInfo = pQueueLoginInfo;
	m_pobjServiceInfoUpdater = pobjServiceInfoUpdater;
}

int CInterfaceServer::start()
{
	// start thread
	int ret = pthread_create(&m_threadid, 0,  _entry_func, (void*)this);
	if (ret != 0) 
	{
		ostringstream msg;		
		msg << "Thread create failed.[" << errno << "]";
		LOG( LERR, "%s", msg.str().c_str());
		cerr << msg.str() << endl;

		return -1;
	}

	return 0;
}
void *CInterfaceServer::entry()
{
	ostringstream msg;
	msg << "Service start.[Port : " << m_port << "]";
	LOG(LDBG, "%s", msg.str().c_str());

	if(m_ipv6)
		Acceptv6();
	else 
		Accept();

	LOG(LDBG, "xxxx here????");

	return 0;
}

void CInterfaceServer::stop()
{
	m_done = true;

	if (m_listenfd >= 0) 
	{
		::shutdown(m_listenfd, SHUT_RDWR);
	}

	if( m_threadid > 0 )
	{
		int status = pthread_join(m_threadid, NULL);
		m_threadid = 0;
	}

	if (m_listenfd >= 0) 
	{
		::close(m_listenfd);
		m_listenfd = -1;
	}
	m_done = false;
}

int CInterfaceServer::RunClient(int nClientfd, string szClientIP)
{
	ostringstream msg;
	LOG(LDBG, "RunClient");

	
	CInterfaceProcessor* objServer = new CInterfaceProcessor(nClientfd, szClientIP, m_pQueueLoginInfo, m_pobjServiceInfoUpdater);

	if( objServer->Run() != 0 )
		delete objServer;

	return 0;
}

