#include "FimngdSocketControl.h"

#include "Logger.h"

#include <arpa/inet.h>

// Packet Header stx 코드
#define HEADER_STX_CODE					0x02

// Packet Header type 구분코드
#define HEADER_TYPE_REQUEST				0x00

// Packet command : fimngd 요청 ( Command 3th Byte 만 사용 )
#define REQUEST_CACHE_CREATE            0x01


// 생성자.
// param [IN] clientSocket client 와 통신 연결된 socket.
CFimngdSocketControl::CFimngdSocketControl( int clientSocket )
	: CBaseSocket( clientSocket )
	  , m_nPacketHeaderLen ( sizeof(m_packetHeader))
	  , m_nPacketDataLen( 0 )
{
}

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

// 수신된 Data 를 전달받은 인자에 설정하여 반환 처리
// @param [OUT] data 수신된 Data 를 저장할 객체에 대한 참조
// @param [in] timeout 대기 timeout (sec)
// @return  -1 : Socket 오류 또는 잘못된 Data 수신시
//           0 : timeout 발생시
//           1 : 정상
int CFimngdSocketControl::GetData( CCacheRequestData & data, int timeout )
{
	// Header 정보를 수신
	int nResult = GetPacketHeader( timeout );
	
	if( nResult == -1 )
	{
		// Socket 끊김 또는 정의되지 않은 Data 수신시.
		return -1;
	}
	else if( nResult == 0 )
	{
		// timeout 발생시
		return 0;
	}

	// Header Data 정상 수신시... command code 에 따른 처리.	
	if( m_packetHeader.command[2] == REQUEST_CACHE_CREATE )
	{
		// Cache 생성 요청인 경우...

		// Body 부분이 고정 Packet 형식이므로....
		// - 수신된 data_length 와 이를 저장할 구조체의 크기가 동일한지 확인
		// - 만약 동일하지 않은 경우.. packet 오류로 처리
		if( m_nPacketDataLen != sizeof( struct stCacheRequestBody ) )
		{
			LOG( LERR, "Cache request packet body size not valid.[%u]/[%lu]"
				, m_nPacketDataLen, sizeof( struct stCacheRequestBody ) );
			return -1;
		}

		// Data 부분 수신 처리.
		if( GetBodyCacheRequest( data ) == false )
		{
			// body 수신 처리시 오류 발생			
			return -1;
		}
		
		return 1;		
	}
	else
	{
		// 미정의된 command code 인 경우( 현재 1개 type 만 정의되어 있으므로. )
		LOG( LERR, "Not defined command code." );
		PrintHeaderToLog();
		return -1;
	}
}


// Packet Header 정보를 Log 파일에 Logging 처리 ( Debug 처리를 위한 함수)
void CFimngdSocketControl::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_nPacketDataLen );
	_LOG( LINF, "extend_code [%02x][%02x]", m_packetHeader.extend_code[0], m_packetHeader.extend_code[1] );

	_LOG( LINF, "------------------------------------" );
}

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

	// Socket 으로 부터 Packet Header 부분 수신.
	// 수신된 정보는 멤버변수에 저장처리.
	/// read 된 데이터의 크기. 0: fd closed, -1: 오류, -2: Timeout
	int nRead = ReadNTimeout( &m_packetHeader, m_nPacketHeaderLen, 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 )
		{
			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_nPacketDataLen = ntohl( m_packetHeader.data_length );

		return 1;
	}
}


// FHS 의 Hot Content Cache 생성 요청 Packet Body 부분을 수신 처리
bool CFimngdSocketControl::GetBodyCacheRequest( CCacheRequestData & data )
{
	// 수신된 Body 를 저장할 변수 생성.
	struct stCacheRequestBody stBody;

	// Body 부분 수신 처리.
	int nRead = ReadNTimeout( &stBody, m_nPacketDataLen, 15 );
	
	if( nRead == -2 )
	{
		// timeout 발생시
		// - 지정된 시간 동안 Header 에 기록된 Body 크기를 읽지 못했으므로. 오류로 처리
		LOG( LERR, "cache request body[%lu] read timeout.", sizeof( struct stCacheRequestBody ) );
		return false;
	}
	else if( nRead <= 0 )
	{
		// timeout 을 제외한 그 밖의 오류 발생시
		LOG( LERR, "cache request body read error[%d]", nRead );
		return false;
	}
	else
	{
		// Body 부분 정상 수신시...
		// - 수신된 Data 를 파싱하여....
		// - 인자로 전달받은 data 객체에 저장 처리.
		data.Set( &stBody );
		
		return true;
	}
}
