// Includes, namespace and prototypes
#include "ta_file.h"

// the variables
// for file processing
bool ta_file::is_bin = false;
char ta_file::mySet[50][64];
char ta_file::aBlank[2] = "";
int ta_file::myInd = 0;
int ta_file::myCnt = 0;
int ta_file::myFid = 0;
char ta_file::myFname[1024] = "";
char ta_file::myLine[1024] = "";
char ta_file::holdLine[1024] = "";
std::string ta_file::agk_str = "";

// for debugging
char ta_file::debLine[2048] = "";

bool ta_file::moreFile()
{
	// first, check for open file
	if (ta_file::myFid == 0) return false;

	// now use the file information
	return (agk::FileEOF(ta_file::myFid)?false:true);
}

bool ta_file::openFileRead(const char* afile,bool as_binary/*=false*/)
{
	// check for a file name
	if (!afile)
	{
		ta_oops::set_ta_oops("Missing File Name");
		return false;
	}

	// make sure the file exists
	if (!agk::GetFileExists(afile))
	{
		ta_oops::set_ta_oops("Missing file: %s",afile);
		return false;
	}

	// copy the file name
	strcpy(ta_file::myFname,afile);

	// attempt to open it
	ta_file::myFid = agk::OpenToRead(ta_file::myFname);

	// make sure it opened
	if (!agk::FileIsOpen(ta_file::myFid))
	{
		// clear the file id
		ta_file::myFid = 0;

		// report error
		ta_oops::set_ta_oops("Failed to open %s",ta_file::myFname);
		return false;
	}

	// store the binary flag
	is_bin = as_binary;

	// all is good
	return true;
}

bool ta_file::openFileWrite(const char* afile,bool clear/*=true*/,bool as_binary/*=false*/)
{
	// copy the file name
	strcpy(ta_file::myFname,afile);

	// attempt to open it
	ta_file::myFid = agk::OpenToWrite(ta_file::myFname,(clear?0:1));

	// make sure it opened
	if (agk::FileIsOpen(ta_file::myFid) == 0)
	{
		// clear the file id
		ta_file::myFid = 0;

		// report error
		ta_oops::set_ta_oops("Failed to open %s",ta_file::myFname);
		return false;
	}

	// store the binary flag
	is_bin = as_binary;

	// all is good
	return true;
}

void ta_file::closeFile(void)
{
	// close the file if open
	if (ta_file::myFid) {agk::CloseFile(ta_file::myFid);}

	// clear the values
	ta_file::myFid = 0;
	ta_file::myInd = 0;
	ta_file::myCnt = 0;
}

const char* ta_file::doAGKstr(char* fix_agk_str)
{
	// copy whatever is in the string
	agk_str = fix_agk_str;

	// now clear the memory
	delete[] fix_agk_str;

	// return the string
	return agk_str.c_str();
}

int ta_file::trim_line()
{
	// we get initial length
	int ilen = strlen(ta_file::myLine);

	// check for empty
	if (ilen<1) {return 0;}

	// go from the end first
	int j = ilen - 1;
	for (;j>0;j--)
	{
		// check for a blank
		if (ta_file::myLine[j] == ' ')
		{
			// clear it
			ta_file::myLine[j] = (char)0;
			
			// decrement count
			ilen--;
		} else {
			// done
			break;
		}
	}

	// check for empty
	if (ilen<1) {return 0;}

	// go from the beginning
	int k = ilen - 1;
	int i;
	j = -1;
	while ((j<=k)&&(ilen>0))
	{
		// check for blank
		if (ta_file::myLine[++j] != ' ') break;

		// copy everything to the end
		for (i=j;i<=k;i++) ta_file::myLine[i] = ta_file::myLine[i+1];
			
		// decrement count
		ilen--;
	}

	// all done
	return ilen;
}

