#include "ThreadExtractControl.h"

#include "Logger.h"
#include "ProcessConfig.h"
#include "ThreadAccessTime.h"

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


// 매 Loop 마다 Sleep 시간 정보 정의 ( sec 단위)
#define SLEEP_TIME_DEFAULT		 0
#define SLEEP_TIME_MAX			10


CThreadExtractControl::CThreadExtractControl( CDataQueue<CDataAccess> * const pQueueTarget
	, CDataQueue<CDataAccess> * const pQueueComplete, unsigned int nMaxThreadCount )
	: m_pQueueTarget(pQueueTarget), m_pQueueComplete(pQueueComplete), m_nMaxThreadCount(nMaxThreadCount)
{
	// 멤버변수 초기화

	m_threadHandle = 0;
	m_uSleep = SLEEP_TIME_DEFAULT;

	m_nCurrentThreadCount = 0;
	pthread_mutex_init( &m_mutexThreadCount, NULL );

}

CThreadExtractControl::~CThreadExtractControl()
{
	// 내부 therad 가 동작 중인 경우... 중지 처리
	if( m_threadHandle != 0 )
	{
		pthread_cancel( m_threadHandle );
	}

	sleep( 1 );		// thread 종료 잠시 대기

	// 혹시나 해서 정석대로 내부 변수 NLLL 처리
	if( m_pQueueTarget != NULL )
		m_pQueueTarget = NULL;

	if( m_pQueueComplete != NULL )
		m_pQueueComplete = NULL;

	pthread_mutex_destroy( &m_mutexThreadCount );

}

bool CThreadExtractControl::Start( void )
{
	// 유효성 check
	if( m_pQueueTarget == NULL || m_pQueueComplete == NULL )
	{
		LOG( LERR, "CThreadExtractControl: queue object pointer not valid." );
		return false;
	}

	// 작업 thread 생성
	int nResult = pthread_create( &m_threadHandle, NULL, CThreadExtractControl::threadFunc, this );
	if( nResult != 0 )
	{
		// thread 생성 실패
		int errorNum = errno;
		LOG( LERR, "CThreadExtractControl: Thread create failed.[%d][%s]", errorNum, strerror( errorNum ) );

		return false;
	}

	return true;
}

void CThreadExtractControl::PlusThreadCountAccess( void )
{
	pthread_mutex_lock( &m_mutexThreadCount );
	++m_nCurrentThreadCount;
	pthread_mutex_unlock( &m_mutexThreadCount );

}

void CThreadExtractControl::MinusThreadCountAccess( void )
{
	pthread_mutex_lock( &m_mutexThreadCount );
	--m_nCurrentThreadCount;
	pthread_mutex_unlock( &m_mutexThreadCount );
}

int CThreadExtractControl::GetThreadCountAccess( void )
{
	return m_nCurrentThreadCount;
}

int CThreadExtractControl::GetCreateableThreadCount( void )
{
	int nCreateableCount = 0;

	// lock 설정 후 추가 생성 가능한 Copy thread 계산한다.
	pthread_mutex_lock( &m_mutexThreadCount );
	nCreateableCount = ((int) m_nMaxThreadCount) - m_nCurrentThreadCount;
	pthread_mutex_unlock( &m_mutexThreadCount );

	// 값 보정
	if( nCreateableCount < 0 )
		nCreateableCount = 0;

	return nCreateableCount;
}



///////////////////////////////////////////////////////////////////////////

void * CThreadExtractControl::threadFunc( void * arg )
{
	CThreadExtractControl * pObject = reinterpret_cast<CThreadExtractControl *>( arg );
	pthread_detach( pthread_self() );

	struct timespec sleepTime;
	sleepTime.tv_sec = 0;
	sleepTime.tv_nsec = 300000000;   // 0.3 sec

	while( 1 )
	{
		pthread_testcancel();
		pObject->Execute();
		pthread_testcancel();

		// 지정된 주기로 실행
		if( pObject->m_uSleep == 0 )
		{
			nanosleep( &sleepTime, NULL );
		}
		else
		{
			sleep( pObject->m_uSleep );
		}

	}

	// thread 종료시 handle 정보 초기화
	pObject->m_threadHandle = 0;

	return NULL;
}

void CThreadExtractControl::Execute( void )
{
	// sleep time 조정 처리
	m_uSleep = SLEEP_TIME_DEFAULT;

	// 작업 대상 content 가 존재하는지?
	if( m_pQueueTarget->GetSize() == 0 )
	{
		// MAX sleep 
		m_uSleep = SLEEP_TIME_MAX;

		_LOG( LINF, "[CONTROL] content queue empty. max sleep %u sec.", m_uSleep );
		return;
	}

	// 작업 결과를 저장할 queue 공간이 있는지?
	if( m_pQueueComplete->GetFreeSize() < (unsigned int)m_nMaxThreadCount )
	{
		// MAX sleep 
		m_uSleep = SLEEP_TIME_MAX;

		_LOG( LINF, "[CONTROL] content complte queue full [free %u/%u]. max sleep %u sec."
			, m_pQueueComplete->GetFreeSize(), m_pQueueComplete->GetMaxSize(),  m_uSleep );

		return;
	}

	// 생성 가능 thread 가 존재하는가?
	int nCreateableCount = GetCreateableThreadCount();
	if( nCreateableCount == 0 )
	{
		m_uSleep = SLEEP_TIME_DEFAULT;

		_LOG( LINF, "[CONTROL] access time thread all working [%d / %d]. sleep %u sec."
			, nCreateableCount, m_nMaxThreadCount, m_uSleep );
		return;
	}
	else
	{
		_LOG( LINF, "[CONTROL] access time thread createable count [%d / %d]"
			, nCreateableCount, m_nMaxThreadCount );
	}

	// queue 에서 pop 처리하여 처리 thread 를 생성 처리한다.
	CDataAccess data;

	while( m_pQueueTarget->GetSize() > 0 )
	{
		// queue pop
		data = m_pQueueTarget->Front();
		m_pQueueTarget->Pop();


		// access time 추출 처리 thrad 생성 처리
		CThreadAccessTime * pThreadAccess = new CThreadAccessTime( data, m_pQueueComplete, this );

		// Thread 생성 또는 start 처리 실패시
		// - thread 생성이 실패할 가능성은 거의 없고..
		// - 아마 thread 객체의 Start 처리시 인자 check 등에서 오류일 가능성이 높으므로.. 
		// - 재시도 하지 않고.. 그냥 skip 처리한다.
		if( pThreadAccess == NULL || pThreadAccess->Start() == false )
		{
			// thread 정리
			if( pThreadAccess != NULL )
			{
				delete pThreadAccess;
				pThreadAccess = NULL;
			}

			// 실패 내역 로깅
			LOG( LWAR, "[CONTROL] access time thread create or start fail. [%s %llu %s]"
				, data.m_strHostname.c_str(), data.m_uResourceId, data.m_strFilenamehash.c_str() );
			
		}
		else
		{
			// thread 가 정상 기동된 경우...
			--nCreateableCount;
		}

		// 생성 가능한 thread 를 전부 생성했는지 check.
		if( nCreateableCount <= 0 )
			break;
	}


	return;

}


