#include "SocketApiClient.h"

#include "Logger.h"


#include <arpa/inet.h>
#include <string.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/syscall.h>


#include <sstream>


// request 의 body 부분 수신 처리를 담당할 임시 버퍼 크기
#define DEFAULT_SOCKET_TEMP_BUFFER_SIZE		(1024 * 6)


CSocketApiClient::CSocketApiClient( int clientSocket )
	: CBaseSocket(clientSocket)
	, m_iRequestHeaderLen( sizeof( m_stRequestHeader ) )
	, m_uRequestDataLen(0)
{
	// thread id 정보 추출
	m_lThreadId = syscall( SYS_gettid );

}

CSocketApiClient::~CSocketApiClient()
{
	// Socket 에 대한 명시적 close 처리
	Close();
}


int CSocketApiClient::GetRequestHeader( int timeout )
{
	if( IsValidSocket() == false )
		return -1;

	// Socket 으로 부터 Request Header 부분 수신.
	// 수신된 정보는 멤버변수에 저장처리.
	/// read 된 데이터의 크기. 0: fd closed, -1: 오류, -2: Timeout
	int nRead = ReadNTimeout( &m_stRequestHeader, m_iRequestHeaderLen, timeout );
	if( nRead == -2 )
	{
		// timeout 발생시
		return 0;
	}
	else if( nRead <= 0 )
	{
		// timeout 을 제외한 그 밖의 오류 발생시
		return -1;
	}
	else
	{
		// 정상 Data 수신시.
		 
		// STX code 검사.
		if( m_stRequestHeader.id != HEADER_ID_CODE_API_CLIENT )
		{
			_LOG( LERR, "[SocketApiClient %d %ld] not valid request header id.", getpid(), m_lThreadId );
			PrintHeaderToLog();
			return -1;
		}

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

		return 1;
	}
}

void CSocketApiClient::PrintHeaderToLog( void )
{
	_LOG( LINF, "------------------------------------" );
	_LOG( LINF, "id               [%u]", ntohl( m_stRequestHeader.id) );
	_LOG( LINF, "data_length      [%u]", m_uRequestDataLen );
	_LOG( LINF, "------------------------------------" );
}

int CSocketApiClient::GetRequestSizHashBody( std::string & szService, std::string & szUri, std::string & szErrorMessage )
{
	char tempBuffer[DEFAULT_SOCKET_TEMP_BUFFER_SIZE];

	// Body 크기가 buffer 보다 큰 경우...
	if( m_uRequestDataLen > DEFAULT_SOCKET_TEMP_BUFFER_SIZE )
	{
		sprintf( tempBuffer, "request body size too large. request[%u] buffer[%u]"
			, m_uRequestDataLen, DEFAULT_SOCKET_TEMP_BUFFER_SIZE );

		szErrorMessage = tempBuffer;

		_LOG( LERR, "[SocketApiClient %d %ld] request body size too large. request[%u] socket_buffer[%u]"
			, getpid(), m_lThreadId
			, m_uRequestDataLen, DEFAULT_SOCKET_TEMP_BUFFER_SIZE );

		return -1;
	}

	// body 수신작업 전 tempBuffer 초기화.
	memset( &tempBuffer, 0x00, DEFAULT_SOCKET_TEMP_BUFFER_SIZE );

	int nRead = 0;
	nRead = ReadNTimeout( &tempBuffer, m_uRequestDataLen, 3);
	if( nRead == -2 )
	{
		// timeout 발생시
		sprintf( tempBuffer, "request body[%u] recv timeout(3 sec).", m_uRequestDataLen );
		szErrorMessage = tempBuffer;

		_LOG( LERR, "[SocketApiClient %d %ld] request body[%u] recv timeout(3 sec)."
			, getpid(), m_lThreadId, m_uRequestDataLen);

		return -1;
	}
	else if( nRead <= 0 )
	{
		// timeout 을 제외한 그 밖의 오류 발생시
		szErrorMessage = "request body recv error.";

		_LOG( LERR, "[SocketApiClient %d %ld] request body[%u] recv error"
			, getpid(), m_lThreadId, m_uRequestDataLen);

		return -1;
	}

	// body 정보가 정상 수신된 경우.
	_LOG( LDBG, "[SocketApiClient %d %ld] request body [%s]", getpid(), m_lThreadId, tempBuffer );

	// json 형식의 body 정보를 파싱 처리한다.
	// 파싱 편의를 위해 전달받은 data 를 string 처리
	std::string szBody = tempBuffer;
	std::string::size_type posStart;
	std::string::size_type posEnd;

	// 서비스 정보 추출 
	posStart = szBody.find( "\"service\":\"" );
	if( posStart == std::string::npos )
	{
		szErrorMessage = "request parsing error. service key not exist.";

		// service 정보가 없는 경우...
		_LOG( LERR, "[SocketApiClient %d %ld] request parsing error. service key not exist.[%s]"
			, getpid(), m_lThreadId, szBody.c_str());

		return 0;
	}

	posStart += 11;
	posEnd = szBody.find( '"', posStart);
	szService = szBody.substr( posStart, posEnd - posStart );

	// uri 정보 추출 
	posStart = szBody.find( "\"uri\":\"" );
	if( posStart == std::string::npos )
	{
		szErrorMessage = "request parsing error. uri key not exist.";

		// service 정보가 없는 경우...
		_LOG( LERR, "[SocketApiClient %d %ld] request parsing error. uri key not exist.[%s]"
			, getpid(), m_lThreadId, szBody.c_str());

		return 0;
	}

	posStart += 7;
	posEnd = szBody.find( '"', posStart);
	szUri = szBody.substr( posStart, posEnd - posStart);


	return 1;
}

