/* FUNCTION list 	---	---	---	---	---	---	---

*/

//~ RaDIaT1oN (2002-04-29):
//~ lowercase file search routines for linux

/* BASE headers	---	---	---	---	---	---	--- */
//#include "agi.h"
#include "../agi.h"

/* LIBRARY headers	---	---	---	---	---	---	--- */
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

/* OTHER headers	---	---	---	---	---	---	--- */
//#include "view/crap.h"
#include "agi_file.h"
#include "mem_wrap.h"
#include "ini_config.h"

/* PROTOTYPES	---	---	---	---	---	---	--- */
//void test_function(void);



/* VARIABLES	---	---	---	---	---	---	--- */


/* CODE	---	---	---	---	---	---	---	--- */


INI *ini_open(const char *ini_name);
void ini_close(INI *inistate);

INI *ini_open(const char *ini_name)
{
	FILE *file_stream;
	int inifile_size;
	INI *ini_new;
	
	// open file
//	if ( (file_stream=fopen(ini_name, "rb")) == 0)
	if ( (file_stream=fopen_nocase(ini_name)) == 0)
		return 0;
	
	// malloc an INI
	ini_new = (INI *)a_malloc(sizeof(INI));
	
	// get size
	fseek(file_stream, 0, SEEK_END);
	inifile_size = ftell(file_stream);
	//fgetpos(file_stream, &inifile_size);
	fseek(file_stream, 0, SEEK_SET);
	
	ini_new->size = (int)inifile_size;
	
	// read file
	ini_new->data = a_malloc(ini_new->size + 1);
	if ( fread(ini_new->data, sizeof(char), ini_new->size, file_stream) !=(size_t)ini_new->size)
	{
		ini_close(ini_new);
		return 0;
	}
	
	// add a newline char at the end of the file so it can properly read the last key
	ini_new->size ++;
	ini_new->data[ini_new->size - 1] = '\n';
	
	// zero other pointers
	ini_new->last = ini_new->data + ini_new->size - 1;
	ini_new->cur_ptr = ini_new->data;
	ini_new->sect_ptr = 0;
	ini_new->isol_ptr = 0;

	// close ini file
	fclose(file_stream);
	
	//return ini
	return ini_new;
}

void ini_close(INI *ini)
{
	if (ini != 0)
	{
		if (ini->data != 0)
			a_free(ini->data);
		a_free(ini);
	}
}

// return 0 if ok
// return 1 if reach end of file or line
// starts from ini->cur_ptr
// don't trash ini_cur ptr if stuffed.. because we can still continue
static int string_isolate(INI *ini, char ch)
{
	char *cur;
	
	cur = ini->cur_ptr;

	// check for eof, eoln
	while ( (cur <= ini->last) && (*cur!='\n') && (*cur!='\r') )
	{
		if (*cur == ch)
		{
			ini->isol_ptr = cur;
			ini->isol_ch = *cur;
			*cur = 0;	// null at the end of string
			return 0;  // alright!!  you better run string_rejoin later on matey
		}
		cur++;
	}
	
	return -1; // i failed.. nothing has been touched
}

// this means if you have a key on the last line.. it better have a eoln at the end
static int line_isolate(INI *ini)
{
	char *cur;
	
	if (ini != 0)
	{
		cur = ini->cur_ptr;
	
		// check for eof
		while (cur <= ini->last) 
		{
			if ((*cur=='\n')||(*cur=='\r')) 	// eoln
			{
				ini->isol_ptr = cur;
				ini->isol_ch = *cur;
				*cur = 0;	// null at the end of string
				return 0;  // alright!!  you better run string_rejoin later on matey
			}
			cur++;
		}
	}
	
	return -1; // i failed.. nothing has been touched
}

static void string_rejoin(INI *ini)
{
	if ((ini!=0) && (ini->isol_ptr != 0) )
	{
		*(ini->isol_ptr) = ini->isol_ch;
		ini->isol_ptr = 0;
	}
}

