#include "ProcessConfig.h"

#include <stdlib.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>

#include "Logger.h"


// static 변수 초기화
CProcessConfig * CProcessConfig::m_pInstance = NULL;


// 생성자
CProcessConfig::CProcessConfig()
{
	// default 값 설정.
	m_bRunIntegrityCheck	= false;
	m_bRunGarbageClean		= false;
	m_bGarbageCleanSlow		= false;
	m_bGarbageForceDelete	= false;
	m_bUserDefineJobRoot	= false;

	m_strJobRoot.clear();
	m_strHostname.clear();	
}

// 소멸자 
CProcessConfig::~CProcessConfig()
{
	// 객체 소멸시... 
	// 만약 static GetInstance() 함수가 가르키는 객체가 자기 자신이라면...
	// 소멸 처리에 의해 문제가 발생할 수 있으므로... 이에 대한 처리를 해 준다.
	if( CProcessConfig::GetInstance() == this )
	{
		CProcessConfig::m_pInstance = NULL;
	}
}

bool CProcessConfig::Init( const std::string strProgramName, const std::string & strConfFileName, std::string & strErrorMessage )
{

	// 1. 임시 처리용 Instance 를 생성한다.
	CProcessConfig * pTempInstance = new CProcessConfig();

	// 2. 전달 받은 Config 정보를 임시 Instance 로 로딩한다.
	//   - 만약 로딩이 실패할 경우... 임시 Instance 객체를 삭제 처리한다.
	if( pTempInstance->Load( strProgramName, strConfFileName, strErrorMessage ) == false )
	{
		delete pTempInstance;
		pTempInstance = NULL;

		return false;	// 생성 실패 사유는 Error String 관련 함수를 사용하여 확인.
	}

	// 3. 전달받은 Conf 정보 로딩에 성공한 경우...
	//   - 기존 Instance 객체가 존재하는 경우.. 교체 처리..
	//   - 기존 Instance 객체가 없는 경우는 신규 Instance 사용토록 처리.
	if( CProcessConfig::GetInstance() == NULL )
	{
		CProcessConfig::m_pInstance = pTempInstance;
	}
	else
	{
		// Instance replace...
		CProcessConfig * pPreviosInstance = CProcessConfig::m_pInstance;
		CProcessConfig::m_pInstance = pTempInstance;
		
		delete pPreviosInstance;
	}

	return true;
}


CProcessConfig * CProcessConfig::GetInstance()
{ 
	return CProcessConfig::m_pInstance;
}


bool CProcessConfig::Load( const std::string & strProgramName, const std::string & strConfFileName, std::string & strErrorMessage )
{
	// 변수 유효성 확인
	if( strProgramName.size() <= 0 )
	{
		strErrorMessage = "Config Program Name[" + strProgramName + "] not valid.";
		return false;
	}

	// Config class (lib 하위) 를 이용하여 config 파일 상의 정보를 로딩 처리 
	Config conf;

	if( conf.Open( strConfFileName ) == false )
	{
		strErrorMessage = "Config file[" + strConfFileName + "] open failed.";
		return false;
	}

	// Config 모듈에 로딩된 정보 중 필요한 정보만 가져온다...
	// - 만약 오류가 발생할 경우... 해당 내역은 Error String 에 저장 처리...

	std::string strValue;
	//std::vector< std::string > vecValue;

	// DEFAULT_LOG_DIR
	if( GetConfigValue( conf, strProgramName, "DEFAULT_LOG_DIR", strValue, strErrorMessage ) == false )
		return false;
	else
	{
		m_strLogPath = strValue;
		strValue.clear();
	}

	// LOG_LEVEL
	if( GetConfigValue( conf, strProgramName, "LOG_LEVEL", strValue, strErrorMessage ) == false )
		return false;
	else
	{
		m_nLogLevel = atoi(strValue.c_str());
		if( m_nLogLevel < 0 || m_nLogLevel > MAX_LOG_LEVEL )
		{
			strErrorMessage = "Config [LOG_LEVEL] value[" + strValue + "] is not valid";
			return false;
		}		
		strValue.clear();
	}
	

	// FILE_STORAGE_ROOT
	// - 작업 대상 경로는 최초 초기화시... conf 의 FILE_STORAGE_ROOT 정보로 설정 처리한다.
	if( GetConfigValue( conf, strProgramName, "FILE_STORAGE_ROOT", strValue, strErrorMessage ) == false )
		return false;
	else
	{
		m_strStorageRoot = strValue;
		strValue.clear();
	}
	
	
	// RCDB IP
	if( GetConfigValue( conf, strProgramName, "RCDB_IP", strValue, strErrorMessage ) == false )
		return false;
	else
	{
		m_strRcdbIp = strValue;
		strValue.clear();
	}

	// RCDB PORT
	if( GetConfigValue( conf, strProgramName, "RCDB_PORT", strValue, strErrorMessage ) == false )
		return false;
	else
	{
		m_nRcdbPort = atoi( strValue.c_str() );

		if( m_nRcdbPort <= 0 || m_nRcdbPort > 65535 )
		{
			strErrorMessage = "Config [RCDB_PORT] value[" + strValue + "] is not valid";
			return false;
		}

		strValue.clear();
	}

	// RCDB DB NAME
	if( GetConfigValue( conf, strProgramName, "RCDB_DB_NAME", strValue, strErrorMessage ) == false )
		return false;
	else
	{
		m_strRcdbName = strValue;
		strValue.clear();
	}

	// RCDB ACCOUNT
	if( GetConfigValue( conf, strProgramName, "RCDB_ACCT", strValue, strErrorMessage ) == false )
		return false;
	else
	{
		m_strRcdbAcct = strValue;
		strValue.clear();
	}

	// RCDB ACCOUNT PASSWORD
	if( GetConfigValue( conf, strProgramName, "RCDB_ACCT_PW", strValue, strErrorMessage ) == false )
		return false;
	else
	{
		m_strRcdbAcctPw = strValue;
		strValue.clear();
	}
	


	// conf 정보 로딩이 모두 정상적으로 완료된 경우... 마지막으로 전달받은 config 명, section 정보를 저장한다.
	m_strConfigFileName = strConfFileName;
	m_strProgramName    = strProgramName;

	return true;
}


