#include "FtsdSocketControl.h"

#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <string.h>

#include <iterator>


/// @brief 생성자.
CFtsdSocketControl::CFtsdSocketControl( )
	: CBaseSocket( SOCKET_NOT_VALID )
	  , m_nPacketHeaderLen ( sizeof(m_packetHeader))
	  , m_nPacketDataLen( 0 )
{
}

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

/// @brief 전달받은 Target 으로 Socket 접속을 수행
/// @param szTarget [in] 접속 대상 Host name 또는 IP
/// @param nPort	[in] 접속 Port
/// @return 접속 성공시 true, 실패시 false 반환. ( 오류 내역은 로깅처리됨.)
bool CFtsdSocketControl::ConnectTarget( const std::string& szTarget, int nPort )
{
	return Connect( szTarget, nPort );
}


/// @brief Packet Header 정보를 Log 파일에 Logging 처리
void CFtsdSocketControl::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, "------------------------------------" );
}


/// @brief Alive Check 를 위한 패킷 전송 => 해당 패킷의 응답은 필요없으므로 수신시 무시할 것.
/// @return 전송 성공시 true, 실패시 false 반환.
bool CFtsdSocketControl::SendAliveCheck()
{
	if( IsValidSocket() == false )
		return false;
	
	// 정보 요청을 위한 Packet Header 생성.
	struct FileTransferPacketHeader stHeader;
	memset(&stHeader, 0x00, sizeof(struct FileTransferPacketHeader));

	stHeader.stx			= HEADER_STX_CODE;
	stHeader.type			= HEADER_TYPE_REQUEST; 
	stHeader.command[0]		= COMMON_ALIVE_CHECK;

	// Packet Header 정보 전송
	if( WriteN( &stHeader, m_nPacketHeaderLen ) != m_nPacketHeaderLen )
		return false;

	return true;
}


/// @brief 연결된 Socket 을 통해 Transfer Daemon 으로 File Replication 명령 전송.
/// @param szFileName [in] 복제할 원본 Source 파일명
/// @param nFileSize  [in] 복제할 원본 Source 파일의 크기.
/// @param szTargetTranId [in[ RC 간 복제 처리시 변경할 대상 Tran ID 정보 값
/// @param nFileHashCheckLevel [in] File 에 대한 Hash Check Level 정보
/// @param bUseInternalIp [in] 내부망을 이용하여 파일 송수신을 수행할지 여부
/// @param vecTargetFhs [in] 복제할 Target FHS 정보
/// @return Replication 복제 요청 메시지 전송 성공시 true, 실패시 false 반환.
bool CFtsdSocketControl::SendFileReplicationRequest(const std::string& szFileName, unsigned long long nFileSize
		, int nFileHashCheckLevel, bool bUseInternalIp, std::vector< std::string >& vecTargetFhs)
{
	std::string szTargetTranId;
	szTargetTranId.clear();

	return SendFileReplicationRequest( szFileName, nFileSize, szTargetTranId, nFileHashCheckLevel, bUseInternalIp, vecTargetFhs);
}

bool CFtsdSocketControl::SendFileReplicationRequest(const std::string& szFileName, unsigned long long nFileSize
		, const std::string& szTargetTranId, int nFileHashCheckLevel, bool bUseInternalIp, std::vector< std::string >& vecTargetFhs)
{
	if( IsValidSocket() == false )
		return false;
	
	// 정보 요청을 위한 Packet Header 생성.
	struct FileTransferPacketHeader stHeader;
	memset(&stHeader, 0x00, sizeof(struct FileTransferPacketHeader));

	stHeader.stx			= HEADER_STX_CODE;
	stHeader.type			= HEADER_TYPE_REQUEST; 
	stHeader.command[0]		= CONTROL_FILE_REPLICATION;

	// Data 부분의 길이를 계산한다.
	unsigned int nTemp = 0;

	nTemp  = 4 + szFileName.size();			// File Name ( 4 + 가변 )
	nTemp += 8;								// File Size ( 8 Byte )
	nTemp += ( 4 + szTargetTranId.size());	// Target Tran ID ( 4 + 가변 )
	nTemp += 4;								// File Hash check Level ( 4 Byte )
	nTemp += 1;								// 내부망 IP 사용 여부 ( 1 Byte )
	nTemp += 4;								// FHS Count ( 4 Byte )

	std::vector< std::string >::const_iterator it;
	for( it = vecTargetFhs.begin(); it != vecTargetFhs.end() ; it++ )
	{
		nTemp = nTemp + 4 + it->size() ;	// Target FHS 정보 ( 4 + 가변 )
	}

	stHeader.data_length = htonl( nTemp );

	// Packet Header 정보 전송
	if( WriteN( &stHeader, m_nPacketHeaderLen ) != m_nPacketHeaderLen )
	{
		LOG( LERR, "File Replication request send failed.");

		return false;
	}

	// Packet Data 부분 전송
	
	// File Name 부분
	nTemp = htonl( szFileName.size() );
	WriteN( &nTemp, 4);
	WriteN( szFileName.c_str(), szFileName.size() );

	// File Size 전송
	WriteN( &nFileSize, 8 );

	// Target Tran ID 부분
	nTemp = htonl( szTargetTranId.size() );
	WriteN( &nTemp, 4);
	if( szTargetTranId.size() > 0 )
		WriteN( szTargetTranId.c_str(), szTargetTranId.size() );

	// File Hash Check Level 전송
	nTemp = htonl( nFileHashCheckLevel );
	WriteN( &nTemp, 4);

	// 내부망 사용 여부 전달
	if( bUseInternalIp == true )
		m_tempBuffer[0] = 0x01;
	else
		m_tempBuffer[0] = 0x00;

	WriteN( m_tempBuffer, 1);

	// Target FHS Count 정보 전달.
	nTemp = htonl( vecTargetFhs.size() );
	WriteN( &nTemp, 4);

	// Loop 를 돌면서 각 FHS 정보를 전달한다.
	for( it = vecTargetFhs.begin(); it != vecTargetFhs.end() ; it++ )
	{
		nTemp = htonl( it->size() );
		WriteN( &nTemp, 4);
		WriteN( it->c_str(), it->size());
	}

	return true;
}