// we can trash this (cur_ptr) if we want.. we need ini->cur and we save ini->cur
// if we can't get to the next line.. we don't need the data.. we just finish
static void line_next(INI *ini)
{
	char *cur;
	cur = ini->cur_ptr;
	
	// step through text
	while ( (cur <= ini->last) && (*cur!='\n') && (*cur!='\r') )
		cur++;
	
	// step through new line chars
	while ( (cur <= ini->last) && ((*cur=='\n')||(*cur=='\r')) )
		cur++;
	
	// set cur ptr
	if (cur <= ini->last)
		ini->cur_ptr = cur;
	else
		ini->cur_ptr = 0;
}


// starts from the start on the ini.
// if found sect_ptr = ptr
// else sect_ptr = 0;
// returns 0 if found.. so we don't have to call key_get
int ini_section(INI *ini, const char *sect_name)
{
	if (ini == 0)
		return -1;
	
	string_rejoin(ini);		// in case a key was read
	
	ini->cur_ptr = ini->data;
	
	while (ini->cur_ptr != 0)
	{
		// find a line starting with [
		if ( *(ini->cur_ptr) == '[' )
		{
			ini->cur_ptr++;
			if (string_isolate(ini, ']') == 0)	// isolate the section name
			{
				// if isolated : compare and then rejoin the string
				if (strcmp(sect_name, ini->cur_ptr) == 0) 
				{
					// if equal then return with sect_ptr to next line
					string_rejoin(ini);
					line_next(ini);
					// if the line_next points to nothing.. it will be set to 0 so no further
					// processing will happen anyways
					break;				// RETURN
				}
				else
					string_rejoin(ini);
			}
		}
		
		// try the next line 
		line_next(ini);
	}
	
	ini->sect_ptr = ini->cur_ptr;	// sect_ptr is SET!
	return (ini->sect_ptr == 0);
}

const char *ini_key(INI *ini, const char *key_name)
{
	if (ini == 0)
		return 0;
	string_rejoin(ini);	// in case we're reading a new key
	
	// always search from start of section
	ini->cur_ptr = ini->sect_ptr;
	
	// keep on going until end of file or line starts with [ (assumed vaild sect name)
	while ( (ini->cur_ptr != 0) && (*(ini->cur_ptr) != '[') )
	{
		if (string_isolate(ini, '=') == 0)	// isolate the key name
		{
			// if isolated : compare and then rejoin the string
			if (strcmp(key_name, ini->cur_ptr) == 0) 
			{
				// if equal then return with ptr to key data
				string_rejoin(ini);
				
				// since we're here.. we KNOW there's a '=' 				
				ini->cur_ptr = strchr(ini->cur_ptr, '=') + 1;
				
				if (line_isolate(ini) == 0)
					return ini->cur_ptr;
				// else keep on looking for a line which does work.. which won't exist since
				// it's at the end of the file if this error occurs
			}
			else
				string_rejoin(ini);
		}

		// try the next line 
		line_next(ini);
	}

	if ( (ini->cur_ptr != 0) && (*(ini->cur_ptr) == '[') )
		ini->cur_ptr = 0;	// no use..  might as well eof
	return ini->cur_ptr;
}


// if sect_name == 0
// then use the current one
const char *ini_recursive(INI *ini, const char *sect_name, const char *key_name)
{
	char *sect_orig;
	const char *key_data;
	char *inherits;
	
	if (ini == 0)
		return 0;
	
	//remember old section
	sect_orig = ini->sect_ptr;
	
	if (sect_name != 0)
		ini_section(ini, sect_name);
	
	key_data = ini_key(ini, key_name);
	if (key_data == 0)
	{
		//read inherits key (have to copy it since the null char is removed 
		key_data = ini_key(ini, "inherits");
		if (key_data != 0)
		{
			inherits = alloca(strlen(key_data) + 1);
			strcpy(inherits, key_data);
			key_data = ini_recursive(ini, inherits, key_name);
		}
	}
	
	ini->sect_ptr = sect_orig;
	
	//return to original section
	return key_data;	
}





// FIXME
// it probably won't read key data that starts with [ (returning 0)  FIXED
// crashes if key data ends at EOF  FIXED
// read keys at the end of the file without a newline FIXED

// bugs fixed:
// no longer puts 0's everywhere at the end of lines
// looks cleaner / solid
// put some often used code into functions
// made it reentrant
// doesn't save the ini when you close it
// more than one ini can be opened at a time.

//  recursive inhertitance actually works more than one level
