#include "ThreadAccessTime.h"


#include "../ftsd/FtsdSocketControl.h"
#include "ProcessConfig.h"

#include <errno.h>
#include <string.h>
#include <sys/syscall.h>



CThreadAccessTime::CThreadAccessTime( CDataAccess & data, CDataQueue<CDataAccess> * const pQueueComplete
	, CThreadExtractControl * const pControl )
	: m_data(data), m_pQueueComplete(pQueueComplete), m_pControl(pControl)
{
	// 멤버 변수 초기화
	m_threadHandle = 0;

}

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

	m_pQueueComplete = NULL;
	m_pControl = NULL;
}

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

	if( m_pControl == NULL )
	{
		LOG( LERR, "CThreadAccessTime: control object pointer not valid." );
		return false;
	}

	// 작업 content 에 대한 유효성 check

	if( m_data.m_strHostname.size() < 7 || m_data.m_strFilenamehash.size() < 5 )
	{
		LOG( LERR, "CThreadAccessTime: content info not valid.[%s][%s]"
			, m_data.m_strHostname.c_str(), m_data.m_strFilenamehash.c_str() );

		return false;
	}


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

		return false;
	}

	return true;
}

void * CThreadAccessTime::threadFunc( void * arg )
{
	CThreadAccessTime * pObject = reinterpret_cast<CThreadAccessTime *>( arg );

	pthread_detach( pthread_self() );
	pthread_testcancel();

	// Thread 동작시 control class 에 count 증가 처리
	pObject->m_pControl->PlusThreadCountAccess();

	// 실제 작업 수행
	pObject->Execute();

	// Thread 동작 완료시 control class 에 count 감소 처리
	pObject->m_pControl->MinusThreadCountAccess();

	pthread_testcancel();

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

	// 객체 자동 delete 처리
	delete pObject;
	return NULL;
}

