#include "ReplicateQueueControl.h"

#include <time.h>

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


// Retry 대상 Content 에 대해 재작업 처리 최소 시간 간격.
#define RETRY_CHECK_TIME 300  // 5분


// 생성자
CReplicateQueueControl::CReplicateQueueControl()
{
	// 멤버 변수 초기화
	m_pSourceQueue = NULL;
	m_pRetryQueue  = NULL;
}

// 소멸자
CReplicateQueueControl::~CReplicateQueueControl()
{

}

// 객체 초기화 함수
bool CReplicateQueueControl::Init( CDataQueue<CSourceData> * pSourceQueue, CDataQueue<CRetryData> * pRetryQueue )
{
	// 전달받은 인자를 멤버변수에 할당
	m_pSourceQueue = pSourceQueue;
	m_pRetryQueue  = pRetryQueue;

	// Report 로그 처리 관련 멤버 객체 초기화.	
	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;
	}

	return true;
}

// Replication 을 처리할 객체를 요청한 수만큼 list 형태로 전달
// @param [in] nCnt 요청 개수를 입력한다.
// @param [out] lstWork 작업대상 정보를 list형태로 전달
// @return true 처리할 data 가 존재하는 경우..
//         false 처리할 data 가 없는 경우
bool CReplicateQueueControl::GetWork(unsigned int nCnt, std::list<CRetryData>& lstWork)
{
	// 업무 Flow 상 nCnt == 0 여부는 호출 모듈에서 이미 처리했으므로...
	// 본 함수에서는 확인하지 않는다.

	// 1. Data 를 저장할 객체 Clear 처리.
	lstWork.clear();
	
	// 2. Retry Queue 상에서 다음 조건을 만족하는 Content 가 존재하는 경우 push 처리
	//   - 재시도 횟수가 설정값 이하이고... (초과된 경우.. 제거 처리)
	//   - 지정된 시간 이상 지났는지...
	//   - Retry Queue 는 특성상 가장 오래된 Data 가 먼저 pop 되므로... 
	//     위의 조건을 만족하지 않는 Data 가 나타나는 경우... push 를 중지한다.
	CRetryData objRetryData;
	time_t timeNow;
	
	// Retry Queue 에 Data 가 존재하는 경우..
	while( m_pRetryQueue->GetSize() > 0 && nCnt > lstWork.size() )
	{
		// Retry Queue 에서 첫번째 Data를 가져와.
		objRetryData = m_pRetryQueue->Front();
		
		// 재시도 횟수를 초과했는지 확인		
		if( objRetryData.m_uFailCount > CServiceConfig::GetInstance()->GetMaxRetryCount() )
		{
			// 재시도 횟수를 초과한 경우...
			// - Report 로그에 내역 기록한 후
			// - queue 에서 pop 하여 제거 처리
			_LOG( LERR, "CReplicateQueueControl: retry count over. replication fail.[%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 );
		
			m_pRetryQueue->Pop();
			continue;
		}
		
		// 재시도 가능 시간( Replication 실패 후 지정된 시간 이상 지났는지)인지 확인
		// - 시간이 지나지 않았다면 더이상 m_pRetryQueue는 조사할 필요가 없다.
		time(&timeNow);
		if( objRetryData.m_timeFailed == 0 )
		{
			LOG( LERR, "time value is zero. check flow." );		    
        }
        
		if( (timeNow - objRetryData.m_timeFailed) < RETRY_CHECK_TIME )
		{
			// 재시도 가능 시간이 아닌 경우.. queue를 그대로 유지하고 loop를 빠져나간다.
			break;
		}

		// 재시도 해야 하는 content 인 경우... 
		// - 결과 변수에 push 처리하고
		// - retry queue 에서는 pop 하여 제거 처리
		lstWork.push_back( objRetryData );
		m_pRetryQueue->Pop();
	}
	
	// 3. Retry Queue 에서 요청한 만큼 Data 를 pop 하지 못한 경우...
	//    - source queue 에서 pop 처리.	
	CSourceData objSourceData;
	while( nCnt > lstWork.size() && m_pSourceQueue->GetSize() > 0 )
	{
		// 대상 정보를 가져온다.
		objSourceData = m_pSourceQueue->Front();

		// list에 넣어주기위해 CRetryData타입으로 생성한다.
		objRetryData.m_OriginData = objSourceData;
		objRetryData.m_uFailCount = 0;
		objRetryData.m_listSuccessHost.clear();
		objRetryData.m_timeFailed = 0;

		// 결과 변수에 push 처리하고... source queue 에서 pop 처리.
		lstWork.push_back(objRetryData);
		m_pSourceQueue->Pop();
	}

	//_LOG( LINF, "CReplicateQueueControl: queue pop count [%zu]", lstWork.size() );

	// 결과 list 에 data 가 존재하는 경우...
	if( lstWork.empty() == false )
		return true;
	else
		return false;

}
