#if defined ini_included
	#endinput
#endif
#define ini_included
/*
 *	@version 1.0.1
 *	@author NeoPhoenix <support@neophoenix.net>
 *	@copyright 2012 <http://www.neophoenix.net>
 */

#include <a_samp>
#define IGNORE_CASE			(true)
#define RECOGNIZE_BOOL_STR	(true)
#define MAX_PATH_LEN		(64)
#define MAX_SECTION_LEN		(24)
#define MAX_KEY_LEN			(32)
#define MAX_VALUE_LEN		(128)
#define COMMENT_CHARACTER	';'
#define INI::				INI_
new tmp_path[MAX_PATH_LEN],
	tmp_section[MAX_SECTION_LEN],
	global_file[MAX_PATH_LEN],
	global_section[MAX_SECTION_LEN];

/*
	native bool:INI::Create(const path[],bool:overwrite=false);
	native bool:INI::Remove(const path[]="");
	native bool:INI::Copy(const path[]="", const path[],bool:delete_oldpath=true,bool:overwrite=false);
	native bool:INI::RemoveKey(const path[]="",const key[],const section[]="");
	native INI::GetValue(const path[]="",const key[],const section[]="");
	native INI::GetValueAsInt(const path[]="",const key[],const section[]="");
	native Float:INI::GetValueAsFloat(const path[]="",const key[],const section[]="");
	native bool:INI::GetValueAsBool(const path[]="",const key[],const section[]="");
	native INI::SetValue(const path[]="",const key[],const value[],const section[]="");
	native INI::SetInt(const path[]="",const key[],value,const section[]="");
	native INI::SetFloat(const path[]="",const key[],Float:value,const section[]="");
	native INI::SetBool(const path[]="",const key[],bool:value,const section[]="");
*/

enum ini_global (+=1)
{
	INI::FILE = 0,
	INI::SECTION,
}
stock INI::Global(ini_global:key,const value[])
{
	switch(key)
	{
		case INI::FILE:		strmid(global_file,value,0,sizeof(global_file));
		case INI::SECTION:	strmid(global_section,value,0,sizeof(global_section));
	}
	return ;
}

stock INI::Reset(ini_global:key)
{
	switch(key)
	{
		case INI::FILE:		global_file[0] = 0;
		case INI::SECTION:	global_section[0] = 0;
	}
	return ;
}

stock INI::Append(const path[]="",const value[])
{
	if(path[0]) strmid(tmp_path,path,0,sizeof(tmp_path));
	else		strmid(tmp_path,global_file,0,sizeof(tmp_path));
	if(!tmp_path[0]) return ;
	if(!INI::Exists(tmp_path)) return ;
	new File:_file = fopen(tmp_path,io_append);
	if(_file) fwrite(_file,value);
	fclose(_file);
	return ;
}

forward bool:INI::Exists(const path[]);
stock bool:INI::Exists(const path[])
{
	if(!path[0]) return false;
	if(fexist(path)) return true;
	return false;
}

stock INI::Create(const path[],bool:overwrite=false)
{
	if(!path[0]) return false;
	if(!overwrite && INI::Exists(path)) return false;
	new File:_file = fopen(path,io_write);
	if(_file)
	{
		fclose(_file);
		return true;
	}
	return false;
}

stock bool:INI::Remove(const path[]="")
{
	if(path[0]) strmid(tmp_path,path,0,sizeof(tmp_path));
	else		strmid(tmp_path,global_file,0,sizeof(tmp_path));
	if(!tmp_path[0]) return false;
	if(!INI::Exists(tmp_path)) return false;
	fremove(tmp_path);
	return INI::Exists(tmp_path)==true?false:true;
}

stock bool:INI::Copy(const oldpath[]="",const newpath[],bool:delete_oldpath=true,bool:overwrite=false)
{
	if(oldpath[0])	strmid(tmp_path,oldpath,0,sizeof(tmp_path));
	else			strmid(tmp_path,global_file,0,sizeof(tmp_path));
	if(!tmp_path[0]) return false;
	if(!INI::Exists(tmp_path) || INI::Exists(newpath) && !overwrite || !newpath[0]) return false;
	new File:_oldfile = fopen(tmp_path,io_read),
		File:_newfile = fopen(newpath,io_write),
		tmp[MAX_KEY_LEN+1+MAX_VALUE_LEN+3];
	while(fread(_oldfile,tmp,sizeof(tmp),false)) fwrite(_newfile,tmp);
	fclose(_oldfile);
	fclose(_newfile);
	if(delete_oldpath) INI::Remove(tmp_path);
	return true;
}

