#include "ReplicateControl.h"

#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <list>
#include <vector>
#include <string>

#include "Logger.h"
#include "ServiceConfig.h"
#include "ReplicateThread.h"

// 매 Loop 마다 Sleep 시간 정보 정의 ( sec 단위)
#define DEFAULT_SLEEP_TIME  2
#define MAX_SLEEP_TIME      5


// 생성자        
CReplicateControl::CReplicateControl( CProperFhs * pProperFhs
		, CProcessStatus * pProcessStatus
		, CDataQueue<CSourceData> * pQueueSource
		, CDataQueue<CRetryData> * pQueueRetry
		, CDataQueue<CCompleteData> * pQueueComplete )
	: m_pProperFhs( pProperFhs )
		, m_pProcessStatus( pProcessStatus )
		, m_pSourceQueue( pQueueSource )
		, m_pRetryQueue( pQueueRetry )
		, m_pCompleteQueue( pQueueComplete )
{
	// 멤버 변수 초기화
	m_threadHandle = 0;

	m_uSleep = DEFAULT_SLEEP_TIME;
	m_uRetryCount = 2; // 그냥 기본 설정. Init() 함수에서 실제 값으로 설정 처리함.
}

// 소멸자
CReplicateControl::~CReplicateControl()
{
	// Thread 동작 정지 처리
	// - 만약 Thread 가 이미 종료된 경우 m_threadHandle 이 다른 Thread Handle 일 수 있으므로 
	//   업무 Flow 수정시 주의할 것
	if( m_threadHandle != 0 )
		pthread_cancel( m_threadHandle );

}

// 객체 초기화 수행
bool CReplicateControl::Init()
{
	// Report 로그 처리 관련 멤버 객체 초기화.
	// - retry 재시도에 관련된 로깅 처리 목적	
	if( m_reportLog.Init( CServiceConfig::GetInstance()->GetLogPath() ) == false )
	{
		std::string strErrorMessage;
		m_reportLog.GetLastError( strErrorMessage );

		LOG( LERR, "report log init fail. [%s]", strErrorMessage.c_str() );
		return false;
	}
	
	// Queue Pop 제어 관련 멤버 객체 초기화.
	if( m_controlQueuePop.Init( m_pSourceQueue, m_pRetryQueue ) == false )
	{
		LOG( LERR, "CQueuePopControl object init() fail.");
		return false;
	}

	// Retry count 값 설정
	m_uRetryCount = CServiceConfig::GetInstance()->GetMaxRetryCount();

	return true;		
}


