#include "Worker.h"

#include <signal.h>
#include <stdlib.h>
#include <time.h>

#include "Logger.h"
#include "ProcessRename.h"
#include "ProcessStatus.h"
#include "PreserveUpdater.h"
#include "GetContentListThread.h"
#include "DataQueue.h"
#include "CategorizeThread.h"
#include "PhysicalDeleteThread.h"
#include "CompleteThread.h"

/// @brief Worker Process 의 main 함수
int WorkerMain( )
{
	// Set Signal Handler
	SetSignalWorker();
	
	// Process Rename..
	set_ps_display( PROG_NAME": Worker", false );

    
    //-- singleton 객체들을 모두 생성 하도록 한다.
    //0. 명시적으로 처리하기 위해서..
    // Global     
    CQueue<CFileInfo> queueFileInfo;
    queueFileInfo.SetCurrentMaxSize(10000);
	
    CQueue<CFileInfo> queueDeleteJob;
    queueFileInfo.SetCurrentMaxSize(10000);
    
    CQueue<CFileInfo> queueCompleteJob;
    queueFileInfo.SetCurrentMaxSize(10000);
    
	// 1. 각 전역 변수 초기화 및 관련 Thread 기동 처리
	//-- 각 객체에 대해 초기화 및 시작 할 수 있는지 먼저 확인한다.
	CPreserveUpdater objPreserveUpdater;
	if ( objPreserveUpdater.ThreadInit() == false )
	{
		LOG( LERR, "CPreserveUpdater initialize failed." );
		exit( EXIT_FAILURE );	// EXIT_FAILURE 를 반환하여 재생성 처리 방지.
	}
	
	CGetContentListThread objContentList(&queueFileInfo, &queueDeleteJob, &queueCompleteJob);
	if ( objContentList.ThreadInit() == false )
	{
		LOG( LERR, "CGetContentListThread initialize failed." );
		exit( EXIT_FAILURE );	// EXIT_FAILURE 를 반환하여 재생성 처리 방지.
	}
    
    CCategorizeThread objCategorize(&queueFileInfo, &queueDeleteJob, &queueCompleteJob);
	if ( objCategorize.ThreadInit() == false )
	{
		LOG( LERR, "CCategorizeThread initialize failed." );
		exit( EXIT_FAILURE );	// EXIT_FAILURE 를 반환하여 재생성 처리 방지.
	}
    
    CCompleteThread objCompleteThread(&queueCompleteJob);
	if ( objCompleteThread.ThreadInit() == false )
	{
		LOG( LERR, "CCategorizeThread initialize failed." );
		exit( EXIT_FAILURE );	// EXIT_FAILURE 를 반환하여 재생성 처리 방지.
	}
	
    //-- 각 객체에 대해 검증이 끝났으므로 각 쓰레드를 구동 시킨다.
	if( objPreserveUpdater.Start() == false )
	{
		LOG( LERR, "Worker[%d]: CPreserveUpdater Start() failed. => Worker Exit", getpid() );
		exit( EXIT_FAILURE );	// EXIT_FAILURE 를 반환하여 재생성 처리 방지.
	}
	
	if( objContentList.Start() == false )
	{
		LOG( LERR, "Worker[%d]: CGetContentListThread Start() failed. => Worker Exit", getpid() );
		exit( EXIT_FAILURE );	// EXIT_FAILURE 를 반환하여 재생성 처리 방지.
	}
	
	if( objCategorize.Start() == false )
	{
		LOG( LERR, "Worker[%d]: CGetContentListThread Start() failed. => Worker Exit", getpid() );
		exit( EXIT_FAILURE );	// EXIT_FAILURE 를 반환하여 재생성 처리 방지.
	}

	if( objCompleteThread.Start() == false )
	{
		LOG( LERR, "Worker[%d]: CGetContentListThread Start() failed. => Worker Exit", getpid() );
		exit( EXIT_FAILURE );	// EXIT_FAILURE 를 반환하여 재생성 처리 방지.
	}
	
	// Job Control.....
	while(1)
	{
		CFileInfo objDelFileInfo;
		_LOG(LDBG , "DeleteThreadCount[Max: %d, Cur: %d]",  CProcessStatus::GetInstance()->GetMaxDeleteThread(), CProcessStatus::GetInstance()->GetDeleteThreadCount() ) ;
		
        if( CProcessStatus::GetInstance()->GetDeleteThreadCount() >= CProcessStatus::GetInstance()->GetMaxDeleteThread())
        {
        	sleep(1);
        	continue;
        }
        if( queueDeleteJob.GetSize() <= 0 )
        {
        	LOG(LDBG, "DeleteJobQueue is Empty...");
        	sleep(1);
        	continue;
        }
	    // 1. QueuePop
    	objDelFileInfo = queueDeleteJob.Front();
    	// Front 는 참조만하고 실제 pop은 Pop() 함수를 통해 한다.
    	queueDeleteJob.Pop();
    	
    	CPhysicalDeleteThread* pPhysicalDeleteTrhead = new CPhysicalDeleteThread(objDelFileInfo, &queueCompleteJob);
    	// 쓰레드 수 증가.
    	
	    if( pPhysicalDeleteTrhead == NULL || pPhysicalDeleteTrhead->Start() == false )
		{
			LOG( LERR, "Worker[%d]: CPhysicalDeleteThread Start() failed. => Worker Exit", getpid() );
			continue;
		}
	}
	
	// 생성된 Thread 의 Join 처리 ???
	while( 1 )
	{
		// 그냥 시그널 대기
		pause();
	}
    
	// Process 종료 처리.
	LOG( LWAR, "Worker Process[%d] exit.. Good Bye..", getpid());

	CLogger::Exit();
	CProcessStatus::Exit();
    
    //잠시 대기 후 종료처리.
	// CHG 2015-09-22 webting
	// - #24369 usleep -> nanosleep 으로 변경 처리.
	struct timespec sleep;
	sleep.tv_sec = 0;
	sleep.tv_nsec = 500000000;  // 0.5 sec
	nanosleep( &sleep, NULL );
	// exit( EXIT_SUCCESS );

	return EXIT_SUCCESS;	// fork 를 수행한 함수에서 exit 함수 호출을 통한 종료처리
}

