#include "SocketSendInfo.h"

#include "Logger.h"
#include "ProcessConfig.h"

#include <arpa/inet.h>
#include <string.h>

#include <iterator>


// 생성자
// param [in] clientSocket client 와 통신 연결된 socket.

CSocketSendInfo::CSocketSendInfo( int clientSocket )
	: CBaseSocket( clientSocket )	
	, m_iPacketHeaderLen( sizeof( m_packetHeader ) )
	, m_uPacketDataLen( 0 )
{
	
}

// 소멸자.
CSocketSendInfo::~CSocketSendInfo()
{
	// 소멸자 Socket 명시적 Close 처리.
	Close();
}


// Packet Header 부분의 수신 처리를 위한 함수
// param command [out] 수신된 command code		
// param timeout [in] 대기시간.
// retrun 0 : timeout
//        1 : 성공
//       -1 : socket 오류 또는 정의된 Data 가 아닌 경우.
int CSocketSendInfo::GetPacketHeader( char & command, int timeout )
{
	if( IsValidSocket() == false )
		return -1;

	// Socket 으로 부터 Packet Header 부분 수신.
	// 수신된 정보는 멤버변수에 저장처리.
	/// read 된 데이터의 크기. 0: fd closed, -1: 오류, -2: Timeout
	int nRead = ReadNTimeout( &m_packetHeader, m_iPacketHeaderLen, timeout );
	if( nRead == -2 )
	{
		// timeout 발생시
		return 0;
	}
	else if( nRead <= 0 )
	{
		// timeout 을 제외한 그 밖의 오류 발생시
		return -1;
	}
	else
	{
		// 정상 Data 수신시.

		// STX code 검사.
		if( m_packetHeader.stx != HEADER_STX_CODE_SEND_INFO )
		{
			LOG( LERR, "Not valid stx code." );
			PrintHeaderToLog();
			return -1;
		}

		// Type Code 검사
		if( m_packetHeader.type != HEADER_TYPE_REQUEST )
		{
			LOG( LERR, "Not valid type code." );
			PrintHeaderToLog();
			return -1;
		}
		

		// Data Length 부분 값을 멤버 변수에 저장처리.
		m_uPacketDataLen = ntohl( m_packetHeader.data_length );

		// command code 정보를 출력 param 에 저장 처리.
		// 통신 정의상 1th 항목만 사용.
		command = m_packetHeader.command[1];

		return 1;
	}
}

// Packet Header 정보를 Log 파일에 Logging 처리 ( Debug 처리를 위한 함수)
void CSocketSendInfo::PrintHeaderToLog()
{
	_LOG( LINF, "------------------------------------" );
	_LOG( LINF, "stx              [%02x]", m_packetHeader.stx );
	_LOG( LINF, "type             [%02x]", m_packetHeader.type );
	_LOG( LINF, "command          [%02x][%02x][%02x][%02x]", m_packetHeader.command[0], m_packetHeader.command[1], m_packetHeader.command[2], m_packetHeader.command[3] );
	_LOG( LINF, "result           [%02x][%02x][%02x][%02x]", m_packetHeader.result[0], m_packetHeader.result[1], m_packetHeader.result[2], m_packetHeader.result[3] );
	_LOG( LINF, "data_length      [%u]", m_uPacketDataLen );
	_LOG( LINF, "protocol_version [%02x]", m_packetHeader.protocol_version );
	_LOG( LINF, "extend_code      [%02x]", m_packetHeader.extend_code );
	_LOG( LINF, "------------------------------------" );
}