/// @brief File Replication 요청에 대한 처리 결과를 수신한다. 
/// @param nTimeout [in] 함수 호출에 대한 Blocking 을 방지하기 위한 Timeout 설정값.
/// @param bSuccess [out] File Replication 이 정상적으로 수행되었는지 여부
/// @param szErrorMessage [out] bSuccess == false 인 경우 발생된 오류메시지 정보.
/// @param mapSuccessFhs [out] Replication 에 대한 처리 성공시 성공한 FHS 및 File 정보를 저장
/// @param mapFailFhs [out] Replication 에 대한 처리 성공시 실패한 FHS 및 오류메시지 정보를 저장.
/// @return -1 : Read 수신시 오류 발생시 => ftsd 와 연결이 끝어진것으로 판단하고 오류처리한다. \n
///<         0 : nTimeout 에 지정된 시간안에 완료 응답을 받지 못한 경우. \n
///<             이 기능은 호출 함수의 Block 을 해제하기 위한 기능으로 \n
///<             이 값이 반환된 경우 다른 작업을 한 후 다시 호출하도록 한다. \n
///<         1 : 파일 복제 처리에 대한 응답을 수신한 경우, Output 인자를 확인할 것. \n
///<         2 : Transfer Daemon 으로 부터 Packet 을 수신하였으나 File Replication 요청에 대한 응답이 아닌 경우 \n
///<             해당 정보는 멤버변수 상에 저장된다. ( 단 Alive Packet 은 아님)
int CFtsdSocketControl::GetFileReplicationResult( int nTimeout, bool& bSuccess, std::string& szErrorMessage
		, std::map< std::string, std::string>& mapSuccessFhs
		, std::map< std::string, std::string>& mapFailFhs )
{
	// 복제 요청에 대한 응답 패킷 대기 
	// 수신 대기 Timeout 은 전달받은 값으로 설정.
	if( IsValidSocket() == false )
		return -1;
	
	int nRead;
	bool isAlivePacket;
	do
	{
		isAlivePacket = false;

		// Socket 으로 부터 Packet Header 부분 수신.
		// 수신된 정보는 멤버변수에 저장처리.
		nRead = ReadNTimeout( &m_packetHeader, m_nPacketHeaderLen, nTimeout );
	
		// nRead  0: Socket Closed, -1: error , -2: Timeout 이므로
		if( nRead == -2 )
			return 0;		// Timeout 반환.
		else if( nRead == 0 || nRead == -1 )
			return -1;		// Socket Close 또는 오류 발생시
	
		// STX code 검사.
		if( m_packetHeader.stx != HEADER_STX_CODE )
		{
			LOG( LERR, "Not valid stx code.");
		
			return -1;
		}

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

		// Alive Check Packet 인 경우 해당 패킷은 무시처리 후 다시 수신 처리한다.
		if( m_packetHeader.type == HEADER_TYPE_REQUEST
				&& m_packetHeader.command[0] == COMMON_ALIVE_CHECK 
				&& m_nPacketDataLen == 0 )
		{
			isAlivePacket = true;
		}

	} while( isAlivePacket == true );

	// Alive Packet 이 아닌 경우 해당 Packet 에 대한 복제에 대한 응답패킷인지 검사.
	if( m_packetHeader.type != HEADER_TYPE_RESPONSE	
			|| m_packetHeader.command[0] != CONTROL_FILE_REPLICATION )
	{
		// 다른 패킷이 들어온 경우 

		// ** 정석대로라면 그냥 냅둬어 수신부에서 처리해야 하지만
		// 현재 수신 처리부가 없어 당장 문제가 생길수 있으므로 
		// 데이터 부분까지 수신하여 임시버퍼에 저장처리 해 놓는다. 
		GetPacketData( m_nPacketDataLen );
		return 2;
	}

	// Replication 에 대한 응답 패킷인 경우

	// 1. 성공, 실패 여부 판단.
	if( m_packetHeader.result[0] != HEADER_RESULT_SUCCESS )
	{
		// 오류가 발생한 경우임
		bSuccess = false;

		// Packet Data 상에서 오류 메시지를 수신받아 반환 처리.
		if( GetPacketData( m_nPacketDataLen, szErrorMessage ) == false )
		{
			LOG( LERR, "Error msg receive failed.");

			return -1;
		}
		else
			return 1;
	}

	// 2. 성공인 경우
	bSuccess = true;
	
	unsigned int nCount;
	unsigned int nLength;
	std::string szKey;
	std::string szValue;

	// 2.1 성공 목록의 Count 정보 수신
	if( GetPacketData( 4 ) == false )
	{
		LOG( LERR, "Success count receive failed.");
		return -1;
	}
	nCount = GetDataToUInt( m_tempBuffer, true);
	for( unsigned int i = 0; i < nCount; i++)
	{
		szKey.clear();
		szValue.clear();

		GetPacketData( 4 );
		nLength = GetDataToUInt( m_tempBuffer, true);		
		GetPacketData( nLength, szKey );

		GetPacketData( 4 );
		nLength = GetDataToUInt( m_tempBuffer, true);		
		GetPacketData( nLength, szValue );

		mapSuccessFhs[ szKey ] = szValue;
	}

	// 2.2 실패 목록의 Count 정보 수신
	if( GetPacketData( 4 ) == false )
	{
		LOG( LERR, "Failed count receive failed.");
		return -1;
	}
	nCount = GetDataToUInt( m_tempBuffer, true);
	for( unsigned int i = 0; i < nCount; i++)
	{
		szKey.clear();
		szValue.clear();

		GetPacketData( 4 );
		nLength = GetDataToUInt( m_tempBuffer, true);		
		GetPacketData( nLength, szKey );

		GetPacketData( 4 );
		nLength = GetDataToUInt( m_tempBuffer, true);		
		GetPacketData( nLength, szValue );

		mapFailFhs[ szKey ] = szValue;
	}

	return 1;
}

