
#include "PhysicalDeleteThread.h"
#include "FtsdSocketControl.h"

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

using namespace std;

#define TRANSFER_LISTEN_PORT	14001					// ftsd 의 Listen Port

CPhysicalDeleteThread::CPhysicalDeleteThread(CFileInfo objDelFileInfo
		, CQueue<CFileInfo>* pQueueCompleteJob)
	: m_pQueueCompleteJob(pQueueCompleteJob)
{
	m_objDelFileInfo = objDelFileInfo;

	// 멤버 변수 초기화
	m_threadHandle = 0;
}

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

}

bool CPhysicalDeleteThread::PhysicalDelete()
{
	
	//////////////   결과값을 저장할 변수 //////////////////
	// 오류 메시지
	std::string szErrorMessage;
	
	// 요청에 대한 응답 대기
	int nTimeout = 5; // 응답 대기 시간 ( sec )
	int nResult = 0;
	bool bResultSuccess;									// 처리 결과의 성공/실패 여부를 저장하기 위한 변수.

	// ftsd 와  통신을 처리할 Socket Control 객체 생성 및 접속
	CFtsdSocketControl ftsdSocket;
	if( ftsdSocket.ConnectTarget( m_objDelFileInfo.m_szHostName, TRANSFER_LISTEN_PORT ) == false )
	{
		// Source ftsd 으로 접속 실패시
		_LOG( LERR, "Source FHS connect fail. [%s][%d]", m_objDelFileInfo.m_szHostName.c_str(), TRANSFER_LISTEN_PORT );
		return false;
	}
	// ftsd 로 파일에 대한 Check 요청 전달
	// SendFileCheckRequest() 함수는 FtsdSocketControl.h 파일 참조.
	if( ftsdSocket.SendFileUnlinkRequest( m_objDelFileInfo.m_szFileNameHash, m_objDelFileInfo.m_nContentLength, true ) == false )
	{
		// Source ftsd 로 전송 실패시
		_LOG( LERR, "Unlink Request send fail.[%s][%s]", m_objDelFileInfo.m_szHostName.c_str(), m_objDelFileInfo.m_szFileNameHash.c_str());
		return false;
	}
	// 루프를 돌면서 요청에 대한 응답을 대기
	while(1)
	{
		// nTimeout 에 지정된 시간동안 응답대기 시도
		// GetFileCheckResult() 함수는 SocketControl.h 파일 참고.
		nResult = ftsdSocket.GetFileUnlinkResult( nTimeout, bResultSuccess, szErrorMessage);
		
		if( nResult == -1 )
		{
			// Socket 통신 관련 오류 또는 접속 종료가 발생한 경우.
			// 해당 내역 로깅 및 루프 종료
			_LOG( LERR, "ftsd Response wait fail by socket [%s][%s]", m_objDelFileInfo.m_szHostName.c_str(), m_objDelFileInfo.m_szFileNameHash.c_str() );
			break;
		}
		else if( nResult == 0 )
		{
			// 지정된 시간 동안 응답대기 중 처리 결과 정보가 아직 수신되지 않은 경우 => Allive Check 패킷 한번 쏘고 다시 Loop 로
			
			if( ftsdSocket.SendAliveCheck() == false )
			{
				// Alive 전송 실패시 => Socket 종료 및 오류가 발생한 경우임.
				_LOG( LERR, "ftsd Response wait fail by socket2 [%s][%s]", m_objDelFileInfo.m_szHostName.c_str(), m_objDelFileInfo.m_szFileNameHash.c_str() );
				break;
			}
			// 정상적인 경우 다시 응답대기.
			continue;
			
		}
		else if( nResult == 1 )
		{
			// 요청에 대한 처리결과 정보가 수신된 경우.
			// 해당 정보 로깅처리.
			
			if( bResultSuccess == true )
			{
				// 요청에 대한 처리가 정상적으로 처리된 경우
				//_LOG( LINF, "[%s] Result SUCCESS [%llu][%s]", szFileName.c_str(), nFileSize, szHashValue.c_str());
				
				_LOG( LDBG, "Unlink result SUCCESS.");
			}
			else
			{
				// 오류 발생시 
				_LOG( LWAR, "[%s][%s] Result ERROR (%s)", m_objDelFileInfo.m_szFileNameHash.c_str(), m_objDelFileInfo.m_szHostName.c_str(), szErrorMessage.c_str() );
			}
			
			break;	// 응답을 받았으니 응답 대기 루프 종료
		}
		else
		{
			// Replication 요청에 대한 응답패킷이 아닌 경우.
			// 해당 패킷은 무시하고 다시 Loop 로 
			continue;
		}
	}
	return true;
}

///@brief 이 함수에서 컨트롤 로직이 표현된다.
void CPhysicalDeleteThread::Execute()
{
	FUNC_BEGIN();
	
	// 해당 함수 내에서 필요한 로깅은 모두 처리한다.
	PhysicalDelete();
	// 어떤 결과가 나오더라도 Meta 정보가 삭제 될 수 있도록 
	m_pQueueCompleteJob->Push(m_objDelFileInfo);
	// 쓰레드 수 감소.
	CProcessStatus::GetInstance()->SubtractJobThreadCount();
	FUNC_END();
}

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

	delete pObject;
	
	FUNC_END();
	return 0;
}

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

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