#include <iostream>
#include <fstream>
#include <stdlib.h>
#include <string.h>
#include "gct.h"

#define ERRORRANGE "Error: CheatNr out of range"

GCTCheats::GCTCheats(void)
{
	Reset();
}

GCTCheats::~GCTCheats(void)
{
	string sGameID ="";
	string sGameTitle = "";
}

unsigned int GCTCheats::getCnt()
{
	return iCntCheats;
}

string GCTCheats::getGameName(void)
{
	return sGameTitle;
}

string GCTCheats::getGameID(void)
{
	return sGameID;
}

string GCTCheats::getCheat(unsigned int nr)
{
	if (nr <= (iCntCheats-1))
		return sCheats[nr];
	return ERRORRANGE;
}

string GCTCheats::getCheatName(unsigned int nr)
{
	if (nr <= (iCntCheats-1))
		return sCheatName[nr];
	return ERRORRANGE;
}

string GCTCheats::getCheatComment(unsigned int nr)
{
	if (nr <= (iCntCheats-1))
		return sCheatComment[nr];
	return ERRORRANGE;
}

//creates gct from internal array
int GCTCheats::createGCT(const char * filename)
{
	ofstream filestr;
	filestr.open(filename);
	if (filestr.fail()) return 0;

	//Header and Footer
	char header[] = { 0x00, 0xd0, 0xc0, 0xde, 0x00, 0xd0, 0xc0, 0xde};
	char footer[] = { 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};

	filestr.write(header,sizeof(header));

	for (unsigned int i=0; i < iCntCheats; ++i)
		if (sCheatSelected[i] == true)
		{
			// cheat is selected, export it
			string buf = getCheat(i);

			int x = 0;
			while (x < (int)buf.size())
			{
				string temp = buf.substr(x,2);
				long int li = strtol(temp.c_str(),NULL,16);
				temp = li;
				filestr.write(temp.c_str(),1);
				x +=2;
			}
		}
	filestr.write(footer,sizeof(footer));
	filestr.close();

	return 1;
}

//rewrite and save code.txt to mark which codes are #selected#
int GCTCheats::createTXT(const char * filename)
{

	// save gct file
	fstream file;
	file.open(filename,ios::out);

	file << sGameID << endl;
	file << sGameTitle << endl << endl;

	for (unsigned int i=0; i < iCntCheats; ++i)
		if  (sCheatSelected[i])
		{
			file << sCheatName[i] << endl;
			for (unsigned int j=0; j+8 < sCheats[i].size(); j+=16)
				file << sCheats[i].substr(j,8) << " " << sCheats[i].substr(j+8,8) << endl;

			file << "#selected#" << sCheatComment[i] << endl;
			file << endl;
		}
			
	for (unsigned int i=0; i < iCntCheats; ++i)
		if  (!sCheatSelected[i])
		{
			file << sCheatName[i] << endl;
			for (unsigned int j=0; j+8 < sCheats[i].size(); j+=16)
				file << sCheats[i].substr(j,8) << " " << sCheats[i].substr(j+8,8) << endl;

			if (sCheatComment[i].size() > 1)
				file << sCheatComment[i] << endl;
			file << endl;
		}

	file.close();
	return 1;
}


int GCTCheats::openTxtfile(const char * filename)
{
	Reset();

	ifstream filestr;
	filestr.open(filename);
	if (filestr.fail()) return 0;

	int i = 0;
	string str;

	filestr.seekg(0,ios_base::end);
	int size = filestr.tellg();
	if (size <= 0) return -1;
	filestr.seekg(0,ios_base::beg);

	getline(filestr,sGameID);
	if (sGameID[sGameID.length() - 1] == '\r')
		sGameID.erase(sGameID.length() - 1);
	
	getline(filestr,sGameTitle);
	if (sGameTitle[sGameTitle.length() - 1] == '\r')
		sGameTitle.erase(sGameTitle.length() - 1);
				
	while (!filestr.eof()) 
	{
		getline(filestr,sCheatName[i]);
		if (sCheatName[i][sCheatName[i].length() - 1] == '\r')
			sCheatName[i].erase(sCheatName[i].length() - 1);

		if (sCheatName[i].length() == 0)
			continue;
			
		string cheatdata;

		while (!filestr.eof())
		{
			getline(filestr,str);
			if (str[str.length() - 1] == '\r')
				str.erase(str.length() - 1);
				 
			if (str == "" || str[0] == '\r' || str[0] == '\n')
				break;

			if (IsCode(str))
			{
				// remove any garbage (comment) after code
				while (str.size() > 17)
					str.erase(str.length() - 1);

				cheatdata.append(str);
				size_t found=cheatdata.find(' ');
				cheatdata.replace(found,1,"");
			}
			else
				sCheatComment[i] = str;
		}

		if (cheatdata.size() > 0)
		{
			sCheats[i] = cheatdata;
			sCheatSelected[i] = false;
			// wiiflow rewrites and saves code.txt with #selected# added to comments if cheat selected
			if (sCheatComment[i].compare(0,10,"#selected#") == 0)
			{
				sCheatSelected[i] = true;
				sCheatComment[i].erase(0,10);
			}
			i++;
		}
		else
			sCheatComment[i] = "";
			
		if (i == MAXCHEATS) break;
	}
	iCntCheats = i;
	filestr.close();
	return 1;
}

bool GCTCheats::IsCode(const std::string& str)
{
	if (str[8] == ' ' && str.size() >= 17)
	{
		// accept strings longer than 17 in case there is a comment on the same line as the code
		char part1[9];
		char part2[9];
		snprintf(part1,sizeof(part1),"%c%c%c%c%c%c%c%c",str[0],str[1],str[2],str[3],str[4],str[5],str[6],str[7]);
		snprintf(part2,sizeof(part2),"%c%c%c%c%c%c%c%c",str[9],str[10],str[11],str[12],str[13],str[14],str[15],str[16]);
		if ((strtok(part1,"0123456789ABCDEFabcdef") == NULL) && (strtok(part2,"0123456789ABCDEFabcdef") == NULL))
			return true;
	}
	return false;
}


int GCTCheats::IsCodeEx(const std::string& str)
{
	int status = 2;
	if (str[8] == ' ' && str.size() >= 17)
	{
		// accept strings longer than 17 in case there is a comment on the same line as the code
		for (int i = 0; i < 17; ++i)
		{
			if (!(str[i] == '0' || str[i] == '1' || str[i] == '2' || str[i] == '3' ||
				  str[i] == '4' || str[i] == '5' || str[i] == '6' || str[i] == '7' ||
				  str[i] == '8' || str[i] == '9' || str[i] == 'a' || str[i] == 'b' ||
				  str[i] == 'c' || str[i] == 'd' || str[i] == 'e' || str[i] == 'f' ||
				  str[i] == 'A' || str[i] == 'B' || str[i] == 'C' || str[i] == 'D' ||
				  str[i] == 'E' || str[i] == 'F' || str[i] == 'x' || str[i] == 'X') && i!=8 )
				status  = 0; // not even x -> no code
			if ((str[i] == 'x' || str[i] == 'X') && i!=8 && status >= 1)
				status  = 1;
		}
		return status;
	}
	return 0; // no code
}

void GCTCheats::Reset()
{
	iCntCheats = 0;
	for (int i=0;i<MAXCHEATS;++i) 
		sCheatSelected[i] = false;
}