/// @brief 대상과 연결된 Socket 을 통해 Transfer Daemon 으로 File Cache 명령 전송.
/// @param szFileName [in] Cache 처리할 원본 Source 파일명 (/stg/node0/186/abcde..)
/// @param nFileSize  [in] Cache 처리할 원본 Source 파일의 크기.
/// @param nFileHashCheckLevel [in] File 에 대한 Hash Check Level 정보 ( conf 파일에 지정됨)
/// @param bUseInternalIp [in] 내부망을 이용하여 파일 송수신을 수행할지 여부 \n
///<                      해당값을 true 로 지정시 ftsd 상에서 내부망을 우선 사용하여 파일 복제 시도
///<                      만약 내부망 사용 불가시 자동으로 외부망 사용.
/// @param vecTargetFhs [in] Cache 대상 Target FHS Host Name 정보
/// @return Cache 요청 메시지 전송 성공시 true, 실패시 false 반환.
bool CFtsdSocketControl::SendFileCacheRequest(const std::string& szFileName, unsigned long long nFileSize
		, int nFileHashCheckLevel, bool bUseInternalIp, std::vector< std::string >& vecTargetFhs)
{
	if( IsValidSocket() == false )
		return false;
	
	// 정보 요청을 위한 Packet Header 생성.
	struct FileTransferPacketHeader stHeader;
	memset(&stHeader, 0x00, sizeof(struct FileTransferPacketHeader));

	stHeader.stx			= HEADER_STX_CODE;
	stHeader.type			= HEADER_TYPE_REQUEST; 
	stHeader.command[0]		= CONTROL_FILE_CACHE;

	// Data 부분의 길이를 계산한다.
	unsigned int nTemp = 0;

	nTemp  = 4 + szFileName.size();		// File Name ( 4 + 가변 )
	nTemp += 8;							// File Size ( 8 Byte )
	nTemp += 4;							// Target Tran ID ( 4 + 실제값은 사용 안하므로 0 )
	nTemp += 4;							// File Hash check Level ( 4 Byte )
	nTemp += 1;							// 내부망 IP 사용 여부 ( 1 Byte )
	nTemp += 4;							// FHS Count ( 4 Byte )

	std::vector< std::string >::const_iterator it;
	for( it = vecTargetFhs.begin(); it != vecTargetFhs.end() ; it++ )
	{
		nTemp = nTemp + 4 + it->size() ;	// Target FHS 정보 ( 4 + 가변 )
	}

	stHeader.data_length = htonl( nTemp );

	// Packet Header 정보 전송
	if( WriteN( &stHeader, m_nPacketHeaderLen ) != m_nPacketHeaderLen )
	{
		LOG( LERR, "File Cache request send failed.");
		return false;
	}

	// Packet Data 부분 전송
	
	// File Name 부분
	nTemp = htonl( szFileName.size() );
	WriteN( &nTemp, 4);
	WriteN( szFileName.c_str(), szFileName.size() );

	// File Size 전송
	WriteN( &nFileSize, 8 );

	// Target Tran ID 부분
	nTemp = htonl( 0 );
	WriteN( &nTemp, 4);

	// File Hash Check Level 전송
	nTemp = htonl( nFileHashCheckLevel );
	WriteN( &nTemp, 4);

	// 내부망 사용 여부 전달
	if( bUseInternalIp == true )
		m_tempBuffer[0] = 0x01;
	else
		m_tempBuffer[0] = 0x00;

	WriteN( m_tempBuffer, 1);

	// Target FHS Count 정보 전달.
	nTemp = htonl( vecTargetFhs.size() );
	WriteN( &nTemp, 4);

	// Loop 를 돌면서 각 FHS 정보를 전달한다.
	for( it = vecTargetFhs.begin(); it != vecTargetFhs.end() ; it++ )
	{
		nTemp = htonl( it->size() );
		WriteN( &nTemp, 4);
		WriteN( it->c_str(), it->size());
	}

	return true;
}

