/***************************************************************************
                   Client Class (Client.cpp)
                -----------------------------------------

	begin      : 2013/02/19
	copyright  : (C) 2013 Solbox Inc.
	author     : Development 1 Team
					- 2013/02/19 - 1st dadamin
	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 "rc_mngd.h"
#include "Client.h"
#include "Service.h"
#include "Parser.h"
#include "JobControl.h"
#include "Logger.h"

#include <unistd.h>



CClient::CClient()
	: m_sfd(-1), m_ssl(NULL), m_parser(NULL)
{
	srand(time(NULL));
}

CClient::CClient(int clientsd, SSL *  ssl)
	: m_sfd(clientsd), m_ssl(ssl), m_parser(NULL)
{
	srand(time(NULL));
}

CClient::~CClient()
{
	if (m_sfd > 0)
		::close(m_sfd);
	if(m_ssl)
		SSL_free(m_ssl);
	if(m_parser)
		delete m_parser;
}

void *CClient::_entry_func(void *arg) 
{
	ostringstream msg;
	CClient* pObject = reinterpret_cast<CClient *>(arg);
	pthread_detach(pthread_self());

	if(pObject->m_ssl)
	{
		if( pObject->acceptSSL() != 0) 
		{
			if(pObject)
			{
				delete pObject;
			}
			
			return 0;
		}
	}

	pObject->SetSocket();

	int nerr = JOB_SUCCESS;
	
	while ( 1 ) 
	{
		// read header
		nerr = pObject->ReadHeader();
		if( nerr  != 0 ) 
		{
			nerr = JOB_ERR_READ_HEADER;
			break;
		}

		// read body
		nerr = pObject->ReadBody();
		if( nerr  != 0 ) 
		{
			nerr = JOB_ERR_READ_BODY;
			break;
		}
				
		while(1)
		{
			// convert data
			nerr = pObject->Client2JobData();
			if( nerr  != 0 ) 
				break;

			// run job
			nerr  = pObject->AnalyzeJob();
			break;
		}

		// send 
		pObject->SendtoClient(nerr);
		break;
	}

	if(pObject)
	{
		delete pObject;
	}

	return 0;
}

int CClient::acceptSSL()
{
	ostringstream msg;

	// SSL_accept 블록함수 이므로 해당 로직은 client 쓰레드에서 호출되도록 함.
	
	// connect the SSL object with a file descriptor
	// 연결된 소켓과 SSL과의 연결
	SSL_set_fd (m_ssl, m_sfd);
	// 가장 중요한 함수, 클라이언트와의 초기 협상과정, 즉 핸드쉐이크 과정을 수행
	int err = SSL_accept (m_ssl);
	if( err <= 0 )
	{
		msg << "SSL Accept Fail. ["<< ERR_error_string(ERR_get_error(), NULL) <<"]";
		LOGACONSOLE(LERR, msg);
		return -1;
	}

	return 0;
}

int CClient::Run(CServiceInfo & info)
{
	pthread_t workerthread = 0;

	// Set Parser
	CParserFactory parserfacotry;
	m_parser = parserfacotry.CreateParser(info);

	if(m_parser == NULL)
	{
		ostringstream msg;		
		msg << "Parser Allocation failed.[" << errno << "]";
		LOGACONSOLE(LERR, msg);
		return -1;
	}

	// start thread
	int ret = pthread_create(&workerthread, 0,  _entry_func, (void*)this);
	if (ret != 0) 
	{
		ostringstream msg;		
		msg << "Client Thread create failed.[" << errno << "]";
		LOGACONSOLE(LERR, msg);
		return -1;
	}

	sleep(0);

	return ret;
}

int CClient::SetSocket()
{
	m_parser->SetSocket(m_sfd, m_ssl);
	return 0;
}

int CClient::ReadHeader()
{	
	int r = 0;
	r = m_parser->ReadHeader();
	return r;
}

int CClient::ReadBody()
{
	int r = 0;
	r = m_parser->ReadBody();
	return r;
}

int CClient::Client2JobData()
{
	int r = 0;
	r = m_parser->ConvertJobData((void *)m_job.GetJobData());
	return r;
}

int CClient::AnalyzeJob()
{
	int r = 0;
	r = m_job.run();
	return r;
}

void CClient::SendtoClient(int nCode)
{
	int32_t bodylen = 0;

	if(m_parser == NULL)
		return;

	bodylen = m_parser->ConvertClient((void *)m_job.GetJobData(), nCode);
	if(bodylen < 0)
	{		
		bodylen = 0;
		nCode = JOB_ERR_CONVERT;
		LOG(LERR, "Body data conversion error.");
	}

	m_parser->WriteHeader(bodylen);
	if(bodylen > 0)
	{
		m_parser->WriteBoad(bodylen);
	}
}