stock INI::RemoveKey(const path[]="",const key[],const section[]="")
{
	if(path[0]) strmid(tmp_path,path,0,sizeof(tmp_path));
	else		strmid(tmp_path,global_file,0,sizeof(tmp_path));
	if(!tmp_path[0]) return false;
	if(!key[0]) return false;
	if(!INI::Exists(tmp_path)) return false;
	if(section[0])	strmid(tmp_section,section,0,sizeof(tmp_section));
	else			strmid(tmp_section,global_section,0,sizeof(tmp_section));
	new tmp[MAX_KEY_LEN+1+MAX_VALUE_LEN+3];
	format(tmp,sizeof(tmp),"%s.tmp",tmp_path);
	if(!INI::Copy(tmp_path,tmp)) return false;
	INI::Create(tmp_path);
	format(tmp,sizeof(tmp),"%s.tmp",tmp_path);
	new File:_tmpfile = fopen(tmp,io_read),
		File:_newfile = fopen(tmp_path,io_append),
		line[MAX_KEY_LEN+1+MAX_VALUE_LEN+3],
		line_len,
		bool:section_required = !tmp_section[0]?false:true,
		bool:in_section = false;
	if(!_tmpfile || !_newfile) return false;
	format(tmp,sizeof(tmp),"%s=",key);
	while(fread(_tmpfile,line,sizeof(line),false))
	{
		line_len = strlen(line);
		if(!line[0] || line[0] == '\n' || line[0] == '\r' || line[0] == COMMENT_CHARACTER)
		{
			fwrite(_newfile,line);
			continue;
		}
		if(section_required && line[0] == '[' && line[line_len-3] == ']')
		{
			if(in_section)
			{
				fwrite(_newfile,line);
				in_section = false;
				continue;
			}
			strmid(tmp,line,1,line_len-3);
			if(!strcmp(tmp,tmp_section,IGNORE_CASE))
			{
				in_section = true;
				fwrite(_newfile,line);
				continue;
			}
		}
		if((!section_required || in_section) && strfind(line,key,IGNORE_CASE) == 0)
		{//Remove key
			continue;
		}
		fwrite(_newfile,line);
		continue;
	}
	fclose(_tmpfile);
	fclose(_newfile);
	format(tmp,sizeof(tmp),"%s.tmp",tmp_path);
	INI::Remove(tmp);
	return true;
}

stock INI::GetValue(const path[]="",const key[],const section[]="")
{
	new value[MAX_VALUE_LEN];
	if(path[0]) strmid(tmp_path,path,0,sizeof(tmp_path));
	else		strmid(tmp_path,global_file,0,sizeof(tmp_path));
	if(!tmp_path[0]) return value;
	if(!key[0]) return value;
	if(!INI::Exists(tmp_path)) return value;
	if(section[0])	strmid(tmp_section,section,0,sizeof(tmp_section));
	else			strmid(tmp_section,global_section,0,sizeof(tmp_section));
	new File:_file = fopen(tmp_path,io_read),
		line[MAX_KEY_LEN+1+MAX_VALUE_LEN+3],
		line_len,
		bool:section_required = !tmp_section[0]?false:true,
		bool:in_section = false,
		tmp[MAX_KEY_LEN+1+MAX_VALUE_LEN+3];
	format(tmp,sizeof(tmp),"%s=",key);
	while(fread(_file,line,sizeof(line),false))
	{
		line_len = strlen(line);
		if(!line[0] || line[0] == '\n' || line[0] == '\r' || line[0] == COMMENT_CHARACTER) continue;
		if(section_required && line[0] == '[' && line[line_len-3] == ']')
		{
			if(in_section) return value;
			strmid(line,line,1,line_len-3);
			if(!strcmp(line,tmp_section,IGNORE_CASE))
			{
				in_section = true;
				continue;
			}
		}
		if(section_required && !in_section) continue;
		if(strfind(line,tmp,IGNORE_CASE) != 0) continue;
		strmid(value,line,strlen(tmp),line_len-2);
		return value;
	}
	return value;
}

stock INI::GetValueAsInt(const path[]="",const key[],const section[]="")
{
	new tmp[MAX_VALUE_LEN];
	format(tmp,sizeof(tmp),"%s",INI::GetValue(path,key,section));
	if(RECOGNIZE_BOOL_STR && tmp[0])
	{
		if(!strcmp(tmp,"Yes",true) || !strcmp(tmp,"True",true)) return 1;
		if(!strcmp(tmp,"No",true) || !strcmp(tmp,"False",true)) return 0;
	}
	return strval(tmp);
}