/// @brief File Cache 요청에 대한 처리 결과를 transfer daemon 으로 부터 수신한다. 
/// @param nTimeout [in] 함수 호출에 대한 Blocking 을 방지하기 위한 Timeout 설정값. 단위( second )\n
///<                     지정된 시간동안 응답을 수신하지 못한 경우 0 을 반환.
/// @param bSuccess [out] File Cache 처리가 정상적으로 수행되었는지 여부 \n
///<                      trnsfer 상에서 오류가 발생한 경우 이 값은 false 가 되며 \n
///<                      szErrorMessage 변수상에 오류내용이 저장됨. \n
///<                      정상적으로 처리된 경우 true 가 저장되며 \n
///<                      mapSuccessFhs, mapFailFhs 상에 관련 정보가 저장됨.
/// @param szErrorMessage [out] bSuccess == false 인 경우 발생된 오류메시지 정보를 저장
/// @param mapSuccessFhs [out] bSuccess == true 인 경우 \n
///<                           Cache 처리에 성공한 FHS 및 저장된 File 이름 정보를 저장
/// @param mapFailFhs [out] bSuccess == true 인 경우 \n
///<                        Cache 처리에 실패한 FHS Host Name 및 발생된 오류메시지 정보를 저장.
/// @return -1 : Socket 통신 관련 오류 발생하여 Transfer Daemon 과 연결이 끊어진 경우 \n
///<             => ftsd 와 연결이 끝어진것으로 판단하고 오류처리한다. \n
///<         0 : 입력변수인 nTimeout 에 지정된 시간안에 Transfer Daemon으로부터
///<             Cache 에 대한 처리 결과 정보를 수신하지 못한 경우. \n
///<             이 기능은 본 함수 호출시 Blocking 발생을 막기 위한 기능으로서 \n
///<             이 값이 반환된 경우 추가 다른 작업을 진행한 후 본 함수를 다시 호출하여 \n
///<             응답을 대기할 수 있다. \n
///<         1 : 파일 Cache 처리에 대한 응답을 수신한 경우, Output 인자인 bSuccess 를 확인하여 처리 결과 확인
///<         2 : Transfer Daemon 으로 부터 Data 을 수신하였으나 File Cache 요청에 대한 응답이 아닌 경우 \n
///<             해당 정보는 본 클래스의 멤버변수 상에 내부적으로 저장된다. ( 단 Alive Packet 은 아님) \n
///<             본 결과가 수신된 경우 무시 처리하고 계속 응답을 대기하면 된다.
int CFtsdSocketControl::GetFileCacheResult( int nTimeout, bool& bSuccess, std::string& szErrorMessage
		, std::map< std::string, std::string>& mapSuccessFhs
		, std::map< std::string, std::string>& mapFailFhs )
{
	// Cache 처리 요청에 대한 응답 패킷 대기 
	// 수신 대기 Timeout 은 전달받은 값으로 설정.
	if( IsValidSocket() == false )
		return -1;
	
	int nRead;
	bool isAlivePacket;
	do
	{
		isAlivePacket = false;

		// Socket 으로 부터 Packet Header 부분 수신.
		// 수신된 정보는 멤버변수에 저장처리.
		nRead = ReadNTimeout( &m_packetHeader, m_nPacketHeaderLen, nTimeout );
	
		// nRead  0: Socket Closed, -1: error , -2: Timeout 이므로
		if( nRead == -2 )
			return 0;		// Timeout 반환.
		else if( nRead == 0 || nRead == -1 )
			return -1;		// Socket Close 또는 오류 발생시
	
		// STX code 검사.
		if( m_packetHeader.stx != HEADER_STX_CODE )
		{
			LOG( LERR, "Not valid stx code.");
			return -1;
		}

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

		// Alive Check Packet 인 경우 해당 패킷은 무시처리 후 다시 수신 처리한다.
		if( m_packetHeader.type == HEADER_TYPE_REQUEST
				&& m_packetHeader.command[0] == COMMON_ALIVE_CHECK 
				&& m_nPacketDataLen == 0 )
		{
			isAlivePacket = true;
		}

	} while( isAlivePacket == true );

	// Alive Packet 이 아닌 경우 해당 Packet 에 대한 Cache 처리 요청에 대한 응답패킷인지 검사.
	if( m_packetHeader.type != HEADER_TYPE_RESPONSE	
			|| m_packetHeader.command[0] != CONTROL_FILE_CACHE )
	{
		// 다른 패킷이 들어온 경우 

		// ** 정석대로라면 그냥 냅둬어 수신부에서 처리해야 하지만
		// 현재 수신 처리부가 없어 당장 문제가 생길수 있으므로 
		// 데이터 부분까지 수신하여 임시버퍼에 저장처리 해 놓는다. 
		GetPacketData( m_nPacketDataLen );
		return 2;
	}

	// Cache 요청에 대한 응답 패킷인 경우

	// 1. 성공, 실패 여부 판단.
	if( m_packetHeader.result[0] != HEADER_RESULT_SUCCESS )
	{
		// 오류가 발생한 경우임
		bSuccess = false;

		// Packet Data 상에서 오류 메시지를 수신받아 반환 처리.
		if( GetPacketData( m_nPacketDataLen, szErrorMessage ) == false )
		{
			LOG( LERR, "Error msg receive failed.");
			return -1;
		}
		else
			return 1;
	}

	// 2. 성공인 경우
	bSuccess = true;
	
	unsigned int nCount;
	unsigned int nLength;
	std::string szKey;
	std::string szValue;

	// 2.1 성공 목록의 Count 정보 수신
	if( GetPacketData( 4 ) == false )
	{
		LOG( LERR, "Success count receive failed.");
		return -1;
	}
	nCount = GetDataToUInt( m_tempBuffer, true);
	for( unsigned int i = 0; i < nCount; i++)
	{
		szKey.clear();
		szValue.clear();

		GetPacketData( 4 );
		nLength = GetDataToUInt( m_tempBuffer, true);		
		GetPacketData( nLength, szKey );

		GetPacketData( 4 );
		nLength = GetDataToUInt( m_tempBuffer, true);		
		GetPacketData( nLength, szValue );

		mapSuccessFhs[ szKey ] = szValue;
	}

	// 2.2 실패 목록의 Count 정보 수신
	if( GetPacketData( 4 ) == false )
	{
		LOG( LERR, "Failed count receive failed.");
		return -1;
	}
	nCount = GetDataToUInt( m_tempBuffer, true);
	for( unsigned int i = 0; i < nCount; i++)
	{
		szKey.clear();
		szValue.clear();

		GetPacketData( 4 );
		nLength = GetDataToUInt( m_tempBuffer, true);		
		GetPacketData( nLength, szKey );

		GetPacketData( 4 );
		nLength = GetDataToUInt( m_tempBuffer, true);		
		GetPacketData( nLength, szValue );

		mapFailFhs[ szKey ] = szValue;
	}

	return 1;
}

