// GameSettings.cpp: implementation of the GameSettings class.
//
//////////////////////////////////////////////////////////////////////

#include "GameSettings.h"

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

GameSettings::GameSettings(LPTSTR Filename, bool saveincurrentdir)
{

	// check if we must consider the file in current directory
	// calculate current directory
	if (saveincurrentdir) {


			TCHAR lpFileName[512],lpFileName2[512];
	
			// extract exe path
			GetModuleFileName(NULL, lpFileName, 255);
		
			// get the last backslash
			for (int i=_tcslen(lpFileName)-1; i>=0; i--) {
				if ( lpFileName[i] == _T('\\') ) {
					_stprintf(lpFileName2, _T("%.*s"), i+1, lpFileName); 
					break;
				}
			}

			// append the filename
			_tcscat(lpFileName2, Filename);

			// save filename
			_tcscpy(filename, lpFileName2);


	} else {
			// use full path for file
			// save filename
			_tcscpy(filename, Filename);
	}

#ifdef DRAWDEBUG1
	char _mfilename[255];
	WideCharToMultiByte(CP_ACP, 0, filename, wcslen(filename)+1, _mfilename , sizeof(_mfilename), NULL, NULL);
	PFrogConsole::instance()->addconsole(0, "SETTINGS FILE: %s", _mfilename);
#endif

	// no buffered mode when we start
	bufferedMode=false;

	// no crypt by default
	cryptKey=0;

	// we have not sections when we start
	headsection = NULL;
}

GameSettings::~GameSettings()
{
	// maio - aggiunto 11 / 03 / 2008
	// come mai non c'era? O_O
	if (this->bufferedMode)
		this->EndBufferedOperation();
}

void GameSettings::loadsettings()
{
	char tmpbuf[(MAXLINEBUFFER+1)*3];	// for string encoding
	char lastsection[(MAXLINEBUFFER+1)*3];	// for string encoding
	char key[(MAXLINEBUFFER+1)*3], value[(MAXLINEBUFFER+1)*3];	// *3 for string encoding
	char *tmp1, *tmp2;
	struct keys *pk = NULL;
	char *ch;
	FILE *fp;

	// sanity check!! Head is not null!!!!
	if (headsection)
		return;

	// open file	
	fp = _tfopen(filename, _T("r") );

	// can't open file
	if (!fp)
		return;

	// first last section is a void string
	strcpy(lastsection, "");

	// read file until eof
	while (!feof(fp)) {

		// read line
		fgets(tmpbuf, MAXLINEBUFFER, fp);

		if (tmpbuf && strlen(tmpbuf) > 0) {

			if (strlen(tmpbuf) > 3 && tmpbuf[0] == '[' && tmpbuf[strlen(tmpbuf)-2] == ']') {

				// it's a section - save value
				// remove [ and ]
				sscanf(tmpbuf, "%*c%[^]]", lastsection);

				// --> decode string if we have a crypt key
				if (this->cryptKey > 0) {
					tmp1=this->StringDecrypt(lastsection);
					if (tmp1) {
						strcpy(lastsection, tmp1);
						free(tmp1);
						tmp1=NULL;
					}
					else
						strcpy(lastsection, "-");
				}
				// <--

			} else {

				// it a key/value pair
				strcpy(value, "");
				strcpy(key, "");

				// extract key/value pair
				ch = strstr(tmpbuf, "=");
				if (ch) {
					sscanf(tmpbuf,"%[^=]%*c%[^\n]", key, value);
				} else {
					// = not found, ignore values
				}

				// --> decode strings if we have a crypt key
				if (this->cryptKey > 0) {
					tmp1=this->StringDecrypt(key);
					tmp2=this->StringDecrypt(value);
					if (tmp1 && tmp2) {
						strcpy(key, tmp1);
						strcpy(value, tmp2);
					} else {
						strcpy(key, "-");
						strcpy(value, "-");
					}
					if (tmp1) { free(tmp1); tmp1=NULL; }
					if (tmp2) { free(tmp2); tmp2=NULL; }
				}
				// <--

				// add key/value pair
				addvalue(lastsection, key, value);

			}	// endif (is not section)

		}	// something read?

	}	// file read cycle


	// close file
	fclose(fp);

}

void *GameSettings::findsection(char *section)
{
	struct sections *p = headsection;
	while (p) {
		if (strcmp(p->section, section) == 0)
			return p;
		p = p->next;
	}
	return NULL;
}