bool ta_file::getAndSplitLine(const char* delim,int reqpec,bool no_blank/*=false*/)
{
	// local stuff
	char* line = 0;
	bool no_line = true;

	// assume nothing read
	ta_file::myInd = 0;
	ta_file::myCnt = 0;
	memset(ta_file::myLine,0,1024);

	// if in binary mode, just report good
	if (is_bin) return true;

	// try to find a non-blank, non-comment line
	while (!agk::FileEOF(ta_file::myFid))
	{
		// grab a line
		line = agk::ReadLine(ta_file::myFid);

		// check for comment
		if (line[0] == '#')
		{
			// set to blank
			delete[] line;

			// go to next line
			continue;
		}

		// clear the space and copy the line
		memset(ta_file::myLine,0,1024);
		strncpy(ta_file::myLine,line,1023);

		// clean up
		delete[] line;

		// trim it
		if (ta_file::trim_line()>0)
		{
			// we have a line
			no_line = false;
			break;
		}
	}

	// check for done with file
	if (agk::FileEOF(ta_file::myFid)) ta_file::closeFile();

	// check for line being returned
	if (no_line)
	{
		// if we required a line and didn't find one, that is a problem
		if (no_blank) ta_oops::set_ta_oops("Missing required line in '%s'",ta_file::myFname);

		// indicate no line
		return false;
	}

	// before the next step, copy the line (the strtok overwrites things)
	strcpy(ta_file::holdLine,ta_file::myLine);

	// time to extract bits
	// check for a delimiter
	char* pch = strtok(ta_file::myLine,delim);
	if (pch != NULL)
	{
		while (pch != NULL)
		{
			// copy the piece
			memset(ta_file::mySet[ta_file::myCnt],0,50);
			strncpy(ta_file::mySet[ta_file::myCnt++],pch,49);

			// check for another
			pch = strtok (NULL,delim);
		}
	} else {
		// it is the whole line
		memset(ta_file::mySet[ta_file::myCnt],0,50);
		strncpy(ta_file::mySet[ta_file::myCnt++],ta_file::myLine,49);
	}

	// check for minimum requirement
	if (reqpec > 0)
	{
		if (ta_file::myCnt < reqpec)
		{
			ta_oops::set_ta_oops("Minimum fields is %d, but only found %d in:\n%s",reqpec,ta_file::myCnt,ta_file::holdLine);
			return false;
		}
	}

	// we have something
	return true;
}

int ta_file::splitCount()
{
	// process based on binary or not
	if (is_bin) return 10000;

	// return actual count
	return ta_file::myCnt;
}

void ta_file::debugLine(char* tag)
{
	writeDebug("%s: %s",tag,ta_file::holdLine);
}

void ta_file::showLastLine(char* tag)
{
	char aline[1090];
	sprintf(aline,"%s: %s",tag,ta_file::holdLine);
	agk::Message(aline);
}

const char* ta_file::getValue()
{
	// process based on binary or not
	if (is_bin)
	{
		char* cval = agk::ReadString(myFid);

		// check for blank
		if (!cval) {return ta_file::aBlank;}

		// copy locally
		memset(myLine,0,1024);
		strncpy(myLine,cval,1023);

		// clean up
		delete[] cval;

		// return the next value
		return ta_file::myLine;
	}

	// check for more pieces
	if (ta_file::myInd >= ta_file::myCnt) {return ta_file::aBlank;}

	// return the next piece and increment index
	return ta_file::mySet[ta_file::myInd++];
}

int ta_file::get_int()
{
	// process based on binary or not
	if (is_bin) return agk::ReadInteger(myFid);

	// return an integer value
	return agk::Val(ta_file::getValue());
}

void ta_file::getValue(signed int& ret)
{
	// set the return
	ret = ta_file::get_int();
}

void ta_file::getValue(unsigned int& ret)
{
	// set the return
	ret = (unsigned int)ta_file::get_int();
}

void ta_file::getValue(signed long long& ret)
{
	unsigned long long tmpll;
	
	// get the value as unsigned
	ta_file::getValue(tmpll);
	
	// set the return value
	ret = (signed long long)tmpll;
}

void ta_file::getValue(unsigned long long& ret)
{
	// this function is designed to work across all platforms
	// and anticipate time_t eventually being universally
	// defined as 8 bytes long
	// local vars
	unsigned long long tmpll;
	unsigned int msb,lsb;

	// process based on file mode
	if (is_bin)
	{
		// get two integer values
		lsb = (unsigned int)agk::ReadInteger(myFid);
		msb = (unsigned int)agk::ReadInteger(myFid);

		// convert to 8 bytes
		tmpll = (((unsigned long long)msb)<<32) + ((unsigned long long)lsb);
	} else {
		// check for more pieces
		if (ta_file::myInd >= ta_file::myCnt) {tmpll = -1;}

		// lets scan out the value
		else if (sscanf(ta_file::mySet[ta_file::myInd++],"%16llX",&tmpll)<1) {tmpll = -1;}
	}
	
	// set the return value
	ret = tmpll;
}

void ta_file::getValue(signed short& ret)
{
	// local vars
	unsigned short sret;

	// call function
	ta_file::getValue(sret);

	// convert and return
	ret = (signed short)sret;
}

void ta_file::getValue(unsigned short& ret)
{
	// local vars
	unsigned short msb,lsb;

	// process based on file mode
	if (is_bin)
	{
		// get two integer values
		lsb = (unsigned short)agk::ReadByte(myFid);
		msb = (unsigned short)agk::ReadByte(myFid);

		// convert to 2 bytes and 'return'
		ret = (msb<<8) + lsb;
	} else {
		// simply return the line, converting the value
		ret = (unsigned short)agk::Val(ta_file::getValue());
	}
}

