/***************************************************************************
                   Login Info Inserter class
                -----------------------------------------

	begin      : 2013/09/16
	copyright  : (C) 2005 Solbox Inc.
	author     : Development 1 Team
					- 2013/09/16
	email      : dev1@solbox.com
	version    : 3.2.0

	CopyRight(C) 2005 Solbox Inc. All Rights reserved.
	Redistribution and use in source and binary forms, with or with out
	modification, are not permitted in outside of Solbox Inc.
***************************************************************************/
#include <fcntl.h>
#include <errno.h>
#include "Database.h"
#include "pthread.h"
#include "time.h"
#include "LoginInfoInserter.h"

extern struct st_config g_envConfig;

#define DEFAULT_INSERT_INTERVAL  ((1)  * (300) ) // 300sec
//#define DEFAULT_INSERT_INTERVAL  ((1)  * (3) ) // 300sec

#define DEFAULT_BUFFER_SIZE 2048

CLoginInfoInserter::CLoginInfoInserter(CQueue<CLoginInfo> *pLoginInfo )
	: m_pLoginInfo (pLoginInfo)
{
}

CLoginInfoInserter::~CLoginInfoInserter()
{
	if( m_pThreadHandle != NULL )
	{
		pthread_cancel( *m_pThreadHandle );
		delete m_pThreadHandle;
		m_pThreadHandle = NULL;
	}

    //pthread_cancel(m_threadHandle);
}

/// @brief
/// 입력받은 데이터가 유효한지만 확인 하기 위해 DataBase를 로컬로 선언한다.
bool CLoginInfoInserter::Init(const std::string &szHost, const int nPort, const std::string &szDBName, const std::string &szAcct, const std::string &szPasswd)
{
	DataBase * pPgSQL = new DataBase;

	if( szHost.empty() 
		|| nPort < 0
		|| szDBName.empty()
		|| szAcct.empty()
		|| szPasswd.empty() )
	{
		// invalid argument
		return false;
	}
	/// @brief DB connection information
	m_szHost = szHost;
	m_nPort = nPort;
	m_szDBName = szDBName;
	m_szAcct = szAcct;
	m_szPasswd = szPasswd;

	if( pPgSQL == NULL )
	{
		LOG( LERR, "Creating new Database has failed.");
		return false;
	}
	if( pPgSQL->PgOpenDB(m_szHost, m_nPort, m_szDBName, m_szAcct, m_szPasswd) == NULL )
	{
		LOG( LERR, "Connecting to Database has failed");
		if (pPgSQL != NULL) 
		{
			delete pPgSQL;
			pPgSQL = NULL;
		}
		return false;
	}

	if (pPgSQL != NULL) 
	{
		delete pPgSQL;
		pPgSQL = NULL;
	}

	return true;
}

/// @brief DB 연결 함수.
bool CLoginInfoInserter::DbConnect()
{
    m_pPgSQL = new DataBase;

	if( m_pPgSQL == NULL )
	{
		LOG( LERR, "Creating new Database has failed.");
		return false;
	}

	if( m_pPgSQL->PgOpenDB(m_szHost, m_nPort, m_szDBName, m_szAcct, m_szPasswd) == NULL )
	{	
		LOG( LERR, "Connecting to Database has failed.");
		DbClose();
		return false;
	}
    return true;
}

/// @breif DB close 함수.
void CLoginInfoInserter::DbClose()
{
	if( m_pPgSQL != NULL )
	{
		delete m_pPgSQL;
        m_pPgSQL = NULL;
	}
}

bool CLoginInfoInserter::Execute()
{
	char szQuery[DEFAULT_BUFFER_SIZE];

    if ( DbConnect() == false)
	{
		LOG(LERR,  "DbConnect() has returned false, so Execute() return false." );
        return false;
	}
    
	while(m_pLoginInfo->GetDepth() > 0)
	{
		CLoginInfo objLoginInfo = m_pLoginInfo->Front();

    	// 1. 인서트 쿼리를 생성.
    	snprintf(szQuery, DEFAULT_BUFFER_SIZE - 1,
                "INSERT INTO cs_service.cs_login_log( "
                "id, module, client_ip, login_date) "
                "VALUES ('%s', '%s', '%s', '%s');"
                ,objLoginInfo.m_szUserID.c_str(), objLoginInfo.m_szIssuer.c_str(), objLoginInfo.m_szClientIP.c_str(), objLoginInfo.m_szLoginDate.c_str()); 
         
        // 2. 인서트 쿼리 수행.
    	m_pPgSQL->PgDoExec(szQuery);
    	if ((m_pPgSQL->PgResult(DataBase::CLEAR)) < 0) 
    	{
    		LOG(LERR, "Query failed. errmsg:%s\nQuery:%s", m_pPgSQL->GetErrorMessage().c_str(), szQuery);
    		if (m_pPgSQL != NULL) 
    		{
    			delete m_pPgSQL;
    			m_pPgSQL = NULL;
    		}
			// 실패 한 경우는 DB Connect 부터 다시 하도록 종료 한다.
    		break;
    	}

		// 3. 쿼리가 정상처리 되었다면 pop()함수를 이용해서 Queue에서 제거한다.
		m_pLoginInfo->Pop();
	}
	
    DbClose();

	return true;
}

void* CLoginInfoInserter::EntryPoint(void* arg)
{
    CLoginInfoInserter* pObject = reinterpret_cast<CLoginInfoInserter *>(arg);
	pthread_detach( pthread_self() );
    
    while( 1 )
    {
        pthread_testcancel();

		//LoginData가 0개이하면 다시 대기
		if (pObject->m_pLoginInfo->GetDepth() <= 0) 
		{
			LOG( LDBG, "Empty Queue!");
			sleep( DEFAULT_INSERT_INTERVAL );
			continue;
		}

		if( pObject->Execute() == false )
		{
			LOG( LWAR, "Execute() has returned false, so retry Execute() after 10 second.");
		}

		sleep(DEFAULT_INSERT_INTERVAL );
        pthread_testcancel();
    }
    
	// pthread_exit();
	return NULL;
}

// DO *NOT* FIX IT
bool CLoginInfoInserter::Start()
{
	m_pThreadHandle = new pthread_t;

	//int nRet = ::pthread_create(&m_threadHandle, 0, CLoginInfoInserter::EntryPoint, this);
	int nRet = ::pthread_create(m_pThreadHandle, 0, CLoginInfoInserter::EntryPoint, this);

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