forward Float:INI::GetValueAsFloat(const path[]="",const key[],const section[]="");
stock Float:INI::GetValueAsFloat(const path[]="",const key[],const section[]="")
{
	new tmp[MAX_VALUE_LEN];
	format(tmp,sizeof(tmp),"%s",INI::GetValue(path,key,section));
	return floatstr(tmp);
}

stock bool:INI::GetValueAsBool(const path[]="",const key[],const section[]="")
{
	new tmp[MAX_VALUE_LEN];
	format(tmp,sizeof(tmp),"%s",INI::GetValue(path,key,section));
	if(RECOGNIZE_BOOL_STR && tmp[0])
	{
		if(!strcmp(tmp,"Yes",true) || !strcmp(tmp,"True",true)) return true;
		if(!strcmp(tmp,"No",true) || !strcmp(tmp,"False",true)) return false;
	}
	return strval(tmp)?true:false;
}

stock INI::SetValue(const path[]="",const key[],const value[],const section[]="")
{
	if(path[0]) strmid(tmp_path,path,0,sizeof(tmp_path));
	else		strmid(tmp_path,global_file,0,sizeof(tmp_path));
	if(!tmp_path[0]) return ;
	if(section[0])	strmid(tmp_section,section,0,sizeof(tmp_section));
	else			strmid(tmp_section,global_section,0,sizeof(tmp_section));
	if(!key[0] || !INI::Exists(tmp_path)) return ;
	new tmp[MAX_KEY_LEN+1+MAX_VALUE_LEN+3];
	format(tmp,sizeof(tmp),"%s.tmp",tmp_path);
	if(INI::Copy(tmp_path,tmp))
	{
		format(tmp,sizeof(tmp),"%s.tmp",tmp_path);
		new File:_oldfile = fopen(tmp,io_read),
			File:_newfile = fopen(tmp_path,io_write),
			line[MAX_KEY_LEN+1+MAX_VALUE_LEN+3],
			line_len,
			bool:section_required = tmp_section[0]?true:false,
			bool:in_section,
			bool:wrote_value = false;
		while(fread(_oldfile,line,sizeof(line),false))
		{
			line_len = strlen(line);
			if(!line[0] || line[0] == '\n' || line[0] == '\r' || line[0] == COMMENT_CHARACTER)
			{
				fwrite(_newfile,line);
				continue;
			}
			else if(line[0] == '[' && line[line_len-3] == ']')
			{
				if(section_required)
				{
					if(in_section)
					{
						if(!wrote_value)
						{
							format(tmp,sizeof(tmp),"%s=%s\r\n",key,value);
							fwrite(_newfile,tmp);
							wrote_value = true;
						}
						fwrite(_newfile,line);
						in_section = false;
						continue;
					}
					fwrite(_newfile,line);
					strmid(line,line,1,line_len-3);
					if(!strcmp(line,tmp_section,IGNORE_CASE)) in_section = true;
					continue;
				}
			}
			
			format(tmp,sizeof(tmp),"%s=",key);
			if((!section_required || in_section) && strfind(line,tmp,IGNORE_CASE) == 0)
			{
				format(tmp,sizeof(tmp),"%s=%s\r\n",key,value);
				fwrite(_newfile,tmp);
				wrote_value = true;
				continue;
			}
			else
			{
				fwrite(_newfile,line);
				continue;
			}
		}
		if(!wrote_value)
		{
			if(section_required && !in_section)
			{
				format(tmp,sizeof(tmp),"[%s]\r\n",tmp_section);
				fwrite(_newfile,tmp);
			}
			format(tmp,sizeof(tmp),"%s=%s\r\n",key,value);
			fwrite(_newfile,tmp);
		}
		fclose(_oldfile);
		fclose(_newfile);
		format(tmp,sizeof(tmp),"%s.tmp",tmp_path);
		INI::Remove(tmp);
		return ;
	}
	return ;
}

stock INI::SetInt(const path[]="",const key[],value,const section[]="")
{
	new tmp[MAX_VALUE_LEN];
	format(tmp,sizeof(tmp),"%d",value);
	INI::SetValue(path,key,tmp,section);
	return ;
}

stock INI::SetFloat(const path[]="",const key[],Float:value,const section[]="")
{
	new tmp[MAX_VALUE_LEN];
	format(tmp,sizeof(tmp),"%f",value);
	INI::SetValue(path,key,tmp,section);
	return ;
}

stock INI::SetBool(const path[]="",const key[],bool:value,const section[]="")
{
	new tmp[MAX_VALUE_LEN];
	if(value) INI::SetValue(path,key,"True",section);
	else INI::SetValue(path,key,"False",section);
	return ;
}

#assert MAX_SECTION_LEN < (MAX_KEY_LEN+MAX_VALUE_LEN+2)