/// @brief Worker Process 종료 Signal 을 전달받은 경우 이를 처리하기 위한 함수.
/// @param nSignalNumber [in] 발생한 시그널 Number
/// @return void
static void SigTermWorker( int nSignalNumber )
{
	// Signal Number 에 따른 로깅처리.
	if( nSignalNumber == SIGTERM )
	{
		LOG( LWAR, "Worker Process[%d] exit job start by user signal [SIGTERM]", getpid() );
	}
	else
	{
		LOG( LWAR, "Worker Process[%d] exit job start by abnormal signal [%d]", getpid(), nSignalNumber );
	}
    
	LOG( LWAR, "Worker Process[%d] exit job end. Good Bye..", getpid());
    
	
	// CHG 2015-09-22 webting
	// - #24369 usleep -> nanosleep 으로 변경 처리.
	struct timespec sleep;
	sleep.tv_sec = 0;
	sleep.tv_nsec = 500000000;  // 0.5 sec
	nanosleep( &sleep, NULL );
	exit( EXIT_SUCCESS );
}


/// @brief Worker 프로세스 signal 처리 설정을 위한 함수	
/// @return void	
void SetSignalWorker( void )
{
	sigset_t set;
	struct sigaction act;

	sigfillset( &set );
	sigprocmask( SIG_SETMASK, &set, NULL );

	memset( &act, 0x00, sizeof(act) );
	sigfillset( &act.sa_mask );

	/* 무시할 신호 목록 */
	act.sa_handler = SIG_IGN;
	sigaction( SIGPIPE, &act, NULL);	/* 파이프 디스크립터 오류 발생시 Process가 죽는 것은 방지하기 위하여 설정 */
	sigaction( SIGHUP , &act, NULL);	/* Process를 기동시킨 관리자의 로그아웃시 발생 시그널 */
	sigaction( SIGINT , &act, NULL);	/* ^C 키를 누른 경우 받는 신호 => demon 으로 기동되기 땜시 이 신호 못받음 */
	sigaction( SIGQUIT, &act, NULL);	/* 키보드에 의한  Abort 신호 처리 => ? */
	
	// Worker 프로세스는 자식 프로세스가 존재하지 않으므로 그냥 무시처리함.
	//act.sa_handler = SigChldWorker;
	sigaction( SIGCHLD, &act, NULL);    
	
	/* 각종 에러나 사용자의 종료 신호 처리 */
	act.sa_handler = SigTermWorker;
	sigaction( SIGTERM, &act, NULL);	/* kill -TERM 에 의한 프로세스 종료시 */

	sigemptyset(&set);                  /* 신호 처리기 처리 설정 위한 블록 해제 */
	sigprocmask(SIG_SETMASK, &set, NULL);
}