// 스레드 내에서 각 loop 단위 처리 호출 함수.
// return true  : 처리할 Data 가 계속 존재하는 경우...
//        false : 처리할 Data 가 없어 idle 한 경우.
bool CReplicateControl::MakeReplication()
{
	// 1. CProcessStatus에서 생성 가능한 Replication Thread  수 확인
	unsigned int nWorkCount = 0;
	nWorkCount = m_pProcessStatus->GetValidReplicationCount();
	if( nWorkCount == 0 )
	{
		// 생성 가능한 Replication Thrad 수가 없는 경우...
		// - false 를 반환할 경우... MAX_SLEEP_TIME 만큼 대기하므로... 
		// - 빠른 시간내에 재시도할 수 있도록 true 를 반환.
		return true;
	}

	// 2. Queue 에서 생성 가능한 Thread 수 만큼 Content 정보 pop 처리.
	std::list<CRetryData> listWork;
	if( m_controlQueuePop.GetWork( nWorkCount, listWork ) == false )
	{
		// Queue 에 작업할 Data 가 존재하지 않는 경우..
		// - idle 상태
		return false;
	}

	// 3. pop 처리된 content 갯수만큼 replication thread 를 생성한다.
	std::list<std::string> listIgnoreFHS;
	std::vector<std::string> vecFHS;
	int nReplicationCount;

	while( listWork.empty() == false )
	{
		// list 에서 Data pop
		CRetryData objRetryData;
		objRetryData = listWork.front();
		listWork.pop_front();

		// 제외 대상 FHS 목록을 설정.
		// - Replication 성공 FHS 와 원본 FHS 를 제외 대상 목록에 포함시킨다.
		listIgnoreFHS.clear();
		listIgnoreFHS = objRetryData.m_listSuccessHost;
		listIgnoreFHS.push_back( objRetryData.m_OriginData.m_szOriginHostName );

		// 복제 횟수 계산.
		// - 객체에 저장된 복제 설정값에서 재시도 처리를 통해 이미 생성 완료된 값을 뺀다.
		nReplicationCount = objRetryData.m_OriginData.m_nReplicationCount - objRetryData.m_listSuccessHost.size();
		if( nReplicationCount <= 0 )
		{
			// 복제 count 가 0보다 작은 경우...
			// - 이상 상황인 경우...
			// - 복제 작업을 하지 않고 로깅 처리.
			LOG( LWAR, "Replication count[%d] not vaild.[%s]"
				, nReplicationCount, objRetryData.m_OriginData.m_szURI.c_str() );

			continue;
		}
		
		// FHS 를 상태를 관리하는 CProperFHS 모듈을 통해
		// - 복제 가능한 FHS 목록을 추출한다.
		vecFHS.clear();
		m_pProperFhs->GetProperFhs( nReplicationCount, vecFHS, listIgnoreFHS );

		// CHG 2014-06-24 huibong
		// #19042 의 팀회의 결과에 따라
		// - 서비스의 설정된 복제 갯수보다 proper FHS 수가 적은 경우가 발생 가능. 이에 따라 다음과 같이 처리
		// - proper FHS 가 0 인 경우 Retry 처리
		// - proper FHS 수가 적은 경우
		//   일단 추출된 proper FHS 에 복제본 생성 처리
		//   향후 신규 개발될 후보정 처리 모듈에서 부족한 복제본에 대한 보정 처리 수행.
		if( vecFHS.size() == 0 )
		{
			// Proper FHS 가 하나도 없는 경우 
			// - 재시도 횟수가 초과된 경우 Report 로그에 기록 후 삭제 처리.
			// - 그외 Retry Queue 에 저장 처리
			if( objRetryData.m_uFailCount + 1 > m_uRetryCount )
			{
				// 재시도 횟수 초과
				_LOG( LERR, "CReplicateControl: proper fhs not exist and retry count over.[%s][%llu]"
					, objRetryData.m_OriginData.m_szURI.c_str()
					, objRetryData.m_OriginData.m_nContentLength );

				m_reportLog.Write( "ERR", "[%s][Replication fail. check content[%s][%llu]]"
					, PROG_NAME
					, objRetryData.m_OriginData.m_szURI.c_str()
					, objRetryData.m_OriginData.m_nContentLength );
			}
			else
			{
				// Retry Queue 에 저장 처리
				objRetryData.m_uFailCount += 1;
				objRetryData.m_timeFailed = time( 0 );

				// Retry Queue push 실패 발생 가능
				// - Retry Queue 가 full 인 경우 push 실패 
				// - 만약 무작정 push 될때까지 루프를 돌리면... 
				// - 본 Control 모듈에서 pop을 할 수 없어 무한 대기가 발생하므로.. 
				// - Push 실패 발생시 report 로그에 기록하고 버린다.
				if( m_pRetryQueue->Push( objRetryData ) == false )
				{
					_LOG( LERR, "CReplicateControl: proper fhs not exist and retry queue push fail.[%s][%llu]"
						, objRetryData.m_OriginData.m_szURI.c_str()
						, objRetryData.m_OriginData.m_nContentLength );
										
					m_reportLog.Write( "ERR", "[%s][Replication fail by retry queue full.check conf[%s][%llu]]"
						, PROG_NAME
						, objRetryData.m_OriginData.m_szURI.c_str()
						, objRetryData.m_OriginData.m_nContentLength );
				}
				else
				{
					_LOG( LWAR, "CReplicateControl: proper fhs not exist. retry add.[%s][%llu]"
						, objRetryData.m_OriginData.m_szURI.c_str()
						, objRetryData.m_OriginData.m_nContentLength );
				}
			}

			// 다음 Content 에 대해 계속 수행.
			continue;
		}

		// 복제 Thread 생성
		// - 이 곳에서는 new로 할당만 처리.
		// - CReplicateThread가 종료될때 delete this를 하기때문이니 놀라지말것.
		CReplicateThread * pReplicateThread 
			= new CReplicateThread( m_pProcessStatus, m_pRetryQueue
			                      , m_pCompleteQueue, objRetryData, vecFHS );

		// Thread 생성 또는 Start 실패의 경우
		// - Retry Queue 에 저장 처리.
		if( pReplicateThread == NULL || pReplicateThread->Start() == false )
		{
			// 최종 Thread 정리
			if( pReplicateThread != NULL )
			{
				delete pReplicateThread;
				pReplicateThread = NULL;
			}

			// 재시도 횟수가 초과된 경우 Report 로그에 기록 후 삭제 처리.
			if( objRetryData.m_uFailCount + 1 > m_uRetryCount )
			{
				// 재시도 횟수 초과
				_LOG( LERR, "CReplicateControl: replicate thread start fail and retry count over.[%s][%llu]"
					, objRetryData.m_OriginData.m_szURI.c_str()
					, objRetryData.m_OriginData.m_nContentLength );

				m_reportLog.Write( "ERR", "[%s][Replication fail. check content[%s][%llu]]"
					, PROG_NAME
					, objRetryData.m_OriginData.m_szURI.c_str()
					, objRetryData.m_OriginData.m_nContentLength );
			}
			else
			{
				// Retry Queue 에 저장 처리
				objRetryData.m_uFailCount += 1;
				objRetryData.m_timeFailed = time( 0 );

				// Retry Queue push 실패 발생 가능
				// - Retry Queue 가 full 인 경우 push 실패 
				// - 만약 무작정 push 될때까지 루프를 돌리면... 
				// - 본 Control 모듈에서 pop을 할 수 없어 무한 대기가 발생하므로.. 
				// - Push 실패 발생시 report 로그에 기록하고 버린다.
				if( m_pRetryQueue->Push( objRetryData ) == false )
				{
					_LOG( LERR, "CReplicateControl: proper fhs not exist and retry queue push fail.[%s][%llu]"
						, objRetryData.m_OriginData.m_szURI.c_str()
						, objRetryData.m_OriginData.m_nContentLength );

					m_reportLog.Write( "ERR", "[%s][Replication fail by retry queue full.check conf[%s][%llu]]"
						, PROG_NAME
						, objRetryData.m_OriginData.m_szURI.c_str()
						, objRetryData.m_OriginData.m_nContentLength );
				}
				else
				{
					_LOG( LWAR, "CReplicateControl: replicate thread start fail. retry add.[%s][%llu]"
						, objRetryData.m_OriginData.m_szURI.c_str()
						, objRetryData.m_OriginData.m_nContentLength );
				}
			}
		}		
	}

	// Pop 한 Content 에 대해 처리가 모두 완료 된 경우...
	return true;
}

