#include "ClientThread.h"

#include <errno.h>
#include <string.h>

#include "Logger.h"
#include "RcSyncdRequestData.h"
#include "ModeMaster.h"
#include "ModeSlave.h"
#include "ModeSync.h"


// 생성자
CClientThread::CClientThread( CProcessStatus * pStatus, int clientSocket, char * pszClientAddress )
	: m_pProcessStatus( pStatus )
	, m_socketClientDesc( clientSocket )
	, m_client( clientSocket )
	, m_strClientIp( pszClientAddress )	  
{
	// 멤버 변수 초기화
	m_threadHandle = 0;

}

// 소멸자
CClientThread::	~CClientThread()
{
	// Thread 동작 정지 처리
	// - 만약 Thread 가 이미 종료된 경우 m_threadHandle 이 다른 Thread Handle 일 수 있으므로  업무 Flow 수정시 주의할 것
	if( m_threadHandle != 0 )
		pthread_cancel( m_threadHandle );

	// client 와 연결된 socket 종료 처리
	m_client.Close();

}

// thread 를 생성하여 처리 업무 flow start
bool CClientThread::Start()
{
	int nRet = ::pthread_create( &m_threadHandle, NULL, CClientThread::threadFunc, this );
	if( nRet != 0 )
	{
		// Thread 생성 실패시
		int errorNum = errno;
		LOG( LERR, "Thread create failed.[%d][%s]", errorNum, strerror( errorNum ) );

		return false;
	}
		
	return true;

}

// 스레드 함수
void* CClientThread::threadFunc( void* arg )
{
	CClientThread* pObject = reinterpret_cast<CClientThread *>( arg );
	pthread_detach( pthread_self() );

	// Client Thread Count 1 증가 처리
	pObject->m_pProcessStatus->PlusClientCount();

	// 실제 작업 수행.
	pObject->Execute();

	// Client Thread Count 1 감소 처리
	pObject->m_pProcessStatus->MinusClientCount();
	
	// Thread 종료시 m_threadHandle 값을 초기화 처리.
	// - 소멸자에서 잘못된 Thread 를 종료할 수 있기 때문.
	pObject->m_threadHandle = 0;


	// 객체 자동 delete 처리.
	delete pObject;	
	return NULL;
}

// client request 요청을 수신하여 이에 따른 job 을 수행
void CClientThread::Execute()
{
	_LOG( LINF, "client thread start [%s]", m_strClientIp.c_str() );

	// Packet Header 정보를 이용한 client 요청 분석
	int nRead;	
	bool bRequest;
	bool bFromClient;
	char command;


	while( 1 )
	{
		// socket 연결을 아래 job 등에서 일부로 끊은 경우를 처리하기 위한 코드.
		// - Server socket 이지만.. 통신 관련 오류시.. server 가 socket 을 끊을 수 있다.
		// - 이는 file 전송 등으로 인해 불필요한 data 가 통신상에 남아 있을 수 있기 때문임.
		if( m_client.IsValidSocket() == false )
			break;

		// Header 수신
		nRead = m_client.GetHeaderInfo( bRequest, bFromClient, command );
		
		if( nRead == 0 )
		{
			// timeout 발생시 - 잠시 대기 후 계속 수신 대기
			sleep( 2 );
			continue;
		}
		else if( nRead < 0 )
		{
			// Socket 접속 종료 또는 오류 발생시
			// - 관련 로그는 해당 모듈에서 다 찍었으므로.. 여기서는 루프 종료 처리.
			break;
		}
		else
		{
			// data 수신시.
			// 정의된 packet 에만 동작.. 그외에는 무시.			
			if( bRequest == true && bFromClient == true )
			{
				// 외부 tool (cmove) 또는 scheduler 에서 보낸 요청인 경우.
				switch( command )
				{
				case CLIENT_COMMAND_SYNC:		// Client 에서 동기화 수행 요청 수신시
					_LOG( LINF, "client[%s] request sync.", m_strClientIp.c_str() );
					ClientRequestSync();
					break;

				default:                        // 미정의 Type 수신시.. client 와 연결 해제 처리
					LOG( LERR, "client[%s] request unknown. check request[%02x]", m_strClientIp.c_str(), command );
					m_client.PrintHeaderToLog();
					m_client.Close();
					break;
				}
			}
			else if( bRequest == true && bFromClient == false )
			{
				// Master rc_syncd 에서 보낸 요청인 경우.
				switch( command )
				{
				case PROCESS_CONTENT_LIST:		// Master 로 부터 Content 정보 목록 요청 수신시
					_LOG( LINF, "master[%s] request content list.", m_strClientIp.c_str() );
					ProcessContentList();
					break;

				case PROCESS_CONTENT_SYNC:		// Master 로 부터 동기화 수행 작업 요청 수신시.
					_LOG( LINF, "master[%s] request sync job.", m_strClientIp.c_str() );
					ProcessContentSync();
					break;

				default:                        // 미정의 Type 수신시.. 연결 해제 처리
					LOG( LERR, "master[%s] request unknown. check request[%02x]", m_strClientIp.c_str(), command );
					m_client.PrintHeaderToLog();
					m_client.Close();
					break;
				}
			}
			else
			{
				// 그 외.. 정의 되지 않은 요청인 경우. => 연결 해제 처리.
				LOG( LERR, "Not defined packet header. client[%s]", m_strClientIp.c_str() );
				m_client.PrintHeaderToLog();
				m_client.Close();
			}
		}

	} // while (1)
	

	// client 와 연결 종료 처리.
	_LOG( LINF, "client connection closed.[%s]", m_strClientIp.c_str() );
	m_client.Close();
}