void CThreadAccessTime::Execute( void )
{
	// FHS ftsd 와 통신 처리 담당한 socket 객체 생성
	CFtsdSocketControl fhs;

	// FHS connect
	bool bResult = fhs.ConnectTarget( m_data.m_strHostname, CProcessConfig::GetInstance()->GetFhsTransferDaemonPort() );

	// FHS 로 access time 정보 추출 요청
	bResult = fhs.SendFileStatRequest( m_data.m_strFilenamehash );

	if( bResult == false )
	{
		// content stat 요청 실패
		LOG( LERR, "[FHS    ] %lu: content stat request send fail to fhs. %s %llu %s "
			, GetThreadId()
			, m_data.m_strHostname.c_str(), m_data.m_uResourceId , m_data.m_strFilenamehash.c_str() );

		return;
	}

	// FHS 로 부터 결과 수신 대기

	// response timeout 처리 관련 변수
	struct timespec timeStart, timeNow;
	double timeElapsed = 0.0;
	memset( &timeStart, 0x00, sizeof( struct timespec ) );
	memset( &timeNow, 0x00, sizeof( struct timespec ) );
	clock_gettime( CLOCK_MONOTONIC, &timeStart );


	// 응답 결과 저장을 위한 변수
	int nResult;
	bool bSuccess = false;
	std::string szErrorMessage;

	while( 1 )
	{
		nResult = fhs.GetFileStatResult( 5, bSuccess, szErrorMessage
			, m_data.m_lFileSize, m_data.m_lAccessTime, m_data.m_lModifyTime, m_data.m_lChangeTime );

		if( nResult == 0 )
		{
			// 지정된 시간동안 결과 수신 못한 경우
			if( fhs.SendAliveCheck() == false )
			{
				// 다음  loop 에서 -1 연결 종료 처리되므로.. 여기서는 로깅만..
				LOG( LERR, "[FHS    ] %lu: content stat alive check fail. %s %llu %s"
					, GetThreadId()
					, m_data.m_strHostname.c_str(), m_data.m_uResourceId, m_data.m_strFilenamehash.c_str() );

			}
			else
			{
				// timeout 발생했으므로.. 2 sec 대기 후 다시 시도 
				sleep( 2 );
			}
		}
		else if( nResult == 1 )
		{
			// 처리 시간 계산
			clock_gettime( CLOCK_MONOTONIC, &timeNow );
			timeElapsed = (double)( ( timeNow.tv_sec - timeStart.tv_sec ) * 1.0e9 + ( timeNow.tv_nsec - timeStart.tv_nsec ) ) / 1.0e9;

			// FHS 로 부터 응답 수신
			if( bSuccess == true )
			{
				// logging
				_LOG( LDBG, "[FHS    ] %lu: stat check ok. %.2lf %s %llu %s %ld %ld %ld"
					, GetThreadId()
					, timeElapsed
					, m_data.m_strHostname.c_str(), m_data.m_uResourceId, m_data.m_strFilenamehash.c_str()
					, m_data.m_lAccessTime, m_data.m_lModifyTime, m_data.m_lChangeTime );

			}
			else
			{
				// content 존재하지 않는 경우
				// - 존재하지 않는 파일에 대해 DB 상에 -1 로 업데이트 되도록 처리
				m_data.m_lAccessTime = -1;
				m_data.m_lModifyTime = -1;
				m_data.m_lChangeTime = -1;
				m_data.m_lFileSize = 0;

				_LOG( LWAR, "[FHS    ] %lu: stat check error. %.2lf %s %llu %s [%s]"
					, GetThreadId()
					, timeElapsed
					, m_data.m_strHostname.c_str(), m_data.m_uResourceId, m_data.m_strFilenamehash.c_str()
					, szErrorMessage.c_str() );

			}

			// complete queue 에 push 처리
			if( m_pQueueComplete->Push( m_data ) == false )
			{
				// 실패시 약 3 sec 대기 후 1회만 재시도
				sleep( 3 );
				if( m_pQueueComplete->Push( m_data ) == false )
				{
					LOG( LERR, "[FHS    ] %lu: complete queue push fail. %s %llu %s"
						, GetThreadId()
						, m_data.m_strHostname.c_str(), m_data.m_uResourceId, m_data.m_strFilenamehash.c_str() );
					break;
				}
			}

			// 처리 결과 수신에 따른 loop 종료
			break;

		}
		else if( nResult == -1 )
		{
			// 처리 시간 계산
			clock_gettime( CLOCK_MONOTONIC, &timeNow );
			timeElapsed = (double)( ( timeNow.tv_sec - timeStart.tv_sec ) * 1.0e9 + ( timeNow.tv_nsec - timeStart.tv_nsec ) ) / 1.0e9;

			// FHS 와 연결 끝어진 경우...
			LOG( LERR, "[FHS    ] %lu: stat check fail. fhs connection broken. %.2lf %s %llu %s"
				, GetThreadId(), timeElapsed
				, m_data.m_strHostname.c_str(), m_data.m_uResourceId, m_data.m_strFilenamehash.c_str() );

			break;
		}
		else if( nResult == 2 )
		{
			// 대상 응답이 아닌 경우.. 무시
			;
		}
		else
		{
			// 미정의된 상황
			LOG( LERR, "[FHS    ] %lu: receive undefine result[%d]. %s %llu %s"
				, GetThreadId(), nResult
				, m_data.m_strHostname.c_str(), m_data.m_uResourceId, m_data.m_strFilenamehash.c_str() );

			break;
		}


		// Total Reponse 대기 시간 확인
		clock_gettime( CLOCK_MONOTONIC, &timeNow );
		timeElapsed = (double)( ( timeNow.tv_sec - timeStart.tv_sec ) * 1.0e9 + ( timeNow.tv_nsec - timeStart.tv_nsec ) ) / 1.0e9;
		if( timeElapsed > 60.0 )	// 1분 이상 대기하는 경우 실패 처리
		{
			LOG( LERR, "[FHS    ] %lu: stat check fail. response wait %.2lf sec, but timeout 60 sec. %s %llu %s"
				, GetThreadId(), timeElapsed
				, m_data.m_strHostname.c_str(), m_data.m_uResourceId, m_data.m_strFilenamehash.c_str() );

			break;
		}

	} // while(1)


	fhs.Close();


}

long CThreadAccessTime::GetThreadId( void )
{
	return syscall( SYS_gettid );
}



