/************************************************************************
 IniFile
 created:  Willem de jonge, willem@argcargv.com, 03/2003
 modified: Bob Kuehne, rpk@blue-newt.com, 09/2005
************************************************************************/

#include "inifile.h"
#include <fstream>

const char COMMENT_CHAR = '#';
const char ASSIGN_CHAR  = '=';
const char* KEY_CHARS   = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
const char* WHITE_CHARS = " \t";

std::string stripComments(const std::string& line)
{
 // is this routine supposed to remove comments?
 // or remove lines _containing_ comments?
	std::string r;

	std::string::size_type p = line.find(COMMENT_CHAR);
	if (p!=std::string::npos)
		r.substr( 0, p );
//		r.assign( line.begin(), p );
	else
		r = line;

	return r;
}

std::string trimWhite(const std::string& s)
{
	std::string::size_type l = s.find_first_not_of(WHITE_CHARS);
	std::string::size_type r = s.find_last_not_of(WHITE_CHARS);
	if (l!=std::string::npos)
		return std::string(s.substr(l,r-l+1));
	else
		return std::string();
}

void IniSection::addLine(const std::string& line)
{
	m_lines.push_back(IniLine(extractKey(line), extractVal(line), line));
}

bool IniSection::getValStr(const std::string& key, std::string& val) const
{
	Lines_t::const_iterator i = m_lines.begin();
	while (i!=m_lines.end())
	{
		if ((*i).m_key == key)
		{
			val = (*i).m_val;
			return true;
		}
		i++;
	}
	return false;
}

std::string IniSection::extractKey(const std::string& line)
{
	std::string r;

	r = stripComments(line);

	std::string::size_type p = line.find(ASSIGN_CHAR);
	if (p!=std::string::npos)
		r = r.substr(0,p);
	else
		r.erase();

	r = trimWhite(r);

	std::string::size_type q = line.find_first_not_of(KEY_CHARS);
	if (q==std::string::npos)
		r.erase();

	return r;
}

std::string IniSection::extractVal(const std::string& line)
{
	std::string r;

	r = stripComments(line);

	std::string::size_type p = r.find(ASSIGN_CHAR);
	if (p!=std::string::npos)
		r = r.substr(p+1, r.length()-p-1);
	else
		r.erase();

	r = trimWhite(r);

	return r;
}

bool IniFile::read(const std::string& fileName)
{
	m_sections.clear();

	std::ifstream file(fileName.c_str());

	if (!file)
		return false;

	IniSection s(""); // unnamed section
	m_sections.push_back(s);

	while (!file.eof())
	{
		std::string line;
		std::getline(file, line);

		std::string secName = trimWhite(stripComments(line));
		if (secName.length()>2 && secName[0]=='[' && secName[secName.length()-1]==']')
		{
			// start new section

			secName = secName.substr(1,secName.length()-2);
			IniSection s(secName);
			m_sections.push_back(s);
		}
		else
		{
			m_sections.back().addLine(line);
		}
	}

	return true;
}

void IniFile::get(const std::string& section, const std::string& key, bool& val, bool def)
{
	val = def;
	std::string valStr;
	if (getValStr(section, key, valStr))
	{
		for (unsigned int i=0; i<valStr.length(); i++)
			valStr[i] = tolower(valStr[i]);

		if ((valStr=="yes") || (valStr=="true") || (valStr=="1"))
			val = true;
		else if ((valStr=="no") || (valStr=="false") || (valStr=="0"))
			val = false;
	}
}

void IniFile::get(const std::string& section, const std::string& key, float& val, float def)
{
	val = def;
	std::string valStr;
	if (getValStr(section, key, valStr) && valStr.c_str())
		val = (float)atof(valStr.c_str());
}

void IniFile::get(const std::string& section, const std::string& key, std::string& val, const std::string& def)
{
	val = def;
	std::string valStr;
	if (getValStr(section, key, valStr))
		val = valStr;
}

bool IniFile::getValStr(const std::string& section, const std::string& key, std::string& val)
{
	Sections_t::const_iterator i = m_sections.begin();
	while (i!=m_sections.end())
	{
		const IniSection& s = *i;
		if (s.name()==section)
			return s.getValStr(key, val);

		i++;
	}
	return false;
}