/// @brief 대상과 연결된 Socket 을 통해 ftsd 로 Content Check 명령 전송.
bool CFtsdSocketControl::SendFileCheckRequest(const std::string& szFileName, bool bHashCheck, unsigned long long nHashCheckSize )
{
	if( IsValidSocket() == false )
		return false;
	
	// 정보 요청을 위한 Packet Header 생성.
	struct FileTransferPacketHeader stHeader;
	memset(&stHeader, 0x00, sizeof(struct FileTransferPacketHeader));

	stHeader.stx			= HEADER_STX_CODE;
	stHeader.type			= HEADER_TYPE_REQUEST; 
	stHeader.command[0]		= CONTROL_FILE_CHECK;

	// Data 부분의 길이를 계산한다.
	unsigned int nTemp = 0;

	nTemp  = 4 + szFileName.size();			// File Name ( 4 + 가변 )
	nTemp += 4;								// File Hash 추출 여부 ( 4 Byte )

	if( bHashCheck == true )
	{
		nTemp += 8;							// File Hash 추출 Size 정보 ( 8 Byte )
	}

	stHeader.data_length = htonl( nTemp );

	// Packet Header 정보 전송
	if( WriteN( &stHeader, m_nPacketHeaderLen ) != m_nPacketHeaderLen )
	{
		LOG( LERR, "File[%s] Check request send failed.", szFileName.c_str() );
		return false;
	}

	// Packet Data 부분 전송
	
	// 1. File Name 부분
	nTemp = htonl( szFileName.size() );
	WriteN( &nTemp, 4);
	WriteN( szFileName.c_str(), szFileName.size() );

	// 2. Hash 추출 여부
	if( bHashCheck == true )
	{
		nTemp = htonl( 1 );
		WriteN( &nTemp, 4);

		// 3. Hash 추출 size (MByte 단위 ) 값 전송.
		WriteN( &nHashCheckSize, 8 );
	}
	else
	{
		nTemp = htonl( 0 );
		WriteN( &nTemp, 4);
	}

	return true;
}



