
#include "Config.h"

#include <iostream>
#include <fstream>

#include <errno.h>
#include <stdlib.h>
#include <boost/algorithm/string.hpp>  // use boost 


Config::Config()
	: m_keyDelimiter( CONFIG_DEFAULT_KEY_DELIMITER )
	  , m_valueDelimiter( CONFIG_DEFAULT_VALUE_DELIMITER )
{

}

/// @brief destructor
Config::~Config()
{
	Clear();
}

/// @brief 지정된 Config 파일의 모든 정보를 읽어 내부 변수에 저장처리
/// @param path [in] Config file 의 전체 경로정보( C 배열 지원을 위해 & 사용안함)
/// @return On success return true, otherwise return false
bool Config::Open( const string path )
{
	ifstream file;

	// Config file open
	file.open( path.c_str());
	if( file.is_open() == false )
	{
		cerr << "[error] " << __FILE__<< ":" << __func__ << ": config file open failed.[" << path << "][" << strerror( errno ) << "]" << endl;
		return false;
	}

	// 기존 데이터가 존재하면 삭제 처리.
	if( m_configData.empty() == false )
		m_configData.clear();
	
	// config file parsing
	string line;
	string section;
	vector< string > configs;

	// 루프를 돌면서 Config 파일을 line 단위로 읽어들인당....
	while( std::getline( file, line ) )
	{
		// 앞뒤 공백 제거 처리
		boost::trim( line );

		// 공백 or 주석처리 라인 검사
		if( line.empty() == true || boost::starts_with( line, string("#") ) == true	|| line == "\r" )
		{
			continue;
		}		

		// Section 여부 검사
		if( boost::starts_with( line, string("[") ) == true && boost::ends_with( line, string("]") ) == true )
		{
			// [] 로 정의된 section 의 문자열 값 추출
			line.erase( line.begin() );
			line.erase( line.end() -1 );
			boost::trim( line );

			if( section.empty() == false && section != line )
			{
				// 이전 세션과 다른 신규 세션인 경우
				// 기존까지 저장했던 데이터를 멤버 변수에 입력 처리 후 내부변수 초기화 처리.

				m_configData.insert( make_pair( section, configs ) );
				section.clear();
				configs.clear();
			}

			// 신규 section 정보 저장처리.
			section = line;
			line.clear();		
		}
		else
		{
			// Section 정보가 아닌 경우 => 실제 설정값 .. ^^
			configs.push_back( line );
		}
	}

	// 마지막 세션 처리된 정보가 존재하는 경우 멤버 변수에 저장 처리.
	if( section.empty() == false )
	{
		m_configData.insert( make_pair( section, configs ) );
	}

	// 종료 처리.
	file.close();
	return true;
}

/// @brief Config 멤버 변수에 저장된 데이터 삭제처리.
/// @return void
void Config::Clear()
{
	m_configData.clear();
}

/// @brief 해당 Section, Key 에 해당하는 value 값을 찾아 반환
/// @param section [in] 찾고자 하는 Section 값
/// @param key [in] 찾고자 하는 key 값
/// @param value [out] 해당 Section, Key 에 해당하는 value 값을 저장하기 위한 변수
/// @return On success return true, otherwise return false
bool Config::GetConfig( const string section, const string key, string& value )
{
	// Section 에 대한 임시 데이터 저장객체 생성	
	vector< string > configs;
	
	// 해당 Section 이 존재하지 않는 경우 
	if( Find( section, configs ) == false )
		return false;

	string line;
	string result;
	vector< string >::const_iterator it;

	// 해당 Key 이 존재하는지 검사
	for( it = configs.begin(); it != configs.end(); it++)
	{
		line = *it;

		// Line 상의 주석 제거
		string::size_type pos = line.find( '#' );
		if( pos != string::npos)
		{
			line = line.substr(0, pos);
			boost::trim( line );
		}
		
		// key = value 에서 key 부분 추출
		pos = line.find( m_keyDelimiter );
		if( pos == string::npos )
		{
			continue;
		}
		else
		{
			result = line.substr(0, pos);
			boost::trim( result );
		}

		if( key == result )
		{
			// Key 값이 동일한 경우
			value = line.substr( pos+1 );
			boost::trim( value );
			return true;
		}
	}

	return false;
}

/// @brief 해당 Section, Key 에 해당하는 value Array 값을 찾아 반환
/// @param section [in] 찾고자 하는 Section 값
/// @param key [in] 찾고자 하는 key 값
/// @param value [out] 해당 Section, Key 에 해당하는 value 값을 저장하기 위한 배열
/// @return On success return true, otherwise return false
bool Config::GetConfig( const string section, const string key, vector< string >& value )
{
	string result;
	vector< string > vec;

	if( GetConfig( section, key, result ) == false )
		return false;

	// 전달받은 result 값을 value delimiter 를 이용하여 parsing 처리 
	boost::split( vec, result, boost::is_any_of( m_valueDelimiter ));
	if( vec.empty() == true )
		return false;

	// 루프를 돌면서 trim 처리 후 결과값 저장처리.
	vector< string >::const_iterator it;
	for( it = vec.begin(); it != vec.end(); it++)
	{
		result = boost::trim_copy( *it );

		if( result.empty() == false )
			value.push_back( result );
	}

	if( value.empty() == true )
		return false;
	else
		return true;
}

/// @brief Config 멤버 변수에 저장된 데이터 중 해당 Section 에 해당하는 데이터 반환.
/// @param section [in] section 명
/// @param configData [out] 해당 Section 에서 읽은 Cofig 정보를 저장할 string 배열
/// @return On success return true, otherwise return false
bool Config::Find( const string& section, vector<string>& configData )
{
	map< string, vector< string > >::iterator it = m_configData.find( section );
	if( it != m_configData.end() )
	{
		// 해당 section 을 찾은 경우
		configData = it->second;
		return true;
	}

	// 해당 section 을 찾지 못한 경우
	return false;
}


