#include "ProcessMonitoringThread.h"

#include "Logger.h"
#include "ProcessRename.h"

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


// 정보 갱신 주기 (sec)
//  - rebalance 작업의 console 상태 정보는 2 sec 단위로 업데이트 한다.
#define INTERVAL_UPDATE_INFO	2


// 생성자
CProcessMonitoringThread::CProcessMonitoringThread( CDataQueue<CRebalanceData> * const pQueueMoveStandby
												, CDataQueue<CRebalanceData> * const pQueueUpdateStandby
												, CDataQueue<CRebalanceData> * const pQueueDeleteStandby
	                                            , CCopyControlThread * const pCopyControlThread )
	: m_pQueueMoveStandby( pQueueMoveStandby)
	, m_pQueueUpdateStandby( pQueueUpdateStandby)
	, m_pQueueDeleteStandby( pQueueDeleteStandby )
	, m_pCopyControlThread( pCopyControlThread )
{
	// 멤버변수 초기화

	m_threadHandle = 0; // thread handle 초기화

	m_mapUnlinkInfo.clear();
	pthread_mutex_init( &m_mutexUnlinkInfo, NULL );
}

// 소멸자
CProcessMonitoringThread::~CProcessMonitoringThread()
{
	// thread 가 동작 중인 경우.. thread 중지 처리
	if( m_threadHandle != 0 )
	{
		pthread_cancel( m_threadHandle );
	}

	pthread_mutex_destroy( &m_mutexUnlinkInfo );
	m_mapUnlinkInfo.clear();

	// 혹시나 해서 정석대로 NULL 처리 추가함.
	if( m_pQueueMoveStandby != NULL )
		m_pQueueMoveStandby = NULL;
	if( m_pQueueUpdateStandby != NULL )
		m_pQueueUpdateStandby = NULL;
	if( m_pQueueDeleteStandby != NULL )
		m_pQueueDeleteStandby = NULL;
	if( m_pCopyControlThread != NULL )
		m_pCopyControlThread = NULL;
}

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

bool CProcessMonitoringThread::Start()
{
	// 멤버 변수 유효성 check
	if( m_pQueueMoveStandby == NULL || m_pQueueUpdateStandby == NULL || m_pQueueDeleteStandby == NULL )
	{
		LOG( LERR, "ProcessMonitoringThread: queue object pointer not valid." );
		return false;
	}
	
	if( m_pCopyControlThread == NULL )
	{
		LOG( LERR, "ProcessMonitoringThread: copy thread object pointer not valid.");
		return false;
	}

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

	return true;
}

void CProcessMonitoringThread::UpdateTitle( void )
{
	char szTemp[100] = { 0 };

	// 본 출력 사항에 대한 설명은 help 에 포함되어 있으므르
	// 수정할 경우 PrintUsage(), Execute() 의 내역도 수정해야 함.
	sprintf( szTemp, "%s: S%u T%d U%u D%u"
		, PROG_NAME
		, m_pQueueMoveStandby->GetSize()
		, m_pCopyControlThread->GetCopyThreadCount()
		, m_pQueueUpdateStandby->GetSize()
		, m_pQueueDeleteStandby->GetSize()
	);

	set_ps_display( szTemp, false );

	return;
}

void CProcessMonitoringThread::UpdateDeletedInfo( CRebalanceData & data )
{
	// CPhysicalDeleteThread 에서
	// 정상적으로 삭제 처리한 content 에 대한 FHS, 삭제 크기 정보를 전달받는다.
	// 전달받은 정보는 내부적으로 저장했다가 주기적으로 로깅 처리한다.
	std::map<std::string, struct FhsUnlinkInfo>::iterator it;

	pthread_mutex_lock( &m_mutexUnlinkInfo );
	it = m_mapUnlinkInfo.find( data.m_szOriginHost );

	if( it != m_mapUnlinkInfo.end() )
	{
		// 이미 해당 FHS 가 존재하는 경우...
		// - 해당 정보를 추가로 sum 처리한다...
		it->second.hour_count += 1;
		it->second.hour_sum += data.m_uOriginContentLength;
		it->second.day_count += 1;
		it->second.day_sum += data.m_uOriginContentLength;
	}
	else
	{
		// 해당 FHS 정보가 없는 경우...
		// - 신규로 추가한다.
		// - map 특성상  key 값에 의한 자동 sort 처리됨.
		struct FhsUnlinkInfo fhs;

		fhs.hour_count = 1;
		fhs.hour_sum = data.m_uOriginContentLength;
		fhs.day_count = 1;
		fhs.day_sum = data.m_uOriginContentLength;

		m_mapUnlinkInfo[ data.m_szOriginHost ] = fhs;
	}
	pthread_mutex_unlock( &m_mutexUnlinkInfo );

	_LOG( LDBG, "ProcessMonitoringThread: deleted info update. [%s %s %llu]"
		, data.m_szOriginHost.c_str()
		, data.m_szServiceSeq.c_str()
		, data.m_uOriginContentLength );

	return;
}

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


// Thread loop 기본 함수
void * CProcessMonitoringThread::threadFunc( void * arg )
{
	CProcessMonitoringThread * pObject = reinterpret_cast<CProcessMonitoringThread *>( arg );
	pthread_detach( pthread_self() );

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

		// 지정된 주기로 실행
		sleep( INTERVAL_UPDATE_INFO );
	}

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

	return NULL;
}