/// @brief 대상과 연결된 Socket 을 통해 ftsd 로 Unlink 명령 전송.
bool CFtsdSocketControl::SendFileUnlinkRequest(const std::string& szFileName, unsigned long long nFileSize, bool bForceUnlink )
{
	if( IsValidSocket() == false )
		return false;
	
	// 정보 요청을 위한 Packet Header 생성.
	struct FileTransferPacketHeader stHeader;
	memset(&stHeader, 0x00, sizeof(struct FileTransferPacketHeader));

	stHeader.stx			= HEADER_STX_CODE;
	stHeader.type			= HEADER_TYPE_REQUEST; 
	stHeader.command[0]		= CONTROL_FILE_UNLINK;

	// Data 부분의 길이를 계산한다.
	unsigned int nTemp = 0;

	nTemp  = 4 + szFileName.size();			// File Name   ( 4 + 가변 )
	nTemp += 8;								// File Size   ( 8 Byte )
	nTemp += 4;								// Unlink Mode ( 4 Byte )

	stHeader.data_length = htonl( nTemp );

	// Packet Header 정보 전송
	if( WriteN( &stHeader, m_nPacketHeaderLen ) != m_nPacketHeaderLen )
	{
		LOG( LERR, "File[%s] Unlink request send failed.", szFileName.c_str() );
		return false;
	}

	// Packet Data 부분 전송
	
	// 1. File Name 부분
	nTemp = htonl( szFileName.size() );
	WriteN( &nTemp, 4);
	WriteN( szFileName.c_str(), szFileName.size() );

	// 2. File Size 부분 
	WriteN( &nFileSize, 8 );

	// 3. Unlink Mode 정보
	if( bForceUnlink == true )
	{
		nTemp = htonl( UNLINK_MODE_FORCE );
	}
	else
	{
		nTemp = htonl( UNLINK_MODE_NORMAL );
	}
	WriteN( &nTemp, 4);


	return true;
}

