#include "HotContentDetectThread.h"
#include "DaemonConfigs.h"
#include "FstatHandler.h"

#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <limits.h>
#include <sys/time.h>

using namespace std;

#define HOT_CONTENT_DETECT_INTERVAL      5 // sec
#define HOT_CONTENT_INFO_CLEAR_TERM      100 // count

CHotContentDetectThread::CHotContentDetectThread()
	:m_threadHandle(0), m_pReqCacheList(NULL), m_pHotContentInfoMap(NULL)
{
}

CHotContentDetectThread::~CHotContentDetectThread()
{

}

bool CHotContentDetectThread::ThreadInit(const sig_atomic_t *sighandle, CCacheRequestList * pReqCacheList /* = NULL */, CHotContentInfoMap * pHotContentInfoMap /* = NULL */)
{
	m_sighandle = sighandle;

	m_pReqCacheList = pReqCacheList;
	m_pHotContentInfoMap = pHotContentInfoMap;
	return true;
}

unsigned int CHotContentDetectThread::GetBaselineOver(std::string filenamehash)
{
	// CHG 2020-07-20 huibong internal cache 관련 fd 감시 on/off 관련 기능 개선 (#33112)
	//                - fhs.conf 상에서 EXTRACT_FD_COUNT 항목을 명시적으로 1 로 설정해야 fd count 정보 추출기능 동작
	//                - 그 외에는 fd count 정보 추출 기능 동작 안함.
	if( CDeamonConfig::GetInstance()->IsExtractContentFdCount() == false )
	{
		//LOG( LINF, "checkpoint: fd count extract setting disabled. [%s]", filenamehash.c_str() );
		return 0;
	}
	else
	{
		CFstatHandler objFstatHandler;
		
		// fstat을 활용해서 해당 파일의 현재 fd count를 얻어온다.
		unsigned int nFdCount = objFstatHandler.GetFdCountOnSystem( filenamehash );
		if( CDeamonConfig::GetInstance()->GetFdCount() > nFdCount )
		{
			//LOG( LDEV, "Fdcount : %d", nFdCount );
			return 0;
		}

		//LOG( LDEV, "Fdcount : %d", nFdCount );
		return nFdCount;
	}
}

bool CHotContentDetectThread::Detector()
{
	CTransferStatData data;
	vector<CTransferStatData> dataList;

    while (*m_sighandle == 0)
    {
        // Download List에서 값을 가져오기 전에 개수를 확인한다.
		if (m_Transfer.GetSize() <= 0)
    	{
    	    //LOG( LDEV, "TransferStatData queue is empty.");
    	    break;
        }

		CTransferStatData dataTransferLog;
		{
			m_Transfer.Pop(dataTransferLog);

			LOG(LDBG, "Dowload File Info : %s", dataTransferLog.GetFilenameHash().c_str());


			// hot content replication 수행.
			CHotContentData* pObjContentInfo = m_pHotContentInfoMap->GetData(dataTransferLog.GetFilenameHash());
			if (pObjContentInfo == NULL)
			{

				// 기준선 이상 이면  카운트를 가지고 옴
				unsigned int nCnt = GetBaselineOver(dataTransferLog.GetFilenameHash());
				if (nCnt <= 0) continue;

				// 존재 하지 않는다면.
				// 1. hot content replication queue에 추가한다. 
				m_pReqCacheList->Push(dataTransferLog.GetFilenameHash(), (uint32_t)dataTransferLog.GetServiceSeq(), nCnt);

				//LOG(LDEV, "Add File : %s", dataTransferLog.GetFilenameHash().c_str());
				// 2. Hot Content Info Map에 등록
				m_pHotContentInfoMap->AddData(dataTransferLog.GetFilenameHash(), nCnt);
				continue;
			}
			else
			{
				// 2. 요청시간이 지정시간 이상 경과되었는가?
				if (pObjContentInfo->IsReqTimeOver() == false)
				{
					// 경과하지 않았다면...
					// 2-1. 정보 업데이트 후 진행..
					//pObjContentInfo->UpdateInfo(nCnt);;
					continue;
				}

				// 기준선 이상 이면  카운트를 가지고 옴
				unsigned int nCnt = GetBaselineOver(dataTransferLog.GetFilenameHash());
				if (nCnt <= 0) continue;

				// 존재한다면
				// 1. 증가 추이 인지 확인한다.
				if (pObjContentInfo->IsIncrease(nCnt) == false)
				{
					// 증가 추이가 아니라면...
					// 1-1. 요청이 있었으므로.. 정보 업데이트
					pObjContentInfo->UpdateInfo(nCnt);
					continue;
				}

				// 3. replication 요청 후..
				// 1. hot content replication queue에 추가한다.
				_LOG(LINF, "ReplicationReq state - Hash Name : %s, FdCount : %d ", dataTransferLog.GetFilenameHash().c_str(), nCnt);
				m_pReqCacheList->Push(dataTransferLog.GetFilenameHash(), (uint32_t)dataTransferLog.GetServiceSeq(), nCnt);

				// Hot Content Info Map Update
				pObjContentInfo->UpdateInfo(nCnt);
				pObjContentInfo->UpdateRequestTime();
				continue;
			}

		}
	}
	
	FUNC_END();
	return true;
}


///@brief 이 함수에서 컨트롤 로직이 표현된다.
void CHotContentDetectThread::Execute()
{
	if ( m_pReqCacheList == NULL )
	{
		LOG( LERR, "failed to push to download list because the instance of CCacheRequestList is NULL." );
		return;
	}

	if( m_pHotContentInfoMap == NULL )
	{
		LOG( LERR, "failed to push to download list because the instance of CHotContentInfoMap  is NULL." );
		return;
	}

	while (*m_sighandle == 0)
	{
        // 아래 함수에서 return false 가 되더라도 무시한다.
        if( Detector() == false ){;}
        
        // 일정 시간이 지난 정보를 정리한다.
        m_pHotContentInfoMap->Arrangement();
        
        sleep( HOT_CONTENT_DETECT_INTERVAL );
	}
	
	LOG(LINF, "Thread end...");
}

void* CHotContentDetectThread::EntryPoint(void* arg)
{
    CHotContentDetectThread* pObject = reinterpret_cast<CHotContentDetectThread *>(arg);
	pthread_detach( pthread_self() );
    
	pObject->Execute();
	
	// Thread 종료시 m_threadHandle 값을 초기화 처리.
	// - 소멸자에서 잘못된 Thread 를 종료할 수 있기 때문.
	//pObject->m_threadHandle = 0;

	return 0;
}

bool CHotContentDetectThread::Start()
{
	int nRet = ::pthread_create(&m_threadHandle, 0, CHotContentDetectThread::EntryPoint, this);

	if( nRet )
	{
		LOG(LERR, "Thread create failed.: errno:%d errmsg:%s", errno, strerror(errno));
		return false;
	}
    LOG(LDBG, "Thread create succeed");
	sleep(0);
	return true;
}