// COMMAND_ALIVE_FHS 요청에 대한 Body 수신 처리.
// - protocol version 에 따른 처리 필요.
// param strHostname [out] 수신된 hostname 정보.
// return false socket 오류, 해당 protocol version 미지원, RC ID 불일치 등 비정상 상황.
//        그외 true
bool CSocketSendInfo::GetPacketBodyAliveFhs( std::string & strHostname )
{	
	// 지원 가능한 protocol version 인지 검사.
	if( m_packetHeader.protocol_version > PARSE_MAX_PROTOCOL_VERSION_ALIVE_FHS )
	{
		// 파싱 불가능한 버전인 경우...
		LOG( LERR, "Not support protocol version. [COMMAND_ALIVE_FHS] support[%02x] request[%02x]."
			 , PARSE_MAX_PROTOCOL_VERSION_ALIVE_FHS, m_packetHeader.protocol_version );

		PrintHeaderToLog();

		std::string strErrorMessage = "Not support request protocol version.";
		SendResultAliveFhsError( strErrorMessage );
		
		return false;
	}
	
	// protocol version 별 파싱 처리.
	if( m_packetHeader.protocol_version == 0x00 )
	{
		// 0x00 버전 지원.
		struct PacketDataRequestAliveFhs_V00 stBody;
		memset( &stBody, 0x00, sizeof( struct PacketDataRequestAliveFhs_V00 ) );

		// Body 부분 크기가.. 수신 처리할 구조체 크기와 동일한지 체크...
		// - 안 할 경우.... body 크기가 저장 구조체 크기보다 큰 경우.. 
		// - 본 모듈이 memory overflow 로 죽으므로... 방어코드 적용.
		if( m_uPacketDataLen != sizeof( stBody ) )
		{
			LOG( LERR, "Invalid body size. [COMMAND_ALIVE_FHS] version[%02x] define[%zu] request_size[%u]"
				 , m_packetHeader.protocol_version, sizeof( stBody ) , m_uPacketDataLen);

			PrintHeaderToLog();

			std::string strErrorMessage = "Not match request(alive fhs) protocol body size.";
			SendResultAliveFhsError( strErrorMessage );

			return false;
		}
		
		// Body 부분 수신 처리.
		int nRead = ReadNTimeout( &stBody, m_uPacketDataLen, 5 );		
		if( nRead == -2 )
		{
			// timeout 발생시
			// - 지정된 시간 동안 Header 에 기록된 Body 크기를 읽지 못했으므로. 오류로 처리
			LOG( LERR, "alive fhs request body[%u] read timeout.", m_uPacketDataLen );
			return false;
		}
		else if( nRead <= 0 )
		{
			// timeout 을 제외한 그 밖의 오류 발생시
			LOG( LERR, "alive fhs request body read error[%d]", nRead );
			return false;
		}
		else
		{		
			// Body 부분 정상 수신시...
			// 1. RC ID 정보 비교 후. (대소문자 무시)
			// 2. 정상인 경우..수신된 Data 를 파싱하여 인자로 전달받은 data 에 저장 처리.
			if( strcasecmp( CProcessConfig::GetInstance()->GetRcId(), stBody.rcid ) != 0 )
			{
				LOG( LERR, "alive fhs body error. RC ID not match. request [%s][%s]", stBody.rcid, stBody.hostname );

				std::string strErrorMessage = "RC ID not match.";
				SendResultAliveFhsError( strErrorMessage );
				
				return false;
			}

			// 정상인 경우...
			strHostname = stBody.hostname;
			
			return true;
		}
	}
	else
	{
		// 해당 버전에 대한 파싱 코드가 아직 없는 경우...
		LOG( LERR, "Not support protocol version parsing. [COMMAND_ALIVE_FHS] request[%02x].", m_packetHeader.protocol_version );

		std::string strErrorMessage = "Not support request protocol version parsing.";
		SendResultAliveFhsError( strErrorMessage );

		return false;
	}

	
}

// COMMAND_ALIVE_FHS 요청 관련 error message 전송
bool CSocketSendInfo::SendResultAliveFhsError( std::string & strMessage )
{
	if( IsValidSocket() == false )
		return false;

	// 정보 전달을 위한 Packet Header 생성
	struct PacketHeaderSendInfo stHeader;
	memset( &stHeader, 0x00, sizeof( struct PacketHeaderSendInfo ) );

	stHeader.stx        = HEADER_STX_CODE_SEND_INFO;
	stHeader.type       = HEADER_TYPE_RESPONSE;	
	stHeader.command[1] = COMMAND_ALIVE_FHS;
	stHeader.result[3]  = RESULT_ALIVE_FHS_ERROR;

	// Data body length
	stHeader.data_length = htonl( strMessage.size() );

	// Protocol version
	// - 수신된 protocol version 과 같도록
	stHeader.protocol_version = m_packetHeader.protocol_version;
	
	// Packet Header 정보 전송
	if( WriteN( &stHeader, m_iPacketHeaderLen ) != m_iPacketHeaderLen )
	{
		LOG( LERR, "[COMMAND_ALIVE_FHS] error result send fail" );
		return false;
	}
	else
	{
		// message 부분이 존재하는 경우.. 전송 처리.
		if( strMessage.empty() == false )
		{
			WriteN( strMessage.c_str(), strMessage.size() );
		}
	}

	return true;
}