/// @brief File Check 요청에 대한 처리 결과를 ftsd 로 부터 수신한다. 
int CFtsdSocketControl::GetFileCheckResult( int nTimeout, bool& bSuccess, std::string& szErrorMessage, unsigned long long& nFileSize, std::string& szHashValue )
{
	if( IsValidSocket() == false )
		return -1;

	int nRead;
	bool isAlivePacket;
	do
	{
		isAlivePacket = false;

		// Socket 으로 부터 Packet Header 부분 수신.
		// 수신된 정보는 멤버변수에 저장처리.
		nRead = ReadNTimeout( &m_packetHeader, m_nPacketHeaderLen, nTimeout );
	
		// nRead  0: Socket Closed, -1: error , -2: Timeout 이므로
		if( nRead == -2 )
			return 0;		// Timeout 반환.
		else if( nRead == 0 || nRead == -1 )
			return -1;		// Socket Close 또는 오류 발생시
	
		// STX code 검사.
		if( m_packetHeader.stx != HEADER_STX_CODE )
		{
			LOG( LERR, "Not valid stx code.");
		
			return -1;
		}

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

		// Alive Check Packet 인 경우 해당 패킷은 무시처리 후 다시 수신 처리한다.
		if( m_packetHeader.type == HEADER_TYPE_REQUEST
				&& m_packetHeader.command[0] == COMMON_ALIVE_CHECK 
				&& m_nPacketDataLen == 0 )
		{
			isAlivePacket = true;
		}

	} while( isAlivePacket == true );

	// Alive Packet 이 아닌 경우 해당 Packet 에 대한 Check에 대한 응답 패킷인지 검사.
	if( m_packetHeader.type != HEADER_TYPE_RESPONSE	
			|| m_packetHeader.command[0] != CONTROL_FILE_CHECK )
	{
		// 다른 패킷이 들어온 경우 

		// ** 정석대로라면 그냥 냅둬어 수신부에서 처리해야 하지만
		// 현재 수신 처리부가 없어 당장 문제가 생길수 있으므로 
		// 데이터 부분까지 수신하여 임시버퍼에 저장처리 해 놓는다. 
		GetPacketData( m_nPacketDataLen );
		return 2;
	}

	// Check 에 대한 응답 패킷이 경우.

	// 1. 성공, 실패 여부 판단.
	if( m_packetHeader.result[0] != HEADER_RESULT_SUCCESS )
	{
		// 오류가 발생한 경우임
		bSuccess = false;

		// Packet Data 상에서 오류 메시지를 수신받아 반환 처리.
		if( GetPacketData( m_nPacketDataLen, szErrorMessage ) == false )
		{
			LOG( LERR, "Error msg receive failed.");

			return -1;
		}
		else
			return 1;
	}

	// 2. 성공인 경우
	bSuccess = true;

	// 2.1 File Size 정보 추출..
	if( GetPacketData( 8 ) == false )
	{
		LOG( LERR, "File size receive fail.");		
		return -1;
	}
	nFileSize = GetDataToUInt64( m_tempBuffer );
	
	// 2.2 File Hash 필드의 Length 정보 추출.
	if( GetPacketData( 4 ) == false )
	{
		LOG( LERR, "Hash length receive fail.");
		return -1;
	}
	
	unsigned int nLength = GetDataToUInt( m_tempBuffer, true);

	if( nLength > 0 )
	{
		// 추출된 Hash 정보 저장.
		GetPacketData( nLength, szHashValue );
	}

	return 1;
}


/// @brief File Unlink 요청에 대한 처리 결과를 ftsd 로 부터 수신한다. 
int CFtsdSocketControl::GetFileUnlinkResult( int nTimeout, bool& bSuccess, std::string& szErrorMessage )
{

	if( IsValidSocket() == false )
		return -1;

	int nRead;
	bool isAlivePacket;
	do
	{
		isAlivePacket = false;

		// Socket 으로 부터 Packet Header 부분 수신.
		// 수신된 정보는 멤버변수에 저장처리.
		nRead = ReadNTimeout( &m_packetHeader, m_nPacketHeaderLen, nTimeout );
	
		// nRead  0: Socket Closed, -1: error , -2: Timeout 이므로
		if( nRead == -2 )
			return 0;		// Timeout 반환.
		else if( nRead == 0 || nRead == -1 )
			return -1;		// Socket Close 또는 오류 발생시
	
		// STX code 검사.
		if( m_packetHeader.stx != HEADER_STX_CODE )
		{
			LOG( LERR, "Not valid stx code.");
		
			return -1;
		}

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

		// Alive Check Packet 인 경우 해당 패킷은 무시처리 후 다시 수신 처리한다.
		if( m_packetHeader.type == HEADER_TYPE_REQUEST
				&& m_packetHeader.command[0] == COMMON_ALIVE_CHECK 
				&& m_nPacketDataLen == 0 )
		{
			isAlivePacket = true;
		}

	} while( isAlivePacket == true );

	// Alive Packet 이 아닌 경우 해당 Packet 에 대한 Unlink 에 대한 응답 패킷인지 검사.
	if( m_packetHeader.type != HEADER_TYPE_RESPONSE	
			|| m_packetHeader.command[0] != CONTROL_FILE_UNLINK )
	{
		// 다른 패킷이 들어온 경우 

		// ** 정석대로라면 그냥 냅둬어 수신부에서 처리해야 하지만
		// 현재 수신 처리부가 없어 당장 문제가 생길수 있으므로 
		// 데이터 부분까지 수신하여 임시버퍼에 저장처리 해 놓는다. 
		GetPacketData( m_nPacketDataLen );
		return 2;
	}

	// Unlink 에 대한 응답 패킷이 경우.

	// 1. 성공, 실패 여부 판단.
	if( m_packetHeader.result[0] != HEADER_RESULT_SUCCESS )
	{
		// 오류가 발생한 경우임
		bSuccess = false;

		// Packet Data 상에서 오류 메시지를 수신받아 반환 처리.
		if( GetPacketData( m_nPacketDataLen, szErrorMessage ) == false )
		{
			LOG( LERR, "Error msg receive failed.");

			return -1;
		}
		else
			return 1;
	}

	// 2. 성공인 경우
	bSuccess = true;

	// 2.1 성공인 경우 Data 부분이 존재하지 않으므로.. Pass..
	
	return 1;
}