// 실제 정보 update 처리 함수
void CProcessMonitoringThread::Execute()
{
	char szTemp[100] = { 0 };

	unsigned int nCountMoveStandby		= m_pQueueMoveStandby->GetSize();
	unsigned int nCountUpdateStandby	= m_pQueueUpdateStandby->GetSize();
	unsigned int nCountDeleteStandby	= m_pQueueDeleteStandby->GetSize();

	int nCountContentTransferThread		= m_pCopyControlThread->GetCopyThreadCount();

	int nCountMaxContentTransferThread = 0;

	// 본 출력 사항에 대한 설명은 help 에 포함되어 있으므르
	// 수정할 경우 PrintUsage(),UpdateTitle() 의 내역도 수정해야 함.
	sprintf( szTemp, "%s: S%u T%d U%u D%u"
		, PROG_NAME
		, nCountMoveStandby
		, nCountContentTransferThread
		, nCountUpdateStandby
		, nCountDeleteStandby );

	set_ps_display( szTemp, false );

	if( nCountMoveStandby != 0
		|| nCountContentTransferThread != 0
		|| nCountUpdateStandby != 0
		|| nCountDeleteStandby != 0 )
	{
		nCountMaxContentTransferThread = m_pCopyControlThread->GetMaxCopyThreadCount();

		_LOG( LINF, "[MONITOR] Move standby: %4u, Transfer thread: %d/%d, Update standby: %4u, Unlink standby: %4u"
			, nCountMoveStandby
			, nCountContentTransferThread, nCountMaxContentTransferThread
			, nCountUpdateStandby
			, nCountDeleteStandby );
	}

	// 각 FHS 별 삭제한 통계 정보를 logging 및 초기화 처리수행.
	printUnlinkInfo();

	return;
}

void CProcessMonitoringThread::printUnlinkInfo()
{
	if( m_mapUnlinkInfo.empty() == true )
		return;

	// 시간 check
	// - 시간 단위로 FHS 별 삭제한 정보를 로깅하고 초기화.
	// - 일 단위 FHS 별 삭제한 정보는 매일 10시에 로깅 및 초기화 되도록 한다.
	time_t currentTime = time( NULL );
	long currentSec = currentTime % 3600; // 1시간 = 3600 sec 로 나누어서 나머지가 현재 시간의  sec 를 나타냄

	if( currentSec == 0 || currentSec == 1 ) //  각 시간 정각 또는 정각+1 sec 
	{
		// total 정보 출력을 위한 변수
		unsigned long long uTotalFhsCount = 0;
		unsigned long long uTotalContentCount = 0;
		unsigned long long uTotalContentSum = 0;

		std::map<std::string, struct FhsUnlinkInfo>::iterator it;;
		struct tm currentTm;
		memset( &currentTm, 0x00, sizeof( struct tm ) );

		localtime_r( &currentTime, &currentTm );

		if( currentTm.tm_hour == 10 )
		{
			// 오전 9시 정각에는 full logging.
			pthread_mutex_lock( &m_mutexUnlinkInfo );

			for( it = m_mapUnlinkInfo.begin(); it != m_mapUnlinkInfo.end(); it++ )
			{
				uTotalFhsCount += 1;
				uTotalContentCount += it->second.day_count;
				uTotalContentSum += it->second.day_sum;

				// FHS 별 확보한 용량 통계 정보 출력
				_LOG( LINF, "[MONITOR] FHS rebalance day report:: %s , count:%5lu , size:%5llu GiB."
					, it->first.c_str()
					, it->second.day_count, (it->second.day_sum)/1024/1024/1024 );
			}
			// 통계 정보 초기화
			m_mapUnlinkInfo.clear();
			pthread_mutex_unlock( &m_mutexUnlinkInfo );

			// total report logging
			_LOG( LINF, "[MONITOR] FHS rebalance total day report:: fhs: %llu , content: %llu , total_size: %llu GiB."
				, uTotalFhsCount
				, uTotalContentCount
				, uTotalContentSum / 1024 / 1024 / 1024 );

		}
		else
		{
			// 나머지는 시간당 삭제 처리한 정보 로깅
			pthread_mutex_lock( &m_mutexUnlinkInfo );

			for( it = m_mapUnlinkInfo.begin(); it != m_mapUnlinkInfo.end(); it++ )
			{
				// 해당 시간에 작업하지 않은 FHS 는 제외
				if( it->second.hour_count > 0 )
				{
					uTotalFhsCount += 1;
					uTotalContentCount += it->second.hour_count;
					uTotalContentSum += it->second.hour_sum;

					// FHS 별 확보한 용량 통계 정보 출력
					_LOG( LINF, "[MONITOR] FHS rebalance hour report:: %s , count:%5lu , size:%5llu GiB."
						, it->first.c_str()
						, it->second.hour_count, (it->second.hour_sum) / 1024 / 1024 / 1024 );

					// 각 장비 시간 단위 통계 정보 초기화
					it->second.hour_count = 0;
					it->second.hour_sum = 0;
				}
			}
			pthread_mutex_unlock( &m_mutexUnlinkInfo );


			// total report logging
			_LOG( LINF, "[MONITOR] FHS rebalance total hour report:: fhs: %llu , content: %llu , total_size: %llu GiB."
				, uTotalFhsCount
				, uTotalContentCount
				, uTotalContentSum / 1024 / 1024 / 1024 );
		}
	}
	return;
}