void *GameSettings::findkey(char *section, char *key)
{
	struct sections *psect = (struct sections *)findsection(section);
	if (psect) {
		struct keys *pkey = psect->key;
		while (pkey) {
			if (strcmp(pkey->key, key) == 0) 
				return pkey;
			pkey = pkey->next;
		}
	}
	return NULL;
}

void GameSettings::freestructs()
{
	// free all structs
	struct sections *psectnext, *psect = headsection;
	struct keys *pkeynext, *pkey;
	// iterate sections
	while (psect) {

		// save next section
		psectnext = psect->next;
		// iterate all keys
		pkey = psect->key;

		while (pkey) {
			// save next key
			pkeynext = pkey->next;
			// free the key
			free(pkey->key);
			free(pkey->value);
			delete pkey;
			// go next key
			pkey = pkeynext;
		}

		// free the section
		free(psect->section);
		delete psect;

		// go next section
		psect = psectnext;

	}

	// delete the head pointer
	headsection = NULL;
}

void GameSettings::savesettings()
{
	struct sections *psect = headsection;
	struct keys *pkey;


	FILE *fp;
	fp = _tfopen(filename, _T("w") );

	char *tmp1, *tmp2;

	if (fp) {
		// iterate sections
		while (psect) {
			// save section name
			if (this->cryptKey > 0) {
				// save crypted
				tmp1=this->StringCrypt(psect->section);
				if (tmp1) {
					fprintf(fp, "[%s]\n", tmp1);
					free(tmp1);
					tmp1=NULL;
				}
			} else {
				// save uncrypted
				fprintf(fp, "[%s]\n", psect->section);
			}

			// foreach section, iterate keys
			pkey = psect->key;
			while (pkey) {
				// save key=value
				if (this->cryptKey > 0) {
					// save crypted
					tmp1=this->StringCrypt(pkey->key);
					tmp2=this->StringCrypt(pkey->value);
					if (tmp1 && tmp2)
						fprintf(fp,"%s=%s\n", tmp1, tmp2);
					if (tmp1) { free(tmp1); tmp1=NULL; }
					if (tmp2) { free(tmp2); tmp2=NULL; }
				} else {
					// save uncrypted
					fprintf(fp,"%s=%s\n", pkey->key, pkey->value);
				}
				pkey = pkey->next;
			}
			// place new line between sections
			fprintf(fp, "\n");
			psect = psect->next;
		}
	}

	// close file
	fclose(fp);

}

// add new section/key/value item to struct
void GameSettings::addvalue(char *section, char *key, char *value)
{
	// find section
	struct sections *cursect = (struct sections *)findsection(section);
	if (!cursect) {	// section not exists, create new
		cursect = new sections;
		cursect->key = NULL;	// no keys for first section item
		
		// malloc memory for the value and copy
		cursect->section = (char *) malloc(strlen(section)+1);
		strcpy(cursect->section, section);

		// link to overall struct
		cursect->next = headsection;
		headsection = cursect;
	}

	// check if we must create also a key/value pair and link to current section
	if (key && strlen(key) > 0) {
		// create value and key
		// first try to find key
		struct keys *curkey = (struct keys *)findkey(section, key);

		if (!curkey) {	// key not exists, create new

			curkey = new keys;
			curkey->key = (char *) malloc(strlen(key)+1);
			strcpy(curkey->key, key);

			// point created key to our section		
			curkey->next = cursect->key ;		// relink new to old head
			cursect->key  = curkey ;			// set new as new head

		} else {

			// key exists already, free old value
			free(curkey->value);

		}

		// finally save new value for current key
		curkey->value = (char *) malloc(strlen(value)+1);
		strcpy(curkey->value, value);

	}	// we have a key to save

}

void GameSettings::StartBufferedOperation()
{
	// save buffered mode, load all struct and set flag
	this->bufferedMode=true;

	// load file in struct
	loadsettings();

}

void GameSettings::EndBufferedOperation()
{
	// end buffered mode, save file and free struct
	this->bufferedMode=false;

	// now save entire struct
	savesettings();

	// free structs
	freestructs();
}




void GameSettings::SetCryptKey(unsigned char newkey)
{
	// set a crypt key
	this->cryptKey=newkey;
}