void ta_file::getValue(signed char& ret)
{
	// local vars
	unsigned char sret;

	// call function
	ta_file::getValue(sret);

	// convert and return
	ret = (signed char)sret;
}

void ta_file::getValue(unsigned char& ret)
{
	// process based on file mode
	if (is_bin)
	{
		// binary
		ret = (unsigned char)agk::ReadByte(myFid);
	} else {
		// simply return the line, converting the value
		ret = (unsigned char)agk::Val(ta_file::getValue());
	}
}

void ta_file::getValue(bool& ret)
{
	// process based on binary or not
	// get an integer value
	int i_val = (is_bin ? agk::ReadByte(myFid)
							  : agk::Val(ta_file::getValue()));

	// return true or false
	ret = (i_val!=0?true:false);
}

void ta_file::getValue(float& ret)
{
	// process based on binary or not
	ret = (is_bin ? agk::ReadFloat(myFid) : agk::ValFloat(ta_file::getValue()));
}

void ta_file::putValue(char* val)
{
	// process based on binary or not
	if (is_bin) agk::WriteString(myFid,val);
	else agk::WriteLine(myFid,val);
}

void ta_file::putValue(signed int val)
{
	// process based on binary or not
	if (is_bin) agk::WriteInteger(myFid,val);
	else agk::WriteLine(myFid,ta_file::doAGKstr(agk::Str(val)));
}

void ta_file::putValue(unsigned int val)
{
	// store converted value
	ta_file::putValue((int)val);
}

void ta_file::putValue(signed long long val)
{
	ta_file::putValue((unsigned long long)val);
}

void ta_file::putValue(unsigned long long val)
{
	// local vars
	unsigned long long tmpll;
	unsigned int msb,lsb;
	char utstr[19];

	// copy the value
	tmpll = val;

	// process based on binary or not
	if (is_bin)
	{
		// get the two 4 byte chunks
		msb = (unsigned int)((tmpll&0xFFFFFFFF00000000)>>32);
		lsb = (unsigned int)(tmpll&0x00000000FFFFFFFF);

		// write them to the file
		agk::WriteInteger(myFid,(int)lsb);
		agk::WriteInteger(myFid,(int)msb);
	} else {
		// convert to a workable string
		sprintf(utstr,"%0.16llX",tmpll);

		// write the string
		agk::WriteLine(myFid,(const char*)utstr);
	}
}

void ta_file::putValue(signed short val)
{
	// store converted value
	ta_file::putValue((unsigned short)val);
}

void ta_file::putValue(unsigned short val)
{
	// process based on binary or not
	if (is_bin)
	{
		// write them to the file (lsb-msb)
		agk::WriteByte(myFid,(int)(val&0xFF));
		agk::WriteByte(myFid,(int)((val&0xFF00)>>8));
	} else {
		// simple string
		agk::WriteLine(myFid,ta_file::doAGKstr(agk::Str((int)val)));
	}
}

void ta_file::putValue(signed char val)
{
	// store converted value
	ta_file::putValue((unsigned char)val);
}

void ta_file::putValue(unsigned char val)
{
	// store as byte
	// process based on binary or not
	if (is_bin) agk::WriteByte(myFid,(int)val);
	else agk::WriteLine(myFid,ta_file::doAGKstr(agk::Str((int)val)));
}

void ta_file::putValue(bool val)
{
	// store value as byte or string
	if (is_bin) agk::WriteByte(myFid,(int)(val?1:0));
	else agk::WriteLine(myFid,(val?"1":"0"));
}

void ta_file::putValue(float val)
{
	// process based on binary or not
	if (is_bin) agk::WriteFloat(myFid,val);
	else agk::WriteLine(myFid,ta_file::doAGKstr(agk::Str(val)));
}

void ta_file::initDebug(const char* game)
{
	// open and clear the file
	int fid = agk::OpenToWrite("ta_debugLog.txt",0);

	// close it to force output
	agk::CloseFile(fid);

	// add some info if provided game name
	if (game != NULL)
	{
		ta_file::writeDebug("================================================================================");
		ta_file::writeDebug("Game: %s",game);
		ta_file::writeDebug("Platform: %s",agk::GetDeviceName());
		ta_file::writeDebug("================================================================================");
	}
}

void ta_file::writeDebug(const char* fmt, ...)
{
	// handle the input
	va_list args;

	// create our own string
	va_start(args,fmt);
	vsprintf (ta_file::debLine, fmt, args );
	va_end(args);

	// open in append mode
	int fid = agk::OpenToWrite("ta_debugLog.txt",1);

	// output the string
	agk::WriteLine(fid,ta_file::debLine);

	// close it to force output
	agk::CloseFile(fid);
}
