/* Ham Sandwich
 *   Copyright 2007-2014
 *   By the AMX Mod X Development Team
 *
 *  Ham Sandwich is free software; you can redistribute it and/or modify it
 *  under the terms of the GNU General Public License as published by the
 *  Free Software Foundation; either version 2 of the License, or (at
 *  your option) any later version.
 *
 *  Ham Sandwich is distributed in the hope that it will be useful, but
 *  WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 *  General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with Ham Sandwich; if not, write to the Free Software Foundation,
 *  Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 *
 *  In addition, as a special exception, the author gives permission to
 *  link the code of Ham Sandwich with the Half-Life Game Engine ("HL
 *  Engine") and Modified Game Libraries ("MODs") developed by Valve,
 *  L.L.C ("Valve"). You must obey the GNU General Public License in all
 *  respects for all of the code used other than the HL Engine and MODs
 *  from Valve. If you modify this file, you may extend this exception
 *  to your version of the file, but you are not obligated to do so. If
 *  you do not wish to do so, delete this exception statement from your
 *  version.
 */
#include "amxxmodule.h"

#include "ham_const.h"
#include "hooklist.h"
#include "offsets.h"

extern hook_t hooklist[];

enum
{
	LEX_INVALID = 0,

	LEX_UNKNOWN,

	LEX_START_SEC,
	LEX_END_SEC,

	LEX_MIRROR,

	LEX_PEV,
	LEX_BASE,

	LEX_END
};

const char *tokens[] =
{
	"", // LEX_INVALID

	"", // LEX_UNKNOWN

	"@section", // LEX_START_SEC
	"@end", // LEX_END_SEC

	"@mirror", // LEX_MIRROR

	"pev", // LEX_PEV
	"base", // LEX_BASE

	"", // LEX_END
};

static void trim_line(char *input);
static void read_mirror(char *input);
static void skip_to_end_of_section(FILE *fp);
static int lex(char*& buffer);

int lex(char*& buffer)
{
	trim_line(buffer);

	size_t len;

	for (int i=0; i<LEX_END; i++)
	{
		if (tokens[i]!=NULL && *(tokens[i])!='\0')
		{
			len=strlen(tokens[i]);
			if (strncmp(buffer,tokens[i],len)==0)
			{
				buffer+=len+1;
				return i;
			}
		}
	}
	return LEX_UNKNOWN;
}

// How we handle "mirrors"
// We just note down the current mod name, and every time
// we come across a mirror with the destination that matches
// the current mod name, we change the current mod name to
// the source for that mirror.

char CurrentModName[64];

static void read_mirror(char *input)
{
	char *data=input;
	char *data2;

	char source[64];
	char dest[64];

	char old;
	while ( *data!=' ' &&
			*data!='\t' &&
			*data!='\0')
	{

		data++;
	}

	old=*data;
	*data='\0';

	// mark down the source
	snprintf(source, sizeof(source)-1, "%s", input);

	*data=old;

	while ( *data==' ' ||
			*data=='\t')
	{
		data++;
	}
	data2=data;

	while ( *data!=' ' &&
			*data!='\t' &&
			*data!='\0')
	{
		data++;
	}
	old=*data;
	*data='\0';

	snprintf(dest, sizeof(dest)-1, "%s", data2);

	*data=old;

	if (strcmp(dest, CurrentModName)==0)
	{
		snprintf(CurrentModName, sizeof(CurrentModName)-1, "%s", source);
	}

}

static void trim_line(char *input)
{
	char *oldinput=input;
	char *start=input;

	while ( *start==' ' ||
			*start=='\t' ||
			*start=='\r' ||
			*start=='\n')
	{
		start++;
	}

	// Overwrite the whitespace

	if (start != input)
	{
		while ((*input++=*start++)!='\0')
			/* do nothing */ ;
	}

	start=oldinput;

	start+=strlen(start) - 1;

	while ( start >= oldinput &&
			( *start == '\0' ||
			  *start == ' '  ||
			  *start == '\r' ||
			  *start == '\n' ||
			  *start == '\t'))
	{
		start--;
	}
	start++;
	*start='\0';

	// Now find any comments and cut off at the start

	while (*start != '\0')
	{
		if (*start == ';')
		{
			*start='\0';
			break;
		}

		start++;
	}
}

