#include "ServiceConfig.h"

#include <stdlib.h>

#include "Logger.h"


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


// 생성자
CServiceConfig::CServiceConfig()
{
	// default 값 설정.
	// 지정된 conf 파일에 해당 설정이 없어도 동작되도록 처리할 값에 대해서만 default 값을 정의한다.	
	m_nChkSmall      =   60;
	m_nChkMiddle     = 3600;
	m_strDiffCommand = "/user/service/etc/diff_com.sh";
	m_strRcCMove	 = "/user/service/bin/rc_cmove";
	m_vecFHSMount.push_back("/stg/node0");
	m_vecFHSMount.push_back("/stg/node1");
	m_vecFHSMount.push_back("/stg/node2");
}

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

}

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

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

	// 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( CServiceConfig::GetInstance() == NULL )
	{
		CServiceConfig::m_pInstance = pTempInstance;
	}
	else
	{
		// Instance replace...
		CServiceConfig * pPreviosInstance = CServiceConfig::m_pInstance;
		CServiceConfig::m_pInstance = pTempInstance;
		
		delete pPreviosInstance;
	}

	return true;
}


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


// 각 항목별 제약값은 기존 service_sync 모듈 소스를 참고하여 정의함.
bool CServiceConfig::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;
	int nValue;
	std::vector< std::string > vecValue;

	
	// Log path
	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
	{
		nValue = atoi(strValue.c_str());		
		if( nValue < 0 || nValue > MAX_LOG_LEVEL )
		{
			strErrorMessage = "Config [LOG_LEVEL] value[" + strValue + "] is not valid";
			return false;
		}
		else
			m_nLogLevel = nValue;
		
		strValue.clear();
	}

	// RC ID
	if( GetConfigValue( conf, strProgramName, "RC_ID", strValue, strErrorMessage ) == false )
		return false;
	else
	{
		m_strRcId = 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
	{
		nValue = atoi(strValue.c_str());
		if( nValue <= 0 || nValue > 65535 )
		{
			strErrorMessage = "Config [RCDB_PORT] value[" + strValue + "] is not valid";
			return false;
		}
		else
			m_nRcdbPort = nValue;
		
		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();
	}

	// Skip keyword : h|path (head) or m|path(middle) or t|path(tail)
	// - 본 설정갑은 없어도 동작하도록 처리한다.
	if( conf.GetConfig( strProgramName, "SKIP_KEYWORD", vecValue ) == true )
	{
		// 해당 값이 존재할 경우..	
		// Multi value 이므로 갯수 검사
		if( vecValue.size() > 0 )
		{
			m_vecSkipWord = vecValue;			
		}

		vecValue.clear();
	}
	else
	{
		// program section 에 존재하지 않을 경우... COMMON 세션 확인
		if( conf.GetConfig( "COMMON", "SKIP_KEYWORD", vecValue ) == true )
		{
			// 해당 값이 존재할 경우..	
			// Multi value 이므로 갯수 검사
			if( vecValue.size() > 0 )
			{
				m_vecSkipWord = vecValue;			
			}

			vecValue.clear();
		}
	}


	
	// Operator Command Port, OP_PORT = 14002
	if( GetConfigValue( conf, strProgramName, "OP_PORT", strValue, strErrorMessage ) == false )
		return false;
	else
	{
		nValue = atoi(strValue.c_str());
		if( nValue <= 0 || nValue > 65535 )
		{
			strErrorMessage = "Config [OP_PORT] value[" + strValue + "] is not valid";
			return false;
		}
		else
			m_nOperationPort = nValue;
		
		strValue.clear();
	}

	// Database connection pool count, DB_POOL = 4
	if( GetConfigValue( conf, strProgramName, "DB_POOL", strValue, strErrorMessage ) == false )
		return false;
	else
	{
		nValue = atoi(strValue.c_str());
		if( nValue < 0 )
		{
			strErrorMessage = "Config [DB_POOL] value[" + strValue + "] is not valid";
			return false;
		}
		else
			m_nDbPoolCount = nValue;
		
		strValue.clear();
	}

	// Work pool count, WORK_POOL = 20
	if( GetConfigValue( conf, strProgramName, "WORK_POOL", strValue, strErrorMessage ) == false )
		return false;
	else
	{
		nValue = atoi(strValue.c_str());
		if( nValue < 0 )
		{
			strErrorMessage = "Config [WORK_POOL] value[" + strValue + "] is not valid";
			return false;
		}
		else
			m_nWorkPoolCount = nValue;
		
		strValue.clear();
	}

	// When endtime to obtain coefficient that are subtracted. (sec), SAFETY_FACTOR = 180
	if( GetConfigValue( conf, strProgramName, "SAFETY_FACTOR", strValue, strErrorMessage ) == false )
		return false;
	else
	{
		nValue = atoi(strValue.c_str());
		if( nValue < 0 )
		{
			strErrorMessage = "Config [SAFETY_FACTOR] value[" + strValue + "] is not valid";
			return false;
		}
		else
			m_nSafetyFactor = nValue;
		
		strValue.clear();
	}

	// Get the set amount of time at a time. (Day), ONCE_SIZE = 1
	// 1 이상이어야 한다.
	if( GetConfigValue( conf, strProgramName, "ONCE_SIZE", strValue, strErrorMessage ) == false )
		return false;
	else
	{
		nValue = atoi(strValue.c_str());
		if( nValue <= 0 )
		{
			strErrorMessage = "Config [ONCE_SIZE] value[" + strValue + "] is not valid";
			return false;
		}
		else
			m_nOnceSize = nValue;
		
		strValue.clear();
	}
	
	// CHK_SMALL = 60 , range : 30 ~CHK_MIDDLE (second)
	// Default 값 존재, 최소 30 이상어야 한다.
	if( GetConfigValue( conf, strProgramName, "CHK_SMALL", strValue, strErrorMessage ) == true )		
	{
		nValue = atoi(strValue.c_str());
		if( nValue < 30 )
		{
			strErrorMessage = "Config [CHK_SMALL] value[" + strValue + "] is not valid";
			return false;
		}		
		else
			m_nChkSmall = nValue;
		
		strValue.clear();
	}

	// CHK_MIDDLE = 3600, range : CHK_SMALL ~(second), 
	// Default 값 존재, CHK_SMALL 값보다 커야 한다.
	if( GetConfigValue( conf, strProgramName, "CHK_MIDDLE", strValue, strErrorMessage ) == true )		
	{
		nValue = atoi(strValue.c_str());
		if( nValue < 0 )
		{
			strErrorMessage = "Config [CHK_MIDDLE] value[" + strValue + "] is not valid";
			return false;
		}
		else if( nValue <= (int) m_nChkSmall )
		{
			strErrorMessage = "Config [CHK_MIDDLE] value[" + strValue + "] less then [CHK_SMALL][";
			strErrorMessage += m_nChkSmall;
			strErrorMessage += "]";
			return false;
		}
		else
			m_nChkMiddle = nValue;
		
		strValue.clear();
	}

	// CHK_ONCE = 7, range : 1Days, hour( 0 - 23 )
	// 0 ~ 23 구간내 값이어야 한다.
	if( GetConfigValue( conf, strProgramName, "CHK_ONCE", strValue, strErrorMessage ) == false )
		return false;
	else
	{
		nValue = atoi(strValue.c_str());
		if( nValue < 0 || nValue > 23)
		{
			strErrorMessage = "Config [CHK_ONCE] value[" + strValue + "] is not valid";
			return false;
		}
		else
			m_nChkOnce = nValue;
		
		strValue.clear();
	}


	// DIFF_COMMAND=/user/service/etc/diff_com.sh
	// Default 값 존재
	if( GetConfigValue( conf, strProgramName, "DIFF_COMMAND", strValue, strErrorMessage ) == true )
	{
		m_strDiffCommand = strValue;
		strValue.clear();
	}

	// RC_CMOVE=/user/service/bin/rc_cmove
	// Default 값 존재
	if (GetConfigValue(conf, strProgramName, "RC_CMOVE", strValue, strErrorMessage) == true)
	{
		m_strRcCMove = strValue;
		strValue.clear();
	}

	// FHS_STORAGE_MOUNT = /stg/node0,/stg/node1,/stg/node2
	// Default 값 존재
	if (conf.GetConfig(strProgramName, "FHS_STORAGE_MOUNT", vecValue) == true)
	{
		// 해당 값이 존재할 경우..	
		// Multi value 이므로 갯수 검사
		if (vecValue.size() > 0)
		{
			m_vecFHSMount = vecValue;
		}

		vecValue.clear();
	}
	else
	{
		// program section 에 존재하지 않을 경우... COMMON 세션 확인
		if (conf.GetConfig("COMMON", "FHS_STORAGE_MOUNT", vecValue) == true)
		{
			// 해당 값이 존재할 경우..	
			// Multi value 이므로 갯수 검사
			if (vecValue.size() > 0)
			{
				m_vecFHSMount = vecValue;
			}

			vecValue.clear();
		}
	}


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


bool CServiceConfig::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;
}