// Client로 부터 Sync 요청 수신시 이를 처리하기 위한 함수.
void CClientThread::ClientRequestSync()
{
	m_pProcessStatus->PlusMasterModeCount();

	// 수신된 Data 를 저장할 객체 생성
	CReqMasterData requestData;
	if( m_client.RecvBodySyncFromClient( requestData ) == false )
	{
		// Data 수신 실패시
		LOG( LERR, "client[%s] request sync. data receive fail.", m_strClientIp.c_str() );
		m_client.Close();
	}
	else
	{
		// Request Data 수신 성공시.
		LOG( LDBG, "client[%s] request sync. type[%d] time[%s]~[%s] svc[%s]"
			 , m_strClientIp.c_str()
			 , requestData.sync_type, requestData.start_time.c_str(), requestData.end_time.c_str() 
			 , ( requestData.one_service.empty() == true ? "ALL" : requestData.one_service.c_str())
			 );

		// Master Mode 처리를 위한 객체 생성
		CMasterMode modeMaster( &m_client );
		if( modeMaster.CreateSyncList( requestData ) == true )
		{
			// 초기화 작업이 정상적으로 완료된 경우...
			// 업무 Flow 수행함수 실행.
			modeMaster.Execute();
		}
		else
		{
			// 초기화 작업 실패시
			std::string strErrorMessage = "sync list initialize fail.";
			if( m_client.SendResultSyncToClient( false, strErrorMessage ) == false )
			{
				LOG( LERR, "client[%s] sync result[error] send fail.", m_strClientIp.c_str() );				
			}
		}
	}
	
	m_pProcessStatus->MinusMasterModeCount();
	return;
}


// Content 목록 정보 요청 ( Slave Mode ) 인 경우 이를 처리하기 위한 함수.
void CClientThread::ProcessContentList()
{
	m_pProcessStatus->PlusSlaveModeCount();

	// 수신된 Data 를 저장할 객체 생성
	CReqSlaveData requestData;
	if( m_client.RecvBodyContentListFromMaster( requestData ) == false )
	{
		// Data 수신 실패시
		LOG( LERR, "master[%s] request content_list. but data receive fail.", m_strClientIp.c_str() );
		m_client.Close();
	}
	else
	{
		// Request Data 수신 성공시.
		LOG( LDBG, "master[%s] request content list. type[%d] time[%s]~[%s] master[%s] slave[%s]"
			 , m_strClientIp.c_str()
			 , requestData.sync_type, requestData.start_time.c_str(), requestData.end_time.c_str()
			 , requestData.master.c_str(), requestData.slave.c_str()
			 );

		// Slave Mode 처리를 위한 객체 생성
		CSlaveMode modeSlave( &m_client );
		if( modeSlave.CreateSyncList( requestData ) == true )
		{
			// 초기화 작업이 정상적으로 완료된 경우...
			// 업무 Flow 수행함수 실행.
			modeSlave.Execute();
		}
		else
		{
			// 초기화 작업 실패시
			std::string strErrorMessage = "sync list initialize fail.";
			if( m_client.SendErrorContentListToMaster( strErrorMessage ) == false )
			{
				LOG( LERR, "master[%s] content_list result[error] send fail.", m_strClientIp.c_str() );
			}
		}
	}

	m_pProcessStatus->MinusSlaveModeCount();
	return;	
}


// Content 동기화 요청 ( Sync Mode ) 인 경우 이를 처리하기 위한 함수.
void CClientThread::ProcessContentSync()
{
	m_pProcessStatus->PlusSyncModeCount();

	// 수신된 Data 를 저장할 객체 생성
	CReqSyncData requestData;	
	
	if( m_client.RecvBodyExecuteSyncFromMaster( requestData ) == false )
	{
		// Data 수신 실패시
		LOG( LERR, "master[%s] request execute sync. but data receive fail.", m_strClientIp.c_str() );
		m_client.Close();
	}
	else
	{
		// Request Data 수신 성공시.
		LOG( LDBG, "master[%s] request execute sync. type[%d] time[%s]~[%s] master[%s] slave[%s]"
			 , m_strClientIp.c_str()
			 , requestData.sync_type, requestData.start_time.c_str(), requestData.end_time.c_str()
			 , requestData.master.c_str(), requestData.slave.c_str()
			 );

		// Sync Mode 처리를 위한 객체 생성
		CSyncMode modeSync( &m_client );
		if( modeSync.CreateSyncList( requestData ) == true )
		{
			// modeSync 객체 초기화 성공시...
			// socket 에서 sync 파일 정보를 수신받아 저장처리한다.
			// 하지만.. 파일명 정보가 CSyncMode 내부에 저장되어 있으므로...
			// 이에 대한 수신 처리 및 응답 처리는 CSyncMode 내의 Execute 함수 내에서 처리한다.

			// 초기화 작업이 정상적으로 완료된 경우...
			// 업무 Flow 수행함수 실행.
			modeSync.Execute();
		}
		else
		{
			// 초기화 작업 실패시
			std::string strErrorMessage = "sync list initialize fail.";
			if( m_client.SendResultExecuteSyncToMaster( false, strErrorMessage ) == false )
			{
				LOG( LERR, "master[%s] execute sync result[error] send fail.", m_strClientIp.c_str() );
			}
		}
	}
	
	
	m_pProcessStatus->MinusSyncModeCount();
	return;
}