bool CSocketApiClient::SendResultSizeHashError( std::string & szService, std::string & szUri, std::string & szMessage )
{
	if( IsValidSocket() == false )
		return false;

	// 정보 전달을 위한 response header 생성
	// - request header 와 동일한 형식
	struct RequestHeaderApiClient stHeader;
	memset( &stHeader, 0x00, sizeof( struct RequestHeaderApiClient ) );

	stHeader.id = HEADER_ID_CODE_API_CLIENT;

	// response body 정보 생성
	std::string szBody;

	szBody = "{\"service\":\"" + szService 
			+ "\", \"uri\":\"" + szUri 
			+ "\", \"result\":\"error\", \"size\":\"0\", \"md5\":\"0\", \"msg\":\"" + szMessage 
			+"\"}";

	stHeader.data_length = htonl( szBody.size() );

	// 로그
	_LOG( LINF, "[SocketApiClient %d %ld] error response [%u][%u][%s]"
		, getpid(), m_lThreadId
		, ntohl( stHeader.id ), ntohl( stHeader.data_length )
		, szBody.c_str() );

	// response header send
	if( WriteN( &stHeader, m_iRequestHeaderLen ) != m_iRequestHeaderLen )
	{
		_LOG( LERR, "[SocketApiClient %d %ld] size, hash error response send fail."
			, getpid(), m_lThreadId );

		return false;
	}
	else
	{
		// response body 부분 전송
		// - 뭐 전송 실패해도 할게 없으므로.. 그냥 call 처리.
		WriteN( szBody.c_str(), szBody.size() );
	}

	return true;
}

bool CSocketApiClient::SendResultSizeHash( std::string & szService, std::string & szUri, unsigned long long & uContentLength, std::string & szHash )
{
	if( IsValidSocket() == false )
		return false;

	// 정보 전달을 위한 response header 생성
	// - request header 와 동일한 형식
	struct RequestHeaderApiClient stHeader;
	memset( &stHeader, 0x00, sizeof( struct RequestHeaderApiClient ) );

	stHeader.id = HEADER_ID_CODE_API_CLIENT;

	// 문자열 조립이 쉽도록 .. 숫자를 문자열로 변환 처리 저장.
	std::stringstream szContentLength;
	szContentLength << uContentLength;
	
	// response body 정보 생성
	std::string szBody;

	szBody = "{\"service\":\"" + szService
		+ "\", \"uri\":\"" + szUri
		+ "\", \"result\":\"ok\", \"size\":\"" + szContentLength.str()
		+ "\", \"md5\":\"" + szHash 
		+ "\", \"msg\":\"\"}";

	stHeader.data_length = htonl( szBody.size() );
	
	// 로그
	_LOG( LINF, "[SocketApiClient %d %ld] response [%u][%u][%s]"
		, getpid(), m_lThreadId
		, ntohl(stHeader.id), ntohl(stHeader.data_length) 
		, szBody.c_str());

	// response header send
	if( WriteN( &stHeader, m_iRequestHeaderLen ) != m_iRequestHeaderLen )
	{
		_LOG( LERR, "[SocketApiClient %d %ld] size, hash result response send fail."
			, getpid(), m_lThreadId );

		return false;
	}
	else
	{
		// response body 부분 전송
		// - 뭐 전송 실패해도 할게 없으므로.. 그냥 call 처리.
		WriteN( szBody.c_str(), szBody.size() );
	}

	return true;
}