// 스레드 내에서 각 loop 단위 처리 호출 함수.
void CReplicateControl::Execute()
{
	if( MakeReplication() == true )
	{
		m_uSleep = DEFAULT_SLEEP_TIME;
	}
	else
	{
		m_uSleep = MAX_SLEEP_TIME;
	}
}

// 스레드 함수
void* CReplicateControl::threadFunc( void* arg )
{
    CReplicateControl* pObject = reinterpret_cast<CReplicateControl *>(arg);    
	pthread_detach( pthread_self() );
    
    while( 1 )
    {
        pthread_testcancel();
        pObject->Execute();
        pthread_testcancel();

		sleep( pObject->m_uSleep );
    }

	// NEW 2014-06-23 huibong
	// Thread 종료시 m_threadHandle 값을 초기화 처리.
	// - 소멸자에서 잘못된 Thread 를 종료할 수 있기 때문.
	pObject->m_threadHandle = 0;
    	
	return NULL;
}

// thread를 생성하여 업무 flow 를 시작.
bool CReplicateControl::Start()
{    
	int nRet = ::pthread_create( &m_threadHandle, NULL, CReplicateControl::threadFunc, this );
	if( nRet != 0 )
	{
		// Thread 생성 실패시
		int errorNum = errno;
		LOG( LERR, "Thread create failed.[%d][%s]", errorNum, strerror( errorNum ) );

		return false;
	}
	
	return true;
}
