#include "CacheControl.h"

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

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

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


CCacheControl::CCacheControl( CProcessStatus* pProcessStatus, CCacheJobList* pJobList, CDataQueue<CCacheData>* pQueueCacheComplete )
	: m_pProcessStatus( pProcessStatus )
	, m_pCacheJob( pJobList )
	, m_pQueueCacheComplete( pQueueCacheComplete )
{
	// 멤버 변수 초기화
	m_threadHandle = 0;

	m_uSleep = DEFAULT_SLEEP_TIME;

}

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

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

		return false;
	}

	return true;
}

void* CCacheControl::threadFunc( void* arg )
{
	CCacheControl* pObject = reinterpret_cast<CCacheControl *>( 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;	
}

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


// 스레드 내에서 각 loop 단위 처리 호출 함수.
// return true  : 처리할 Data 가 계속 존재하는 경우...
//        false : 처리할 Data 가 없어 idle 한 경우.
bool CCacheControl::MakeCache()
{
	// 1. CProcessStatus에서 생성 가능한 Cache Thread  수 확인
	unsigned int nWorkCount = 0;
	nWorkCount = m_pProcessStatus->GetValidCacheCount();
	if( nWorkCount == 0 )
	{
		// 생성 가능한 Cache Thrad 수가 없는 경우...
		// - false 를 반환할 경우... MAX_SLEEP_TIME 만큼 대기하므로... 
		// - 빠른 시간내에 재시도할 수 있도록 true 를 반환.
		return true;
	}
	
	// 2. Job 정보를 가져온다.
	// - 생성 가능한 Cache Thread 수 만큼 Job 을 가져온다.
	std::list<CCacheData> listWork;
	if( m_pCacheJob->GetDataToStart( nWorkCount, listWork ) == false )
	{
		// 진행할 Cache Job 이 없는 경우.
		// - idle 상태
		return false;
	}

	// 3. 가져온 Job 만큼 Cache Thread 를 생성한다.
	CCacheData dataCache;
	while( listWork.empty() == false )
	{
		// List 상의 항목을 POP 처리
		dataCache = listWork.front();
		listWork.pop_front();
		
		// CCacheThread 생성.
		// - 이 곳에서는 new로 할당만 한다.
		// - CCacheThread가 종료될때 delete this를 하기때문이니 놀라지말것.
		CCacheThread* pCacheThread = new CCacheThread( m_pProcessStatus, m_pCacheJob, m_pQueueCacheComplete, dataCache );

		// Thread 생성 또는 기동 실패시
		// - 해당 Cache 요청을 삭제 처리한다.		
		if( pCacheThread == NULL || pCacheThread->Start() == false )
		{
			// 최종 Thread 정리
			if( pCacheThread != NULL )
			{
				delete pCacheThread;
				pCacheThread = NULL;
			}

			_LOG( LERR, "CCacheControl: cache thread start fail.[%s][%s]"
				, dataCache.m_requestData.m_strRequestHost.c_str()
				, dataCache.m_requestData.m_strFileNameHash.c_str() );

			// Job 항목에서 삭제 처리한다.
			m_pCacheJob->UnRegister( dataCache );			
		}
	}

	// 처리가 완료된 경우.
	return true;
}
