#include "rc_cmove.h"
#include "Logger.h"
#include "parameter.h"
#include "dbconnpool.h"
#include "workpool.h"
#include "jobmanager.h"
#include "monitoring.h"
#include "RcSyncdRequest.h"


#ifdef _DEBUG
#define		DBPOOL_KEEPALIVE_TIMEOUT	10  // 10 sec
#else // !_DEBUG
#define		DBPOOL_KEEPALIVE_TIMEOUT	300  // 30 min
#endif // _DEBUG

static bool _bexit = false;

static void SigTermMain( int nSignalNumber )
{
	ostringstream msg;
	if( _bexit == true ) 
		return;

	_bexit= true;
	// Signal Number 에 따른 로깅처리.
	if( nSignalNumber == SIGTERM )
	{
		msg << "Main Process exit job start by user signal [SIGTERM]";
	}
	else
	{
		msg << "Main Process exit job start by user signal ["<< nSignalNumber <<"]";
	}
	// Process 종료관련 작업 추가
	LOGACERR(LWAR, msg);

	// waitpid
	while(waitpid(-1, NULL, WNOHANG) > 0);
	//g_pLog = NULL;
	
	solusleep(500000);

	// end
	monitoring::getInstance()->release();
	exit( EXIT_SUCCESS );
}