bool CProcessConfig::GetConfigValue( Config & conf, const std::string & strProgramName, const std::string strKey, std::string & strValue, std::string & strErrorMessage )
{
	// conf get value
	if( conf.GetConfig( strProgramName, strKey, strValue) == false )
	{
		if( conf.GetConfig( "COMMON", strKey, strValue) == false )
		{
			strErrorMessage = "Config [" + strKey + "] value is not exist";

			return false;
		}
	}

	// value check.
	if( strValue.empty() == true )
	{
		strErrorMessage = "Config [" + strKey + "] value is not valid";
		return false;
	}


	return true;
}


// 작업을 수행할 content 저정 경로 정보를 반환한다.
// - 사용자가 옵션으로 경로를 지정한 경우.. 해당 정보를 반환.
// - 사용자가 옵션으로 경로를 지정하지 않은 경우... conf 에 정의된 FILE_STORAGE_ROOT 값을 반환한다.
// - 사용자의 옵션 지정 여부는 IsUserDefineJobRoot() 함수를 이용하여 확인 가능.
const char * CProcessConfig::GetJobRoot()
{
	if( m_bUserDefineJobRoot == false )
		return m_strStorageRoot.c_str();
	else
		return m_strJobRoot.c_str();
}

// 사용자가 옵션으로 작업 경로를 지정한 경우.. 해당 정보를 저장 처리
// - 지정된 경로는 conf 의 FILE_STORAGE_ROOT 설정값 하위 폴더이어야 하며...
// - 오류 발생시.. 해당 내역을 strErrorMessage 에 저장하여 반환 처리.		
bool CProcessConfig::SetJobRoot( std::string & strPath, std::string & strErrorMessage )
{

	if( m_strStorageRoot != strPath )
	{
		// conf 에 정의된 FILE_STORAGE_ROOT 설정값과 옵션으로 입력받은 path 값이 서로 다른 경우...

		// 폴더 비교 처리를 위해.. 마지막에. '/' 이 포함되어 있지 않은 경우... 붙여서 비교 처리하도록 한다.
		std::string strStorageRoot = m_strStorageRoot;
		if( strStorageRoot.at( strStorageRoot.size() - 1 ) != '/' )
			strStorageRoot.append( "/" );

		if( strncmp( strStorageRoot.c_str(), strPath.c_str(), strStorageRoot.size() ) != 0 )
		{
			// FILE_STORAGE_ROOT 의 하위 폴더가 아닌 경우...
			strErrorMessage = "user path[" + strPath + "] not valid. set sub path of conf value[" + m_strStorageRoot + "]";
			return false;			
		}
		else
		{
			// FILE_STORAGE_ROOT 의 하위 폴더 인 경우.
			// - 저장 규칙에 따라.. 전달받은 경로의 맨 끝에 '/' 가 존재하는 경우.. 제거 처리한다.
			m_strJobRoot = strPath;
			if( m_strJobRoot.at( m_strJobRoot.size() - 1 ) == '/' )
				m_strJobRoot.erase( m_strJobRoot.size() - 1 );

			// /stg/ 로 사용자가 정의한 경우.. 최종 검사로 판별하기 위해 다시한번 검사 처리.
			if( m_strStorageRoot != m_strJobRoot )
				m_bUserDefineJobRoot = true;
			else
			{
				m_bUserDefineJobRoot = false;
				m_strJobRoot.clear();
			}
		}
		
	}
	else
	{
		// conf 에 정의된 FILE_STORAGE_ROOT 설정값과 옵션으로 입력받은 path 값이 동일한 경우...
		m_bUserDefineJobRoot = false;
	}

	// 최종 결정된 Job Path directory 가 실제 존재하는지 확인한다.
	std::string strJobRoot = GetJobRoot();
	struct stat dirStat;

	if( lstat( strJobRoot.c_str(), &dirStat ) != 0 )
	{
		strErrorMessage = "path not valid. check path [" + strJobRoot + "] [" + strerror( errno ) + "]";

		return false;
	}

	// 해당 정보가 Directory 가 아닌 경우
	if( S_ISDIR( dirStat.st_mode ) == false )
	{
		strErrorMessage = "path is not directory. check path [" + strJobRoot + "]";
		return false;
	}
	
	return true;
}



// hostname 처리 관련 함수
bool CProcessConfig::SetHostname( const char * name )
{
	if( name == NULL )
		return false;

	if( strlen( name ) == 0 )
		return false;
	
	m_strHostname = name;

	return true;
}

const char * CProcessConfig::GetHostname()
{
	return m_strHostname.c_str();
}
