#include "ProcessConfig.h"

#include <stdlib.h>

#include "Logger.h"


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

// conf 상의 FHS_LISTEN_PORT_FOR_STATUS_MONITORING 정보가 없을 경우 사용할 Default 정보
// - GMS, Neocast 등에서 FHS 상태를 감시하기 위한 TCP port
// - 본 모듈에서 해당 TCP port 를 Listen 하면서 서비스 상태에 따라 TCP 상태를 제어하여
// - FHS 서비스 불가 등의 문제 발생시.. GMS, Neocast 가 이를 인지할 수 있도록 하기 위한 Port 정보임
#define DEFAULT_TCP_LISTEN_PORT_FOR_STATUS_MONITORING	8083


// 생성자
CProcessConfig::CProcessConfig()
{
	// default 값 설정.

	// FHS_LISTEN_PORT_FOR_STATUS_MONITORING
	m_nListenPortForStatusMonitoring = DEFAULT_TCP_LISTEN_PORT_FOR_STATUS_MONITORING;
	
}

// 소멸자 
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();
	}
	
	// RCID
	if( GetConfigValue( conf, strProgramName, "RCID", strValue, strErrorMessage ) == false )
		return false;
	else
	{
		m_strRcId = strValue;
		strValue.clear();
	}

	// FILE_STORAGE_ROOT
	if( GetConfigValue( conf, strProgramName, "FILE_STORAGE_ROOT", strValue, strErrorMessage ) == false )
		return false;
	else
	{
		m_strStorageRoot = strValue;
		strValue.clear();
	}
		
	// RCTS_SERVER
	if( GetConfigValue( conf, strProgramName, "RCTS_SERVER", strValue, strErrorMessage ) == false )
		return false;
	else
	{
		m_strRctsServer = strValue;
		strValue.clear();
	}

	// FHS_PORT_MNGCHECK
	// - fhs fimngd 의 alive check 를 위한 tcp port
	if( GetConfigValue( conf, strProgramName, "FHS_PORT_MNGCHECK", strValue, strErrorMessage ) == false )
		return false;
	else
	{
		m_nFimngdCheckPort = atoi( strValue.c_str() );

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

		strValue.clear();
	}

	// FHS_LISTEN_PORT_FOR_STATUS_MONITORING
	// - GMS, Neocast 등에서 FHS 상태를 감시하기 위한 TCP port
	// - 본 모듈에서 해당 TCP port 를 Listen 하면서 서비스 상태에 따라 TCP 상태를 제어하여
	// - FHS 서비스 불가 등의 문제 발생시.. GMS, Neocast 가 이를 인지할 수 있도록 하기 위한 Port 정보임 
	if( GetConfigValue( conf, strProgramName, "FHS_LISTEN_PORT_FOR_STATUS_MONITORING", strValue, strErrorMessage ) == false )
	{
		// 해당 항목이 존재하지 않을 경우
		// 하위 호환성 및 binary 만 우선 적용 등의 이슈로 인해
		// default 값을 적용 처리한다.
		m_nListenPortForStatusMonitoring = DEFAULT_TCP_LISTEN_PORT_FOR_STATUS_MONITORING;
	}
	else
	{
		// 해당 값이 존재하는 경우
		m_nListenPortForStatusMonitoring = atoi( strValue.c_str() );

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

		strValue.clear();
	}


	// SERVICE_NIC_NAME
	// - 다중 값으로 정의 될 수 있으며..  존재하지 않는 경우.. 오류로 처리
	if( conf.GetConfig( strProgramName, "SERVICE_NIC_NAME", vecValue ) == true )
	{
		// 해당 값이 존재할 경우
		// - GetConfig() 에서 결과값이 empty 인 경우.. false 를 반환하므로...
		// - ture 반환시에는 정상적인 값이 존재한다고 판단해도 무방
		m_vecSvcNic = vecValue;
		vecValue.clear();
	}
	else
	{
		// program section 에 존재하지 않을 경우... COMMON 세션 확인
		if( conf.GetConfig( "COMMON", "SERVICE_NIC_NAME", vecValue ) == true )
		{
			m_vecSvcNic = vecValue;
			vecValue.clear();
		}
		else
		{
			// program, COMMON section 에 해당값이 모두 설정되지 않은 경우....
			// 오류 처리
			strErrorMessage = "Config [SERVICE_NIC_NAME] value is not valid";
			return false;
		}
	}

	// CHECK_DNS_LIST
	// - 다중 값으로 정의 될 수 있으며..  
	// - 설정값이 존재하지 않을 수 있다. 
	if( conf.GetConfig( strProgramName, "CHECK_DNS_LIST", vecValue ) == true )
	{
		// 해당 값이 존재할 경우
		// - GetConfig() 에서 결과값이 empty 인 경우.. false 를 반환하므로...
		// - ture 반환시에는 정상적인 값이 존재한다고 판단해도 무방
		m_vecCheckDns = vecValue;
		vecValue.clear();
	}
	else
	{
		// program section 에 존재하지 않을 경우... COMMON 세션 확인
		if( conf.GetConfig( "COMMON", "CHECK_DNS_LIST", vecValue ) == true )
		{
			m_vecCheckDns = vecValue;
			vecValue.clear();
		}
		else
		{
			// program, COMMON section 에 해당값이 모두 설정되지 않은 경우....
			// - 설정값이 존재하지 않을 수 있으므로.. 그냥 통과.
			m_vecCheckDns.clear();
			vecValue.clear();
		}
	}
	
	// RCTS_PORT_INFO_SYSTEM
	// - rcts rc_mond 의 system info 수신을 위한 TCP listen port 정보
	if( GetConfigValue( conf, strProgramName, "RCTS_PORT_INFO_SYSTEM", strValue, strErrorMessage ) == false )
		return false;
	else
	{
		m_nRctsSystemInfoPort = atoi( strValue.c_str() );

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

		strValue.clear();
	}

	// RCTS_PORT_INFO_SERVICE
	// - rcts rc_mond 의 service check info 수신을 위한 TCP listen port 정보
	if( GetConfigValue( conf, strProgramName, "RCTS_PORT_INFO_SERVICE", strValue, strErrorMessage ) == false )
		return false;
	else
	{
		m_nRctsServiceInfoPort = atoi( strValue.c_str() );

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

		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;
}