char *GameSettings::StringCrypt(char *str)
{
	// malloc return string
	char *ret = (char *) malloc((strlen(str) * 3) +2);

	if (!ret)
		return NULL;	// sanity check

	unsigned char xorred;
	char chunk[4];

	// iterate string
	strcpy(ret, "");
	for (unsigned short x=0; x < strlen(str); x++) {
		// xor our char according to its position and keyvalue
		xorred=str[x] ^ (cryptKey << (x+1));
		// build encoded val
		sprintf(chunk, "%03u", xorred);
		// add chunks to final string
		strcat(ret, chunk);
	}	// string iteration

	// ok, return encrypted string
	return ret;
}

char *GameSettings::StringDecrypt(char *str)
{
	// malloc return string
	char *ret = (char *) malloc((strlen(str) / 3) + 2);

	// sanity check
	if (!ret)
		return NULL;

	// if encrypted string length is not multiply of 3, we have an encode error, so exit!
	if (strlen(str) % 3 != 0)
		return NULL;

	unsigned char unxorred;
	char tmpb[4];

	// iterate encrypted string
	strcpy(ret, "");
	for (unsigned short x=0; x < strlen(str); x+=3) {
		// get 3 encoded chars
		sprintf(tmpb, "%c%c%c", str[x], str[x+1], str[x+2]);
		// get decrypted char
		unxorred=atoi(tmpb) ^ (cryptKey << ((x / 3)+1));
		sprintf(tmpb, "%c", unxorred);
		// add char to string and continue with next
		strcat(ret, tmpb);
	}	// string iteration
	// return decrypted string
	return ret;
}




void GameSettings::_SaveSetting(char *section, char *key, char *value)
{
	// load file in struct (only if we aren't in buffered mode)
	if (!bufferedMode)
		loadsettings();

	/*
	if (!headsection)	// nothing loaded? exit
		return;
	????
	*/

	// add new item to struct
	addvalue(section, key, value);

	// now save entire struct (if we aren't in buffered mode)
	if (!bufferedMode)
		savesettings();

	// free structs (if we aren't in buffered mode)
	if (!bufferedMode)
		freestructs();
}

char *GameSettings::_LoadSetting(char *section, char *key, char *defvalue, size_t maxsize)
{
	// load file in struct (if we aren't in buffered mode)
	if (!bufferedMode)
		loadsettings();

	// allocate return buffer the found value
	char *foundvalue = (char *)malloc(maxsize+1);
	if (defvalue)
		strcpy(foundvalue, defvalue);
	else
		strcpy(foundvalue, "");

	// find section
	struct sections *cursect = (struct sections *)findsection(section);
	// find key
	if (cursect) {
		struct keys *curkey = (struct keys *)findkey(section, key);
		if (curkey)
			strcpy(foundvalue, curkey->value);
	}

	// free structs (if we aren't in buffered mode)
	if (!bufferedMode)
		freestructs();

	// returns the value
	return foundvalue;
}

void GameSettings::SaveBool(char *section, char *key, bool value)
{
	char tmp[4];
	if (value)
		strcpy(tmp, "1");
	else
		strcpy(tmp, "0");
	this->_SaveSetting(section, key, tmp);
}

bool GameSettings::LoadBool(char *section, char *key, bool defvalue)
{
	char *tmp;
	bool valz;

	tmp=_LoadSetting(section, key,"-1",4);

	if		(strcmp(tmp,"1") == 0)
		valz=true;
	else if (strcmp(tmp,"0") == 0)
		valz=false;
	else
		valz=defvalue;

	if (tmp)
		free(tmp);

	return valz;
}

void GameSettings::SaveLong(char *section, char *key, long value)
{
	char tmp[30];
	sprintf(tmp, "%ld", value);
	_SaveSetting(section, key, tmp);
}

long GameSettings::LoadLong(char *section, char *key, long defvalue)
{
	char defValue[30];
	sprintf(defValue, "%ld", defvalue);

	char *tmp = _LoadSetting(section, key, defValue, 30);
	long ret = atol(tmp);

	if (tmp)
		free(tmp);

	return ret;
}

void GameSettings::SaveShortString(char *section, char *key, char *string)
{
	_SaveSetting(section, key, string);
}

char *GameSettings::LoadShortString(char *section, char *key, char *defvalue)
{
	return _LoadSetting(section, key, defvalue, 512);
}