void skip_to_end_of_section(FILE *fp)
{
	char buffer[1024];

	while (!feof(fp))
	{
		buffer[0]='\0';

		fgets(buffer, sizeof(buffer)-1, fp);

		trim_line(buffer);

		char *b=&buffer[0];
		if (lex(b)==LEX_END_SEC)
		{
			break;
		}
	}
}
static const char* get_localinfo( const char* name , const char* def = 0 )
{
	const char* b = LOCALINFO( (char*)name );
	if (((b==0)||(*b==0)) && def )
		SET_LOCALINFO((char*)name,(char*)(b = def) );
	return b;
}
int read_start_section(char *data)
{
	if (strncasecmp(data, CurrentModName, strlen(CurrentModName))==0)
	{
		data+=strlen(CurrentModName)+1;
		trim_line(data);

#ifdef _WIN32
		if (strcmp(data, "windows")==0)
#elif defined(__linux__)
		if (strcmp(data, "linux")==0)
#elif defined(__APPLE__)
		if (strcmp(data, "mac")==0)
#endif
		{
			return 1;
		}
	}
	return 0;
}
int read_number(char *input)
{
	char *end; /* Temporary pointer, needed for strtoul(). */

	// if begins with 0x or 0X it's to be interpretted as hex
	if (*input=='0' && 
		(*(input+1)=='x' || *(input+1)=='X'))
	{
		return strtoul(input,&end,16);
	}

	// otherwise it's to be interpretted as base 10
	return strtoul(input,&end,10);
}
void process_pev(char *data)
{
	trim_line(data);
	Offsets.SetPev(read_number(data));
}
void process_base(char *data)
{
	trim_line(data);
	Offsets.SetBase(read_number(data));
}
void process_key(char *data)
{
	size_t size=0;

	char *a=data;

	while (*a != ' ' && *a != '\t' && *a != '\0')
	{
		a++;
		size++;
	}

	if (size==0)
	{
		return;
	}
	int set=0;
	for (int i=0; i< HAM_LAST_ENTRY_DONT_USE_ME_LOL; i++)
	{
		if (strncmp(data, hooklist[i].name, size)==0)
		{
			data+=size+1;

			trim_line(data);
			int value=read_number(data);

			hooklist[i].isset=1;
			hooklist[i].vtid=value;


			set=1;
			break;

		}
	}

	if (set==0)
	{
		printf("stray key in process_key: %s\n", data);
	}

}
int ReadConfig(void)
{
	char FileName[512];

	MF_BuildPathnameR(FileName,sizeof(FileName)-1,"%s",get_localinfo("amxx_configsdir","addons/amxmodx/configs"));

	strncat(FileName,"/hamdata.ini",sizeof(FileName)-1);

	FILE *fp=fopen(FileName,"r");


	snprintf(CurrentModName, sizeof(CurrentModName)-1, "%s", MF_GetModname());

	if (!fp)
	{
		MF_Log("Unable to open \"%s\" for reading.", FileName);

		return -1;
	}

	char data[2048];

	int insec=0;

	while (!feof(fp))
	{
		data[0]='\0';

		fgets(data, sizeof(data)-1, fp);

		char *b=&data[0];

		switch(lex(b))
		{
		case LEX_PEV:
			{
				if (insec)
				{
					process_pev(b);
				}
				break;
			};
		case LEX_BASE:
			{
				if (insec)
				{
					process_base(b);
				}
				break;
			};
		case LEX_MIRROR:
			{
				read_mirror(b);
				break;
			};
		case LEX_START_SEC:
			{
				insec=read_start_section(b);

				if (!insec)
				{
					skip_to_end_of_section(fp);
				}
				break;
			};
		case LEX_END_SEC:
			{
				insec=0;
				break;
			};
		case LEX_UNKNOWN:
			{
				if (insec)
				{
					process_key(b);
				}
			};
		}


	}

	return 1;
}