// signal
static void SetSignalMain()
{
	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( SIGQUIT, &act, NULL);	/* 키보드에 의한  Abort 신호 처리 => ? */

	// Child Process 종료에 대한 처리기 설정.
	//act.sa_handler = sigchld_hdl;
	//sigaction( SIGCHLD, &act, NULL);
	/* 각종 에러나 사용자의 종료 신호 처리 */
	act.sa_handler = SigTermMain;
	sigaction( SIGTERM, &act, NULL);	/* kill -TERM 에 의한 프로세스 종료시 */
	sigaction( SIGINT, &act, NULL);	/*  ^C 키를 누른 경우 받는 신호 => kill -TERM 과 동일한 처리함 */

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

// mode sync
void chksync()
{
	ostringstream msg;
	msg << "Mode Sync Check...";
	_LOGACOUT(LINF, msg);

	string service = parameter::getInstance()->getservice();
	string sdate = parameter::getInstance()->getsyncstart();
	string edate = parameter::getInstance()->getsyncend();

	if (sdate.empty() || edate.empty())
	{
		msg << "Empty date.(-p start_date:end_date)  " << service;
		_LOGACERR(LERR, msg);
		return;
	}

	msg << "START SYNC CHECK: " << service;
	_LOGACOUT(LINF, msg);

	if (sdate.compare(edate) > 0)
	{
		msg << "Start time is greater than End time.";
		_LOGACERR(LERR, msg);
	}
	else
	{
		SYNC_TYPE::SYNC_TYPE synct = SYNC_TYPE::CHK_ONLY_N_ONLY;

		if (parameter::getInstance()->includdel())
			synct = SYNC_TYPE::CHK_ONLY;

		// sync 요청 처리 
		// scheduler -> rc_syncd (local host)
		// 성공 실패 여부는 objRcSyncdRequest 객체 내부에서 로깅 처리 함.
		CRcSyncdRequest objRcSyncdRequest;

		if (service.empty())
		{
			objRcSyncdRequest.SendSyncRequest(synct, sdate, edate);
		}
		else
		{
			objRcSyncdRequest.SendSyncRequest(synct, sdate, edate, service);
		}
	}

	msg << "END SYNC CHECK: " << service;
	_LOGACOUT(LINF, msg);

}

// mode copy and move
void copyamove(JOB_TYPE::JOB_TYPE type)
{
	ostringstream msg;
	
	string mode = (type == JOB_TYPE::COPY ? MODE_COPY : MODE_MOVE);

	// create DB connection
	masterdbpool::init(parameter::getInstance()->getdbinfo(parameter::MASTER));
	if (masterdbpool::getInstance()->CreatePool(
		parameter::getInstance()->getdbpoolcnt(parameter::MASTER)) <= 0)
	{
		msg << "database connection pool created fail(Master).";
		LOGACERR(LERR, msg);
		return ;
	}

	masterdbpool::getInstance()->SetKeepAliveTimeout(DBPOOL_KEEPALIVE_TIMEOUT);

	// create work pool
	if (workpool::getInstance()->CreatePool(parameter::getInstance()->getworkpoolcnt()) != 0)
	{
		cerr << "work pool created fail." << endl;
		return ;
	}

	msg << "RCDB : ";
	msg << (masterdbpool::getInstance()->UseDisplayname() ? "NEW" : "OLD");
	msg << " Type.";
	_LOGACOUT(LINF, msg);

	msg << "Mode " << mode << "...";
	_LOGACOUT(LINF, msg);

	string syncfilepath = parameter::getInstance()->getsyncpath();

	if (syncfilepath.empty())
	{
		msg << "Not found sync file. and re-run -f [sync file]" << syncfilepath;
		_LOGACERR(LERR, msg);
		return;
	}

	msg << "START : sync path = " << syncfilepath;
	_LOGACOUT(LINF, msg);

	vector<jobmanager *> jobs;
	jobmanager *newjob = new jobmanager(type, syncfilepath.c_str());
	if (newjob->run() == 0)
	{
		jobs.push_back(newjob);
	}
	else
	{
		delete newjob;
	}

	if (jobs.size() == 0)
	{
		msg << "sync job empty.";
		_LOGACERR(LERR, msg);
	}

	// set monitoring
	monitoring::getInstance()->setjobs(&jobs);

	// wait job end
	int rc;
	while (jobs.size() > 0)
	{
		jobmanager *job = static_cast<jobmanager *> (*jobs.begin());
#ifdef _DEBUG
		cout << "Wait Job...." << endl;
#endif // _DEBUG
		rc = pthread_join(job->getjobhandle(), NULL);
		if (rc == 0)
		{
			monitoring::getInstance()->monlock();
			job->setexit(true);
			jobs.erase(jobs.begin());
			delete job;
			job = NULL;
			if (jobs.empty())
				monitoring::getInstance()->setexit();
			monitoring::getInstance()->monunlock();
		}
	}	

	msg << "END : sync file = " << syncfilepath;

	
	if (type == JOB_TYPE::COPY)
	{
		unlink(syncfilepath.c_str());
	}
	
	_LOGACOUT(LINF, msg);
}

// main function
int main( int argc, char * argv[] )
{
	ostringstream msg;

	// setting signal 
	SetSignalMain();

	// argument to data
	if( parameter::getInstance()->argvtoparam(argc, argv) == false )
		return EXIT_FAILURE;

	// load config
	if( parameter::getInstance()->loadconf() == false )
		return EXIT_FAILURE;

	// log init
	if( CLogger::Init( PROG_NAME, parameter::getInstance()->getlogpath(),
		parameter::getInstance()->getloglevel() ) == false )
	{
		cerr << "Logger init failed."  << endl;
		return EXIT_FAILURE;
	}

	// monitoring init
	if( monitoring::getInstance()->init() == false)
	{
		cerr << "monitoring created fail." << endl;
	}

	// work
	bool runfail = false;
	string mode = parameter::getInstance()->getsyncmode();
	if (mode.compare(MODE_SYNC) == 0)
	{
		chksync();
	}
	else if (mode.compare(MODE_COPY) == 0)
	{
		copyamove(JOB_TYPE::COPY);
	}
	else if (mode.compare(MODE_MOVE) == 0)
	{
		copyamove(JOB_TYPE::MOVE);
	}
	else
	{
		runfail = true;
		msg << "This mode is not supported.[-m " << mode << "]";
		_LOGACOUT(LERR, msg);
	}

	// end
	if (workpool::getInstance())
		workpool::getInstance()->release();

	if (masterdbpool::getInstance())
		masterdbpool::getInstance()->release();
	
	if (monitoring::getInstance())
		monitoring::getInstance()->release();
	parameter::getInstance()->release();

	CLogger::Exit();
	return (runfail ? EXIT_FAILURE: EXIT_SUCCESS);
}