///////////////////////////////////////////////////////////////////////////////////////

/// @brief Packet Header 부분의 수신 처리를 위한 함수.
/// @param timeout [in] 대기시간.
/// @retrun 성공시 true, 오류 발생및 실패시 fasle 반환.
bool CFtsdSocketControl::GetPacketHeader( int timeout )
{
	if( IsValidSocket() == false )
		return false;

	int nRead;
	bool isAlivePacket;
	do
	{
		isAlivePacket = false;

		// Socket 으로 부터 Packet Header 부분 수신.
		// 수신된 정보는 멤버변수에 저장처리.
		nRead = ReadNTimeout( &m_packetHeader, m_nPacketHeaderLen, timeout );
	
		// 오류 발생시 
		if( nRead <= 0 )
			return false;
	
		// STX code 검사.
		if( m_packetHeader.stx != HEADER_STX_CODE )
		{
			LOG( LERR, "Not valid stx code.");
			return false;
		}

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

		// Alive Check Packet 인 경우 해당 패킷은 무시처리 후 다시 수신 처리한다.
		if( m_packetHeader.type == HEADER_TYPE_REQUEST
				&& m_packetHeader.command[0] == COMMON_ALIVE_CHECK 
				&& m_nPacketDataLen == 0 )
		{
			isAlivePacket = true;
		}

	} while( isAlivePacket == true );

	return true;

}

/// @brief socket 에서 지정된 크기만큼의 데이터를 읽어 출력변수에 저장처리.
/// @param size [in] read 할 데이터 크기
/// @param value [out] 읽은 데이터를 저장할 string 변수
/// @return On success return true, otherwise return false.
bool CFtsdSocketControl::GetPacketData( unsigned int& size, std::string& value )
{
	// 1. 수신할 크기가 임시버퍼보다 큰 경우 새로운 버퍼를 생성한다.
	BYTE * pBuffer = NULL;
	bool bNewBufferCreated = false;
	if( size > DEFAULT_SOCKET_TEMP_BUFFER_SIZE )
	{
		pBuffer = new BYTE[size];	// 신규 메모리 할당.
		bNewBufferCreated = true;
	}
	else
		pBuffer = m_tempBuffer;

	// 2. 데이터 수신 처리.
	int nRead = ReadNTimeout( pBuffer, size );
	
	// 오류 발생시
	if( nRead <= 0 )
	{
		// 신규로 생성된 버퍼인 경우 메모리 해제 처리.
		if( bNewBufferCreated == true )
			delete [] pBuffer;

		return false;
	}

	// 3. 수신 데이터 저장 처리 - 기존 데이터 뒤에 붙여 준다=> 이게 사용하기 편함.
	value.append( (char *)pBuffer, size );

	// 신규로 생성된 버퍼인 경우 메모리 해제 처리.
	if( bNewBufferCreated == true )
		delete [] pBuffer;

	return true;
}

/// @brief socket 에서 지정된 크기만큼의 데이터를 읽어 내부 임시버퍼인 m_tempBuffer 에 저장처리.
/// @param size [in] read 할 데이터 크기
/// @return On success return true, otherwise return false.
bool CFtsdSocketControl::GetPacketData( unsigned int size )
{
	// 1. 수신할 크기가 임시버퍼보다 큰 경우 오류 반환.
	if( size > DEFAULT_SOCKET_TEMP_BUFFER_SIZE )
		return false;

	// 2. 데이터 수신 처리.
	int nRead = ReadNTimeout( m_tempBuffer, size );
	
	// 오류 발생시
	if( nRead <= 0 )
		return false;

	// 3. 정상인 경우는 ReadNTimeout 함수가 무조건 요청 크기만큼 read 처리하므로 정상수신임.
	return true;
}

/// @brief 전달받은 데이터를 unsigned int 형으로 변환처리 및 Endian 변환
unsigned int CFtsdSocketControl::GetDataToUInt( BYTE * pValue, bool bConvertEndian )
{
	BYTE tempBuffer[4];
	unsigned int result;

	memcpy(tempBuffer, pValue, 4);
	unsigned int * pInt = (unsigned int *)tempBuffer;

	// Endian 변환처리.
	if( bConvertEndian == true )
	{
		result = ntohl( *pInt );
	}
	else
		result = *pInt;

	return result;
}

unsigned long long CFtsdSocketControl::GetDataToUInt64( BYTE * pValue )
{
	unsigned long long * pResult;
	BYTE tempBuffer[8];
	memcpy(tempBuffer, pValue, 8);

	pResult = (unsigned long long *)tempBuffer;

	return  *pResult;
}

