//
//  GM-Ini.cpp
//  Wumbo
//
//  Created by Wade McGillis on 11/22/13.
//  Copyright (c) 2013 Wade McGillis. All rights reserved.
//

#include "GM-Ini.h"
#include "Wumbo-Functions.h"
namespace GM {

	bool wrotestuff;

	std::string __INI_PATH;

	struct __GM_VARIABLE_TYPE{
		std::string stringValue;
		double realValue;
		bool isReal;
		__GM_VARIABLE_TYPE(double val)
		{
			isReal = true;
			realValue = val;
			stringValue = "";
		}
		__GM_VARIABLE_TYPE(std::string val)
		{
			isReal = false;
			realValue = 0;
			stringValue = val;
		}
		__GM_VARIABLE_TYPE()
		{
			isReal = true;
			realValue = 0;
			stringValue = "";
		}
	};

	std::map<std::string,std::map<std::string,__GM_VARIABLE_TYPE>*> __GM_INI_MAP;
	
	std::string readline(FILE *f)
	{
		std::string s = "";
		while(!feof(f))
		{
			char c = fgetc(f);
			if (c == 0x0D)
			{
				if (fgetc(f) == 0x0A)
				{
					//printf("ENCOUNTER \\r\\n! STRING IS %s\n",s.c_str());
					return s;
				}
				else
				{
					//printf("ENCOUNTER \\r! STRING IS %s\n",s.c_str());
					fseek(f,ftell(f)-1,SEEK_SET);
					return s;
				}
			}
			else
			{
				s += c;
			}
		}
		return s;
	}
	
	bool ini_open(std::string name)
	{
		__INI_PATH = name;
		if (__INI_PATH.substr(__INI_PATH.length()-7) == "o_o.ini")
			printf("TAPE DECK!\n");
		FILE *f = Wumbo::fopen_safe4ios(__INI_PATH.c_str(),"rb");
		if (f == NULL)
		{
			printf("ini_close() : Unable to open .ini at location %s.\n",__INI_PATH.c_str());
			return false;
		}
		std::string s;
		std::string currentsection = "";
		__GM_INI_MAP.insert(std::pair<std::string,std::map<std::string,__GM_VARIABLE_TYPE>*>(currentsection,new std::map<std::string,__GM_VARIABLE_TYPE>()));
		wrotestuff = false;
		printf("~~~~INI READING: %s~~~~\n",name.c_str());
		while(!feof(f))
		{
			s = readline(f);
			if (s == "")
				continue;
			if (s.at(0) == '[')
				if (s.at(s.length()-1) == ']')
				{
					currentsection = s.substr(1,s.length()-2);
					//printf("[%s]\n",currentsection.c_str());
					__GM_INI_MAP.insert(std::pair<std::string,std::map<std::string,__GM_VARIABLE_TYPE>*>(currentsection,new std::map<std::string,__GM_VARIABLE_TYPE>()));
					continue;
				}
			int equalsPos = s.find("=",0);
			if (equalsPos == s.npos) // just a line of text
				continue;
			std::string s1 = s.substr(0,equalsPos);
			if (s.at(equalsPos-1) == ' ')
				s1 = s.substr(0,equalsPos-1);
			//printf("\t%s = ",s1.c_str());
			std::string s2 = s.substr(equalsPos+1,s.length()-equalsPos-1);
			if (s2.at(0) == ' ')
				s2 = s.substr(equalsPos+2,s.length()-equalsPos-1);
			//printf("%s",s2.c_str());
			if (__GM_INI_MAP.find(currentsection) != __GM_INI_MAP.end())
			{
				__GM_VARIABLE_TYPE t;
				t.isReal = true;
				for(int i=0;i<s2.length();i++)
				{
					char c = s2.at(i);
					if ((c < '0' || c > '9') && c != '.')
					{
						//printf("[ERROR PARSE NUMBER: %c]",c);
						t.isReal = false;
						t.stringValue = s2;
						i = s2.length();
					}
				}
				if (t.isReal)
					t.realValue = atof(s2.c_str());
				else
					printf(" INI VARIABLE %s = %s\n",s1.c_str(),t.stringValue.c_str());
				/*if (t.isReal)
					printf(" <<< NUMBER\n");//,currentsection.c_str(),s1.c_str(),t.realValue);
				else
					printf(" <<< STRING\n");//,currentsection.c_str(),s1.c_str(),t.stringValue.c_str());*/
				__GM_INI_MAP.find(currentsection)->second->insert(std::pair<std::string,__GM_VARIABLE_TYPE>(s1,t));
			}
		}
		fclose(f);
		return true;
	}
	void ini_close()
	{
		if (wrotestuff)
		{
#pragma mark need to check to see if there is an ini file open in the first place
			FILE *f = Wumbo::fopen_safe4ios(__INI_PATH.c_str(),"wb");
			if (f == NULL)
			{
				printf("ini_close() : Unable to open .ini at location %s.\n",__INI_PATH.c_str());
				return;
			}
			printf("~~~~INI WRITING: %s~~~~\n",__INI_PATH.c_str());
			for(std::map<std::string,std::map<std::string,__GM_VARIABLE_TYPE>*>::iterator it=__GM_INI_MAP.begin();it!=__GM_INI_MAP.end();it++)
			{
				//printf("[%s]\n",it->first.c_str());
				fprintf(f,"[%s]\r\n",it->first.c_str());
				for(std::map<std::string,__GM_VARIABLE_TYPE>::iterator it2=it->second->begin();it2!=it->second->end();it2++)
				{
					if (it2->second.isReal)
					{
						if (it2->second.realValue-(float)((int)it2->second.realValue) == 0)
						{
							//printf("\t%s = %i <<< INTEGER\n",it2->first.c_str(),(int)it2->second.realValue);
							fprintf(f,"%s = %i\r\n",it2->first.c_str(),(int)it2->second.realValue);
						}
						else
						{
							//printf("\t%s = %f <<< FLOAT\n",it2->first.c_str(),it2->second.realValue);
							fprintf(f,"%s = %f\r\n",it2->first.c_str(),it2->second.realValue);
						}
					}
					else
					{
						printf(" INI VARIABLE %s = %s\n",it2->first.c_str(),it2->second.stringValue.c_str());
						//printf("%s = %s <<< STRING\n",it2->first.c_str(),it2->second.stringValue.c_str());
						fprintf(f,"%s = %s\r\n",it2->first.c_str(),it2->second.stringValue.c_str());
					}
				}
			}
			fclose(f);
		}
		for(std::map<std::string,std::map<std::string,__GM_VARIABLE_TYPE>*>::iterator it=__GM_INI_MAP.begin();it!=__GM_INI_MAP.end();it++)
		{
			it->second->clear();
			delete it->second;
		}
		__GM_INI_MAP.clear();
	}
	
