#include "GetSource.h"
#include "Logger.h"
#include "ProcessSocketControl.h"
#include "ServiceConfig.h"
#include "RcSyncdRequestData.h"

#define REQUEST_TIME_OUT_DIFF	(120*60)
#define REQUEST_TIME_OUT		(5)

CGetSource::CGetSource(CSyncInfo &info)
	: m_syncinfo(info)
{
}

CGetSource::~CGetSource()
{
}

bool CGetSource::GetRemote()
{
	int nResult;
	// Slave rc_syncd 접속 및 content 정보 요청
	CProcessSocketControl slave;
	if( slave.ConnectTarget(m_syncinfo.sync_rcts, CServiceConfig::GetInstance()->GetOperationPort()) == false )
	{
		_LOG( LERR, "Connect failed to slave, %s", m_syncinfo.sync_rcts.c_str() );
	}
	
	CReqSlaveData request;
	request.sync_type = m_syncinfo.sync_type;
	request.start_time = m_syncinfo.start_time;
	request.end_time = m_syncinfo.end_time;
	request.master = m_syncinfo.sync_master;
	request.slave = m_syncinfo.sync_slave;
	
	if( slave.SendRequestContentListToSlave( request ) == false )
	{
		// 전송 실패시
		_LOG( LERR, "ContentList request send fail.(=> slave)");
		slave.Close();
		return false;
	}
	
	// check request result 
	// timeout check
	time_t timeStart;
	time_t timeNow;
	timeStart = time(&timeStart);

	// 루프를 돌면서 요청에 대한 응답을 대기
	while(1)
	{
		bool bResultSuccess = false;	// 처리 결과의 성공/실패 여부를 저장하기 위한 변수.
		std::string strMessage;   		// 수신 메시지 저장 변수.
		
		// diff time 120분
		if ( difftime(time(&timeNow), timeStart) > REQUEST_TIME_OUT_DIFF )
		{ // REQUEST_TIME_OUT_DIFF
			LOG(LERR, "Content List Request timeout from slave. [Master svcid:%s]", request.master.c_str());

			slave.Close();
			return false;
		}
		// nTimeout 에 지정된 시간동안 응답대기	
		nResult = slave.RecvResultContentListFromSlave( REQUEST_TIME_OUT, bResultSuccess, m_syncinfo.file_slave_name, strMessage);
        
		if( nResult == -1 )
		{
			// Socket 통신 관련 오류 또는 접속 종료가 발생한 경우.
			// 해당 내역 로깅 및 루프 종료
			_LOG( LERR, "Response wait fail by socket" );
			
			slave.Close();
			return false;
		}
		else if( nResult == 0 )
		{
			// 지정된 시간 동안 응답대기 중 처리 결과 정보가 아직 수신되지 않은 경우 => Alive Check 패킷 한번 쏘고 다시 Loop 로
			
			if( slave.SendAliveCheck() == false )
			{
				// Alive 전송 실패시 => Socket 종료 및 오류가 발생한 경우임.
				_LOG( LERR, "Response wait fail by socket2");
				slave.Close();
				return false;
			}

			LOG( LDBG, "Response wait");			
			// 정상적인 경우 다시 응답대기.
			continue;
		}
		else if( nResult == 1 )
		{
			// 요청에 대한 처리결과 정보가 수신된 경우.
			// 해당 정보 로깅처리.
			
			if( bResultSuccess == true )
			{
				// 요청에 대한 처리가 정상적으로 처리된 경우				
				_LOG( LINF, "Sync result SUCCESS [%s]", strMessage.c_str() );
			
			}
			else
			{
				// 오류 발생시 
				_LOG( LINF, "Sync result ERROR [%s]", strMessage.c_str() );
				slave.Close();
				return false;
			}			
			break;	// 응답을 받았으니 응답 대기 루프 종료
		}
		else
		{
			// Replication 요청에 대한 응답패킷이 아닌 경우.
			// 해당 패킷은 무시하고 다시 Loop 로 
			continue;
		}
	}
	
	slave.Close();
	return true;
}

bool CGetSource::GetSelf(bool isMaster, CSourceData & self)
{
	if (self.Init(isMaster, m_syncinfo) == false)
	{
		LOG(LERR, "Failed Source date initialization.");
		return false;
	}

	//// data 추출 시작
	if (self.Execute() == false)
	{
		LOG(LERR, "Failed Source date execute.");
		return false;
	}

	return true;
}

bool CGetSource::GetSource(GET_TYPE	t)
{
	switch (t)
	{
	case CGetSource::BOTH:
	case CGetSource::SELF_MASTER_ONLY:
		{
			int nRetry = 0;
			bool bRemoteSuccess= false;
			// master source 데이터 자기 자신
			CSourceData master;
			if (GetSelf(true, master) == false)
			{
				LOG(LERR, "Failed GetSource(Slave).");
				return false;
			}
			
			if (t == CGetSource::BOTH)
			{
				// Slave에 동기화 대상 파일 요청
				do
				{
					bRemoteSuccess = GetRemote();
					if( bRemoteSuccess == true)
					{
						break;
					}
					++nRetry;
				}while(nRetry > 3); // 재시도 3회
			}
			
			//// data 추출 완료 대기
			pthread_join(*master.GetThreadHandle(), NULL);
			
			if( bRemoteSuccess == false )
			{
				return false;
            }
			//// 작업 성공 유무 체크
			string errmsg;
			if (master.isError(errmsg) == true)
			{
				LOG(LERR, "Failed Master Source date [%s].", errmsg.c_str());
				return false;
			}
		}
		break;
	case CGetSource::SELF_SLAVE_ONLYE:
		{
			CSourceData slave;
			if (GetSelf(false, slave) == false)
			{
				LOG(LERR, "Failed GetSource(Slave).");
				return false;
			}
			//// data 추출 완료 대기
			pthread_join(*slave.GetThreadHandle(), NULL);

			//// 작업 성공 유무 체크
			string errmsg;
			if (slave.isError(errmsg) == true)
			{
				LOG(LERR, "Failed Source date [%s].", errmsg.c_str());
				return false;
			}
		}
		break;
	default:
		LOG(LERR, "Unknown GetSouce Type.");
		return false;
		break;
	}

	return true;
}
