#include "LocalDbClearThread.h"
#include "Logger.h"
#include "ProcessConfig.h"

#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <stdio.h>
#include <sqlite3.h>


#define SQLITE_DB_LOCK_TIMEOUT       3000   // 세션 중에 SQLITE_BUSY 상황 발생 문제 해결을 위해 DB Lock 관련 timeout 설정값. ( 3 sec )


// 생성자..
CLocalDbClearThread::CLocalDbClearThread()
{
	// Thread Handle 초기화
	m_threadHandle = 0;
}

// 소멸자..
CLocalDbClearThread::~CLocalDbClearThread()
{
	// Thread 가 동작 중인 경우 Thread 동작 정지 처리.
	if( m_threadHandle != 0 )
		pthread_cancel(m_threadHandle); 
	
}


// Thread 에 정의된 작업을 시작한다...
bool CLocalDbClearThread::Start()
{
	int nResult = pthread_create( &m_threadHandle, NULL, CLocalDbClearThread::threadFunc, this );
	if( nResult != 0 )
	{
		// Thread 생성 실패시...
		int errorNum = errno;
		LOG( LERR, "LocalDbClearThread: Thread create failed.[%d][%s]", errorNum, strerror(errorNum) );

		return false;
	}

	return true;
}


// Thread 함수...
void * CLocalDbClearThread::threadFunc( void * arg )
{
	CLocalDbClearThread * pObject = reinterpret_cast<CLocalDbClearThread *>(arg);
	pthread_detach( pthread_self() );

	// Local DB 정리 작업은 기동시 1번 수행하고...  
	sleep( 1 );

	pthread_testcancel();
	pObject->Execute();
	pthread_testcancel();

	// Thread 종료시 Handle 초기화
	pObject->m_threadHandle = 0;
	
	return NULL;
}

// Local DB 정리 작업은 중요도가 낮은 작업이므로... 1일 1회만 수행되도록 한다.
void CLocalDbClearThread::Execute()
{
	// 변수 선언 및 초기화..

	// Local DB 관련 변수 초기화.
	sqlite3 * db  = NULL;
	int db_result;
	int nRetryCount;
	char szQuery[1024];
	char * db_err;


	// 주기적으로 Local DB 에 대해 
	// 보관 기간이 초과되었고.... 
	// 전송 처리가 완료된 row 를 삭제 처리한다.

	while( 1 )
	{

		// Local DB 와 연결을 수행한다.		
		// - Network, Storage 통계 정보 clear 처리

		db_result = sqlite3_open( CProcessConfig::GetInstance()->GetLocalDbFileName(), &db );
		if( db_result != SQLITE_OK )
		{
			LOG( LERR, "LocalDbClearThread: sqlite3 open failed.[%s][%d][%s]", CProcessConfig::GetInstance()->GetLocalDbFileName(), db_result, sqlite3_errmsg( db ) );
			sqlite3_close( db );
			db = NULL;
		}
		else
		{
			// DB File 이 open 된 경우...

			// 1. network_stat 테이블을 정리한다.
			// - 각 FHS 로 부터 5분단위로 수신된 정보이므로... FHS 수가 많을 수록 보관량이 많다.
			// - 따라서 전송되지 않은 정보를 제외하고.. 7 day 지난 row 는 삭제처리한다. (보존기간 7 day, #21025)

			_LOG( LINF, "LocalDbClearThread: Local DB network_stat table clear start." );
			
			snprintf( szQuery, 1023,
					  "DELETE FROM network_stat WHERE send_flag != 0 AND insert_time < date('now', 'localtime', '-7 day') " );

			db_result = sqlite3_exec( db, szQuery, NULL, NULL, &db_err );
			nRetryCount = 0;

			// 만약 BUSY 상태로 인해 오류가 발생하면..10번 재시도
			while( db_result == SQLITE_BUSY  && nRetryCount < 10 )
			{
				sqlite3_free( db_err );
				sqlite3_busy_timeout( db, SQLITE_DB_LOCK_TIMEOUT );
				++nRetryCount;

				LOG( LWAR, "LocalDbClearThread: local db delete sqlite3_exec SQLITE_BUSY. Retry [%d]", nRetryCount );

				// Query 재수행
				db_result = sqlite3_exec( db, szQuery, NULL, NULL, &db_err );
			}

			if( db_result != SQLITE_OK )
			{
				LOG( LERR, "LocalDbClearThread: Local DB network_stat table clear failed.[%s][%s]", db_err, szQuery );
				sqlite3_free( db_err );
			}
			else
			{
				_LOG( LINF, "LocalDbClearThread: Local DB network_stat table clear end. [%d] rows deleted.", sqlite3_changes( db ) );
			}


			// 2. 서비스별 Storage 사용량 정보가 저장된 storage_stat 테이블을 정리한다.
			// - 각 서비스별로 5분당 1개 Row 가 생성되므로... network_stat 테이블에 비해 row 수가 많지는 않다.
			// - 따라서 전송되지 않은 정보를 제외하고.. 7 day 지난 row 는 삭제처리한다. (보존기간 7 day, #21025)

			_LOG( LINF, "LocalDbClearThread: Local DB storage_stat table clear start." );
			
			snprintf( szQuery, 1023,
					  "DELETE FROM storage_stat WHERE send_flag != 0 AND insert_time < date('now', 'localtime', '-7 day') " );

			db_result = sqlite3_exec( db, szQuery, NULL, NULL, &db_err );
			nRetryCount = 0;

			// 만약 BUSY 상태로 인해 오류가 발생하면..10번 재시도
			while( db_result == SQLITE_BUSY  && nRetryCount < 10 )
			{
				sqlite3_free( db_err );
				sqlite3_busy_timeout( db, SQLITE_DB_LOCK_TIMEOUT );
				++nRetryCount;

				LOG( LWAR, "LocalDbClearThread: local db delete sqlite3_exec SQLITE_BUSY. Retry [%d]", nRetryCount );

				// Query 재수행
				db_result = sqlite3_exec( db, szQuery, NULL, NULL, &db_err );
			}

			if( db_result != SQLITE_OK )
			{
				LOG( LERR, "LocalDbClearThread: Local DB storage_stat table clear failed.[%s][%s]", db_err, szQuery );
				sqlite3_free( db_err );
			}
			else
			{
				_LOG( LINF, "LocalDbClearThread: Local DB storage_stat table clear end. [%d] rows deleted.", sqlite3_changes( db ) );
			}

			// 3. 최종 Local DB 와의 연결을 해제한다.
			sqlite3_close( db );
			db = NULL;
		}
		

		// 본 루프는 1일 1회만 동작하도록 처리한다.
		// 24시간 * 60분 * 60 sec = 86400
		sleep( 86400 );
	}
	

	return;
}