// COMMAND_ALIVE_FHS 요청 관련 SKIP result 전송
bool CSocketSendInfo::SendResultAliveFhsSkip()
{
	if( IsValidSocket() == false )
		return false;

	// 정보 전달을 위한 Packet Header 생성
	struct PacketHeaderSendInfo stHeader;
	memset( &stHeader, 0x00, sizeof( struct PacketHeaderSendInfo ) );

	stHeader.stx        = HEADER_STX_CODE_SEND_INFO;
	stHeader.type       = HEADER_TYPE_RESPONSE;
	stHeader.command[1] = COMMAND_ALIVE_FHS;
	stHeader.result[3]  = RESULT_ALIVE_FHS_SKIP;

	// Data body length
	stHeader.data_length = htonl( 0 );

	// Protocol version
	// - 수신된 protocol version 과 같도록
	stHeader.protocol_version = m_packetHeader.protocol_version;

	// Packet Header 정보 전송
	if( WriteN( &stHeader, m_iPacketHeaderLen ) != m_iPacketHeaderLen )
	{
		LOG( LERR, "[COMMAND_ALIVE_FHS] skip result send fail" );
		return false;
	}

	// 전송할 Body 는 없으므로.. pass.

	return true;
}


// COMMAND_ALIVE_FHS 요청 관련 Alive FHS 목록 전송
bool CSocketSendInfo::SendResultAliveFhs( std::vector< struct FhsServiceStatInfo > & vecAliveFhs )
{
	if( IsValidSocket() == false )
		return false;

	// 정보 전달을 위한 Packet Header 생성
	struct PacketHeaderSendInfo stHeader;
	memset( &stHeader, 0x00, sizeof( struct PacketHeaderSendInfo ) );

	stHeader.stx        = HEADER_STX_CODE_SEND_INFO;
	stHeader.type       = HEADER_TYPE_RESPONSE;
	stHeader.command[1] = COMMAND_ALIVE_FHS;
	stHeader.result[3]  = RESULT_ALIVE_FHS_OK;

	// Protocol version
	// - 수신된 protocol version 과 같도록
	stHeader.protocol_version = m_packetHeader.protocol_version;

	// protocol vesion 에 따른 body 부분 처리
	if( m_packetHeader.protocol_version == 0x00 )
	{
		// 전달받은 결과 목록을 result 전송 형식에 맞도록 data 조립
		// - 0x00 버전에서는 data 구조가 동일하므로.. 그냥 전송 처리해도 되지만...
		// - 향후 버전 확장을 대비하여... data 조립하도록 코딩 처리함.

		std::vector< struct PacketDataResponseAliveFhs_V00 > vecResponse;
		struct PacketDataResponseAliveFhs_V00 fhs;
		std::vector< struct FhsServiceStatInfo >::iterator it;

		vecResponse.clear();

		// 전달받은 Alive FHS 정보를 전송하기 위한 vector 에 저장 처리한다.
		// - 향후 통신 프로토콜 변경 대비 변환 처리 수행함.				
		for( it = vecAliveFhs.begin(); it != vecAliveFhs.end(); ++it )
		{
			// hostname 정보 복사.			
			strncpy( fhs.hostname, it->hostname, FIMNGD_HOST_NAME_MAX_LENGTH );

			// 서비스 상태 정보 code
			fhs.service_stat = it->service_stat;

			vecResponse.push_back( fhs );
		}

		// Data 전송을 위한 Size 계산 및 Body 항목 조립

		// 1. 목록 크기 계산.
		unsigned int nListSize = sizeof( struct PacketDataResponseAliveFhs_V00 ) * vecResponse.size();

		// 2. Data body length 설정.
		unsigned int nTemp   = 4 + nListSize; // count (int 4Byte) + PacketDataResponseAliveFhs_V00 list			
		stHeader.data_length = htonl( nTemp );
		
		// 3. body 정보 중 FHS count 정보는 network byte order 변환 후 전송 ( 전송 규약)
		nTemp = htonl( vecResponse.size() );

		// Packet Header 정보 전송
		if( WriteN( &stHeader, m_iPacketHeaderLen ) != m_iPacketHeaderLen )
		{
			LOG( LERR, "[COMMAND_ALIVE_FHS] result header send fail." );
			return false;
		}
		else
		{
			// Body 부분 전송 처리.

			// 1. FHS Count ( Network byte order )
			WriteN( &nTemp, 4 );

			// 2. Alive FHS 목록 정보.
			if( vecResponse.empty() == false )
			{
				if( WriteN( &( vecResponse[0] ), nListSize ) != nListSize )
				{
					LOG( LERR, "[COMMAND_ALIVE_FHS] result body send fail. count[%zu] size[%u]", vecResponse.size(), nListSize );
					return false;
				}
			}
		}

		return true;
	}
	else
	{
		// 전송 protocol 규약을 지원하지 못하는 경우....
		LOG( LERR, "Not support protocol version to send resutl [COMMAND_ALIVE_FHS]. request[%02x].", m_packetHeader.protocol_version );

		std::string strErrorMessage = "Not support request protocol version.";
		SendResultAliveFhsError( strErrorMessage );

		return false;
	}	
}
