#include <stdlib.h>
#include <string>
#include <map>
#include <iterator>

#include "Logger.h"
#include "FtsdSocketControl.h"



#define LOG_PATH				"/user/service/logs"	// Log 파일은 /user/service/logs/sample/sample_날짜.log 로 생성됨.

#define TRANSFER_LISTEN_PORT	14001					// ftsd 의 Listen Port



int main()
{
	///////////// 1. 기본 정보 설정 ///////////////////
	
	// Source 장비 Host Name
	std::string szSourceHostName		= "bd-16-fhs001.ktsh.co.kr";	
	
	// Source 파일명 ( Mount 위치까지 포함함 전체 경로 )
	//std::string szSourceFileNameHash	= "/stg/node1/186/4b2d62b6357b548ac984894a12630e25";
	//std::string szSourceFileNameHash	= "/user2/dav_storage/186/00ace1da4678aeb61e7aac6708dcdcaa";
	std::string szSourceFileNameHash	= "/stg/node0/1/huibong.txt";
	
	// Source 파일의 크기 ( 만약 일치하지 않을 경우 ftsd 에서 오류로 처리함)
	//unsigned long long nSourceFileSize	= 100000000;
	unsigned long long nSourceFileSize	= 42;

	// Target FHS Host Name 정보 ( 다중 Target 사용 가능으로 인해 vector 사용)
	std::vector< std::string > vecTarget;
	//vecTarget.push_back( "bd-16-fhs002.ktsh.co.kr" );
	//vecTarget.push_back( "bd-16-fhs003.ktsh.co.kr" );
	//vecTarget.push_back( "222.122.153.81" );
	vecTarget.push_back( "222.122.153.82" );
	
	//vecTarget.push_back( "bd-04-fhs003.ktsh.co.kr" );
	//vecTarget.push_back( "bd-04-fhs004.ktsh.co.kr" );
	//vecTarget.push_back( "bd-04-fhs005.ktsh.co.kr" );
	//vecTarget.push_back( "bd-04-fhs006.ktsh.co.kr" );



	///////////// 2. 옵션 설정 //////////////

	// 전송완료후 Hash 검사 Level, 0: 안함, 1: 앞뒤 1Mbyte만 검사, 2: 파일 전체검사
	int nFileHashCheckLevel	= 1;		
	
	// 파일 전송시 내부망을 사용할지 여부, RC 내 처리시에는 true 설정, RC-RC 간 동작시에는 효율성을 위해 false 설정할 것.
	bool bUseInternalIp		= true;	
	
	

	////////////// Sample 코드 //////////////

	// Log 처리용 객체 생성 및 초기화.
	if( CLogger::Init( PROG_NAME, LOG_PATH, LDBG ) == false )
	{
		std::cout << "[error] Logger object created failed.." << std::endl;
		return 0;
	}


	// Start logging
	_LOG( LINF, "ftsd sample Start: Source[%s][%s][%llu]", szSourceHostName.c_str(), szSourceFileNameHash.c_str(), nSourceFileSize );


	// ftsd 와  통신을 처리할 Socket Control 객체 생성 및 접속
	CFtsdSocketControl ftsdSocket;
	if( ftsdSocket.ConnectTarget( szSourceHostName, TRANSFER_LISTEN_PORT ) == false )
	{
		// Source ftsd 으로 접속 실패시
		_LOG( LERR, "Source FHS connect fail. [%s][%d]", szSourceHostName.c_str(), TRANSFER_LISTEN_PORT );
		return EXIT_FAILURE;
	}
	
	// Source ftsd 로 파일에 대한 Cache 처리 요청 
	if( ftsdSocket.SendFileCacheRequest( szSourceFileNameHash, nSourceFileSize, nFileHashCheckLevel, bUseInternalIp, vecTarget ) == false )
	{
		// Source ftsd 로 Replication 요청 전송 실패시
		_LOG( LERR, "Cache Request send fail.[%s][%s][%llu]", szSourceHostName.c_str(), szSourceFileNameHash.c_str(), nSourceFileSize);
		return EXIT_FAILURE;
	}


	// Replication 요청에 대한 응답 대기
	int nResult = 0;

	int nTimeout = 5;										// 응답 대기 시간 ( sec )
	bool bResultSuccess;									// 처리 결과의 성공/실패 여부를 저장하기 위한 변수.
	std::string szResultErrorMessage;						// bResultSuccess= fale 인 경우 오류메시지를 저장하기 위한 변수.
	std::map< std::string, std::string> mapSuccessInfo;		// 성공한 FHS Host명 및 파일정보를 저장하기 위한 list
	std::map< std::string, std::string> mapFailInfo;		// 실패한 FHS Host명 및 각 FHS별 오류메시지를 저장하기 위한 list

	// 루프를 돌면서 Replication 요청에 대한 응답을 대기
	while(1)
	{

		// nTimeout 에 지정된 시간동안 응답대기 시도
		nResult = ftsdSocket.GetFileCacheResult( nTimeout, bResultSuccess, szResultErrorMessage, mapSuccessInfo, mapFailInfo );


		if( nResult == -1 )
		{
			// Socket 통신 관련 오류 또는 접속 종료가 발생한 경우.
			// 해당 내역 로깅 및 루프 종료
			_LOG( LERR, "ftsd Response wait fail by socket [%s][%s]", szSourceHostName.c_str(), szSourceFileNameHash.c_str() );
			break;

		}
		else if( nResult == 0 )
		{
			// 지정된 시간 동안 응답대기 중 처리 결과 정보가 아직 수신되지 않은 경우 => Allive Check 패킷 한번 쏘고 다시 Loop 로

			if( ftsdSocket.SendAliveCheck() == false )
			{
				// Alive 전송 실패시 => Socket 종료 및 오류가 발생한 경우임.
				_LOG( LERR, "ftsd Response wait fail by socket2 [%s][%s]", szSourceHostName.c_str(), szSourceFileNameHash.c_str() );
				break;
			}

			// 정상적인 경우 다시 응답대기.
			continue;

		}
		else if( nResult == 1 )
		{
			// 요청에 대한 처리결과 정보가 수신된 경우.
			// 해당 정보 로깅처리.

			if( bResultSuccess == true )
			{
				// 요청에 대한 처리가 일단은 수행된 경우 => 성공한 FHS 정보 와 실패한 FHS 정보가 나타남.

				std::map< std::string, std::string>::const_iterator it;
				// 성공한 FHS 목록 
				for( it = mapSuccessInfo.begin(); it != mapSuccessInfo.end(); it++ )
				{
					// 성공한 경우 Target FHS Host Name 및 저장된 File Full Name 정보가 표시됨.
					_LOG( LINF, "Result SUCCESS [%s][%s]", (it->first).c_str(), (it->second).c_str() );
				}

				// 실패한 FHS 목록
				for( it = mapFailInfo.begin(); it != mapFailInfo.end(); it++ )
				{
					// 실패한 경우 Target FHS Host Name 및 오류메시지 정보가 표시됨.
					_LOG( LINF, "Result FAIL [%s][%s]", (it->first).c_str(), (it->second).c_str() );
				}
			}
			else
			{
				// 요청에 대한 처리를 아예 수행할 수 없는 경우 => Source FHS 파일 정보 및 각 정보 검사시 오류가 발생한 경우. ( Target FHS 로 접근 작업 전 )
				// 인자로 전달한 szResultErrorMessage 상에 오류 발생정보가 저장됨.
				_LOG( LINF, "Cache result error [%s][%s][%s]", szSourceHostName.c_str(), szSourceFileNameHash.c_str(), szResultErrorMessage.c_str() );

			}			

			break;	// 응답을 받았으니 응답 대기 루프 종료
		}
		else
		{
			// Replication 요청에 대한 응답패킷이 아닌 경우.
			// 해당 패킷은 무시하고 다시 Loop 로 
			continue;
		}
	}


	// End logging
	_LOG( LINF, "ftsd sample process End [%s][%s][%llu]", szSourceHostName.c_str(), szSourceFileNameHash.c_str(), nSourceFileSize );
	_LOG( LINF, "-------------------------------------------------------------------------------" );

	CLogger::Exit();

	return EXIT_SUCCESS;
}