	std::string ini_read_string(std::string section, std::string key, std::string defult)
	{
		if (ini_section_exists(section))
		{
			if (ini_key_exists(section,key))
			{
				if (__GM_INI_MAP.find(section)->second->find(key)->second.isReal)
					return defult;
				else
					return __GM_INI_MAP.find(section)->second->find(key)->second.stringValue;
			}
			else
				return defult;
		}
		else
			return defult;
	}
	double ini_read_real(std::string section, std::string key, double defult)
	{
		if (ini_section_exists(section))
		{
			if (ini_key_exists(section,key))
			{
				if (__GM_INI_MAP.find(section)->second->find(key)->second.isReal)
					return __GM_INI_MAP.find(section)->second->find(key)->second.realValue;
				else
					return defult;
			}
			else
				return defult;
		}
		else
			return defult;
	}
	
	void ini_write_string(std::string section, std::string key, std::string value)
	{
		if (__INI_PATH.substr(__INI_PATH.length()-7) == "o_o.ini")
			printf("INI WRITE STRING %s\n",value.c_str());
		if (!ini_section_exists(section))
			__GM_INI_MAP.insert(std::pair<std::string,std::map<std::string,__GM_VARIABLE_TYPE>*>(section,new std::map<std::string,__GM_VARIABLE_TYPE>()));
		if (ini_key_exists(section,key))
			__GM_INI_MAP.find(section)->second->find(key)->second = __GM_VARIABLE_TYPE(value);
		else
			__GM_INI_MAP.find(section)->second->insert(std::pair<std::string,__GM_VARIABLE_TYPE>(key,__GM_VARIABLE_TYPE(value)));
		wrotestuff = true;
	}
	void ini_write_real(std::string section, std::string key, double value)
	{
		if (!ini_section_exists(section))
			__GM_INI_MAP.insert(std::pair<std::string,std::map<std::string,__GM_VARIABLE_TYPE>*>(section,new std::map<std::string,__GM_VARIABLE_TYPE>()));
		if (ini_key_exists(section,key))
			__GM_INI_MAP.find(section)->second->find(key)->second = __GM_VARIABLE_TYPE(value);
		else
			__GM_INI_MAP.find(section)->second->insert(std::pair<std::string,__GM_VARIABLE_TYPE>(key,__GM_VARIABLE_TYPE(value)));
		wrotestuff = true;
	}
	
	bool ini_key_exists(std::string section, std::string key)
	{
		if (ini_section_exists(section))
			return __GM_INI_MAP.find(section)->second->find(key) != __GM_INI_MAP.find(section)->second->end();
		return false;
	}
	bool ini_section_exists(std::string section)
	{
		return __GM_INI_MAP.find(section) != __GM_INI_MAP.end();
	}
	
	bool ini_key_delete(std::string section, std::string key)
	{
		if (ini_key_exists(section,key))
			__GM_INI_MAP.find(section)->second->erase(key);
		return false;
	}
	bool ini_section_delete(std::string section)
	{
		if (ini_section_exists(section))
		{
			delete __GM_INI_MAP.find(section)->second;
			__GM_INI_MAP.find(section)->second->erase(__GM_INI_MAP.find(section)->first);
		}
		return false;
	}
};