/*
 * Copyright (c) 2007 Vittorio De Tomasi <vittorio at detomasi dot it>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#ifndef INIMANAGER
#define INIMANAGER

#include <vector>
#include <fstream>
#include <string>
#include <algorithm>
#include <functional>
#include <stdio.h>

typedef struct{	std::string name, value, section;} entry; 

class IniManager
{
public:
	// constructor, arguments are the .ini file to read and the value separator (by default '=')
   // comments have a '#' or a ';' at the first column
	IniManager(const std::string, const char c = '=');
   // methods for accessing a parameter in a section (default section is ""). The default value is in defval.
   // method returns true if the parameter specified by name was defined in the .ini file, false otherwise.
   // if the parameter does not exist, it is added in the appropriate section
	bool Get(const std::string name, std::string& value, const std::string defval = "", const std::string section = "");
	bool Get(const std::string name, double& value, const double defval = 0.0, const std::string section = "");
	bool Get(const std::string name, float& value, const double defval = 0.0, const std::string section = "");
	bool Get(const std::string name, long int& value, const int defval = 0, const std::string section = "");
	bool Get(const std::string name, int& value, const int defval = 0, const std::string section = "");
	bool Get(const std::string name, unsigned int& value, const int defval = 0, const std::string section = "");
	// method for writing back the section/parameters/values set to a file
	void Write(const std::string);
private:
	std::string	myfile;
	char	s;
	std::vector<entry>	v;
};





inline bool INI_eq_entry(entry e1, entry e2) {return (e1.name != "" && e1.section == e2.section && e1.name == e2.name);}
inline bool INI_eq_section(entry e1, entry e2) {return (e1.section == e2.section);}

IniManager::IniManager(const std::string fname, const char c)
{
	std::string buf, section = "", name, value, ws = " \t\r";
	std::string::size_type n1, n2;
	std::ifstream is;
	std::vector<entry>::iterator itr;
	entry	ey;
	
	myfile = fname;
	s = c;
	try{
		is.open(fname.c_str());
		while(is.good()) {
			getline(is, buf);
			n1 = buf.find_first_not_of(ws);				         // remove whitespace chars
			n2 = buf.find_last_not_of(ws);
			if(n1 == std::string::npos)
				continue;
			buf = buf.substr(n1, n2 - n1 + 1);
			if (buf[0] == '[' && buf[buf.size() - 1] == ']') {	// new section ?!?
				section = buf.substr(1, buf.size() - 2);
				continue;
			}
			ey.section = section;
			if (buf[0] == '#' || buf[0] == ';') {				   // comment ?
				ey.name = "";
				ey.value = buf;
			}
			else {
				n1 = buf.find_first_of(s);
				if(n1 == std::string::npos)		               // name/value pair ?!?
					continue;
				name = buf.substr(0, n1);
				value = buf.substr(n1 + 1, buf.size() - n1);
				ey.name = name;
				ey.value = value;
			}
			if((itr = find_if(v.begin(), v.end(), std::bind2nd(std::ptr_fun(INI_eq_entry), ey))) == v.end())
				v.push_back(ey);
			else
				itr->value = value;
		}
	}
	catch(std::ifstream::failure e){
		if(is.eof())
			is.close();
		else
			throw(e);
	}
}



bool IniManager::Get(std::string name, std::string& value, std::string defval, std::string section)
{
	bool retval;
	entry ey;
	ey.name = name;
	ey.section = section;
	std::vector<entry>::iterator itr;
	
	if((itr = find_if(v.begin(), v.end(), std::bind2nd(std::ptr_fun(INI_eq_entry), ey))) == v.end()) {
		value = defval;
		ey.value = value;
		if((itr = find_if(v.begin(), v.end(), std::bind2nd(std::ptr_fun(INI_eq_section), ey))) == v.end())
			v.push_back(ey);
		else
			v.insert(itr, ey);
		retval = false;
	}
	else {
		value = itr->value;
		retval = true;
	}
	return retval;
}


bool IniManager::Get(std::string name, long int& value, int defval, std::string section) {
	bool retval;
	entry ey;
	char s[20];
	
	ey.name = name;
	ey.section = section;
	std::vector<entry>::iterator itr;
	
	if((itr = find_if(v.begin(), v.end(), std::bind2nd(std::ptr_fun(INI_eq_entry), ey))) == v.end()) {
		sprintf(s, "%ld", defval);
		ey.value = s;
		value = defval;
		if((itr = find_if(v.begin(), v.end(), std::bind2nd(std::ptr_fun(INI_eq_section), ey))) == v.end())
			v.push_back(ey);
		else
			v.insert(itr, ey);
		retval = false;
	}
	else {
		value = atoi(itr->value.c_str());
		retval = true;
	}
	return retval;
}


bool IniManager::Get(std::string name, int& value, int defval, std::string section) {
	bool retval;
	entry ey;
	char s[20];
	
	ey.name = name;
	ey.section = section;
	std::vector<entry>::iterator itr;
	
	if((itr = find_if(v.begin(), v.end(), std::bind2nd(std::ptr_fun(INI_eq_entry), ey))) == v.end()) {
		sprintf(s, "%d", (int)defval);
		ey.value = s;
		value = defval;
		if((itr = find_if(v.begin(), v.end(), std::bind2nd(std::ptr_fun(INI_eq_section), ey))) == v.end())
			v.push_back(ey);
		else
			v.insert(itr, ey);
		retval = false;
	}
	else {
		value = atoi(itr->value.c_str());
		retval = true;
	}
	return retval;
}


bool IniManager::Get(std::string name, unsigned int& value, int defval, std::string section) {
	bool retval;
	entry ey;
	char s[20];
	
	ey.name = name;
	ey.section = section;
	std::vector<entry>::iterator itr;
	
	if((itr = find_if(v.begin(), v.end(), std::bind2nd(std::ptr_fun(INI_eq_entry), ey))) == v.end()) {
		sprintf(s, "%u", (unsigned int)defval);
		ey.value = s;
		value = defval;
		if((itr = find_if(v.begin(), v.end(), std::bind2nd(std::ptr_fun(INI_eq_section), ey))) == v.end())
			v.push_back(ey);
		else
			v.insert(itr, ey);
		retval = false;
	}
	else {
		value = atoi(itr->value.c_str());
		retval = true;
	}
	return retval;
}


bool IniManager::Get(const std::string name, double& value, const double defval, const std::string section)
{
	bool retval;
	entry ey;
	char s[20];
	
	ey.name = name;
	ey.section = section;
	std::vector<entry>::iterator itr;
	
	if((itr = find_if(v.begin(), v.end(), std::bind2nd(std::ptr_fun(INI_eq_entry), ey))) == v.end()) {
		sprintf(s, "%lf", defval);
		ey.value = s;
		value = defval;
		if((itr = find_if(v.begin(), v.end(), std::bind2nd(std::ptr_fun(INI_eq_section), ey))) == v.end())
			v.push_back(ey);
		else
			v.insert(itr, ey);
		retval = false;
	}
	else {
		value = atof(itr->value.c_str());
		retval = true;
	}
	return retval;
}


bool IniManager::Get(const std::string name, float& value, const double defval, const std::string section)
{
	bool retval;
	entry ey;
	char s[20];
	
	ey.name = name;
	ey.section = section;
	std::vector<entry>::iterator itr;
	
	if((itr = find_if(v.begin(), v.end(), std::bind2nd(std::ptr_fun(INI_eq_entry), ey))) == v.end()) {
		sprintf(s, "%lf", defval);
		ey.value = s;
		value = defval;
		if((itr = find_if(v.begin(), v.end(), std::bind2nd(std::ptr_fun(INI_eq_section), ey))) == v.end())
			v.push_back(ey);
		else
			v.insert(itr, ey);
		retval = false;
	}
	else {
		value = atof(itr->value.c_str());
		retval = true;
	}
	return retval;
}

void IniManager::Write(std::string fname)
{
	std::ofstream os(fname.c_str());
	std::string st = v[0].section;
	
	for(unsigned int i = 0; i < v. size(); i++) {
		if (v[i].section != st){
			st = v[i].section;
			os << '[' << st << ']' << std::endl;
		}
		if (v[i].name.length() == 0)
			os << v[i].value << std::endl;
		else
			os << v[i].name << s << v[i].value << std::endl; 
	}
	
	os.close();
}



#endif



