// UpdatePackage.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include "VariableLoader.h"

bool isVerbose = false;

//CRLF is \13 then \10 
// \09 is tab

void processFile(string fileToOpen, item *dataArea);
void _processFile(jassFile * file, item *dataArea);
bool lineHastContent(string lineIn);
jassFile * fillArray(string fileIn);
string cleanString(string in);
string fixspacing(string in);
string doFormatFor(string functionLine);

int main(int argc, char* argv[])
{ 
	int size, x;
	item fileContents;
	ofstream fileOut;
	string outputA, outputB, outputC, outputD, tmp;
	cout << "Hello!\n";

	processFile("common.j", &fileContents);
	processFile("blizzard.j", &fileContents);

	outputA.append("CREATE TABLE Constant (Name TEXT, Data TEXT);\n");
	outputA.append("CREATE TABLE Function(Name TEXT, code TEXT, isNative NUMERIC DEFAULT 0, isConstant NUMERIC DEFAULT 0);\n");

	outputD.append("    item\n");
	outputD.append("      KeywordListNumber = 5\n");
	outputD.append("      Name = 'natives'\n");
	outputD.append("      Keywords.Strings = (\n");

	for (size=0;size<(int)fileContents.allFunctions.size();size++)
	{
		outputA.append("INSERT INTO Function VALUES('");
		outputA.append(fileContents.allFunctions.at(size).Name);
		outputA.append("','");
		outputA.append(fileContents.allFunctions.at(size).code);
		outputA.append("',");
		if (fileContents.allFunctions.at(size).isNative) {outputA.append("1");} else {outputA.append("0");}
		outputA.append(",");
		if (fileContents.allFunctions.at(size).isConstant) {outputA.append("1");} else {outputA.append("0");}
		outputA.append(");\n");

		outputB.append(fileContents.allFunctions.at(size).Name);
		outputB.append("\n");

		tmp = fileContents.allFunctions.at(size).code;
		x=0;
		while ((x<tmp.size()) && (tmp.at(x) != '\n') && (tmp.at(x) != '\r'))
		{
			x++;
		}
		outputC.append(doFormatFor(tmp.substr(0,x)));
		outputC.append("\n");

		if (fileContents.allFunctions.at(size).isNative)
		{
			outputD.append("          '");
			outputD.append(fileContents.allFunctions.at(size).Name);
			outputD.append("'\n");
		}
	}
	outputD = outputD.substr(0,outputD.size()-1);
	outputD.append(")\n");

	outputD.append("    end\n");
	outputD.append("    item\n");
	outputD.append("      KeywordListNumber = 7\n");
	outputD.append("      Name = 'BJs'\n");
	outputD.append("      Keywords.Strings = (\n");

	for (size=0;size<(int)fileContents.allFunctions.size();size++)
	{
		if (!fileContents.allFunctions.at(size).isNative)
		{
			outputD.append("          '");
			outputD.append(fileContents.allFunctions.at(size).Name);
			outputD.append("'\n");
		}
	}
	outputD = outputD.substr(0,outputD.size()-1);
	outputD.append(")\n");

	outputD.append("    end\n");
	outputD.append("    item\n");
	outputD.append("      KeywordListNumber = 6\n");
	outputD.append("      Name = 'Expand'\n");
	outputD.append("    end\n");
	outputD.append("    item\n");
	outputD.append("      KeywordListNumber = 8\n");
	outputD.append("      Name = 'Constants'\n");
	outputD.append("      Keywords.Strings = (\n");

	for (size=0;size<(int)fileContents.allConstants.size();size++)
	{
		outputA.append("INSERT INTO Constant VALUES('");
		outputA.append(fileContents.allConstants.at(size).Name);
		outputA.append("','");
		outputA.append(fileContents.allConstants.at(size).Data);
		outputA.append("');\n");

		outputD.append("          '");
		outputD.append(fileContents.allConstants.at(size).Name);
		outputD.append("'\n");
	}
	outputD = outputD.substr(0,outputD.size()-1);
	outputD.append(")\n");
	outputD.append("    end>\n");

	fileOut.open("sqlstatements.txt",ios::out);
	fileOut << outputA.c_str();
	fileOut.close();

	fileOut.open("AStrings.Strings.txt",ios::out);
	fileOut << outputB.c_str();
	fileOut.close();

	fileOut.open("ApiStrings.Strings.txt",ios::out);
	fileOut << outputC.c_str();
	fileOut.close();

	fileOut.open("LanguageManager.LanguageList.txt",ios::out);
	fileOut << outputD.c_str();
	fileOut.close();

	cout << "Sleeping...\n";
	Sleep(1000 * 1000);

	return 0;
}

void processFile(string fileToOpen, item *dataArea)
{
	ifstream inputFile;
	char * memBlock;
	int size;
	jassFile * usableFile;
	string rx;
	cout << "Opening File";
	cout << ".";
	inputFile.open(fileToOpen.c_str(), ios::in|ios::binary|ios::ate);
	if (!inputFile.is_open())
	{
		cout << "\n000 Error: blizzard., or common.j is missing! Extract them and run them side by side with this executable.";
		return;
	}
	cout << ".";
    size = inputFile.tellg();
	cout << ".";
    memBlock = new char[size];
	cout << ".";
    inputFile.seekg (0, ios::beg);
	cout << ".";
    inputFile.read (memBlock, size);
    inputFile.close();
	cout << ".\n";

	cout << "Doing preliminary processing\n";
	usableFile = fillArray(memBlock);
	cout << "Processing constants and functions\n";
	_processFile(usableFile, dataArea);

	cout << "File " << fileToOpen.c_str() << " completed!\n";
}


#define FUNC_NOT_STARTED -1
void _processFile(jassFile * file, item *dataArea)
{
	int i, funcStart, y;
	line inFile;
	string prt;
	bool isConstant, isNative;
	Function fstore;
	Constant cstore;
	vector<char> lin, singleL;

	funcStart = FUNC_NOT_STARTED;

	for (i=0;i<(file->amountofLines-1);i++)
	{
		inFile = file->lines->at(i);
		prt = inFile.line_delimited.at(0);
		if ((!prt.compare("function")) || ((!prt.compare("constant")) && (!inFile.line_delimited.at(1).compare("function"))) )
		{
			if (funcStart != FUNC_NOT_STARTED)
			{
				printf("103 Warning: The jass file may have mismatched function/endfunctions\n");
			}
			if (!prt.compare("function"))
			{
				fstore.isConstant = false;
				fstore.isNative = false;
				fstore.Name = cleanString(inFile.line_delimited.at(1));
			}
			else
			{
				fstore.isConstant = true;
				fstore.isNative = false;
				fstore.Name = cleanString(inFile.line_delimited.at(2));
			}
			lin.clear();
			for (y=0;y<(int)inFile.line.size();y++)
			{
				lin.push_back(inFile.line.at(y));
			}
			lin.push_back('\n');
			funcStart = i;
			singleL.clear();
		}
		else if (!prt.compare("endfunction"))
		{
			if (funcStart == FUNC_NOT_STARTED)
			{
				printf("104 Warning: The jass file may have mismatched function/endfunctions\n");
			}
			else
			{
				
				for (y=0;y<(int)inFile.line.size();y++)
				{
					lin.push_back(inFile.line.at(y));
				}
				singleL.push_back('\0');
				lin.push_back('\n');
				lin.push_back('\0');
				fstore.code = cleanString(string((char*)&lin.front()));
				fstore.retVal = cleanString(string((char*)&singleL.front()));
				dataArea->allFunctions.push_back(fstore);
			}
			funcStart = FUNC_NOT_STARTED;
		}
		else if ((!prt.compare("native")) || ((!prt.compare("constant")) && (!inFile.line_delimited.at(1).compare("native"))) )
		{
			if (funcStart != FUNC_NOT_STARTED)
			{
				printf("105 Warning: A native has been found within a function. Strange\n");
			}
			if (!prt.compare("native"))
			{
				fstore.isConstant = false;
				fstore.isNative = true;
				fstore.Name = cleanString(inFile.line_delimited.at(1));
			}
			else
			{
				fstore.isConstant = true;
				fstore.isNative = true;
				fstore.Name = cleanString(inFile.line_delimited.at(2));
			}
			fstore.code = fixspacing(cleanString(inFile.line));
			dataArea->allFunctions.push_back(fstore);
		}
		else if ((!prt.compare("constant")) && (inFile.line_delimited.at(1).compare("native") && inFile.line_delimited.at(1).compare("function")))
		{
			if (funcStart != FUNC_NOT_STARTED)
			{
				printf("106 Warning: A constant has been found within a function. Strange\n");
			}
			cstore.Data = fixspacing(cleanString(inFile.line));
			cstore.Name = cleanString(inFile.line_delimited.at(2));
			dataArea->allConstants.push_back(cstore);
		}
		else
		{
			if (funcStart != FUNC_NOT_STARTED)
			{
				singleL.clear();
				if ((!prt.compare("return")) && (inFile.line_delimited.size() > 1) && (inFile.line_delimited.at(1).compare("nothing")))
				{
					for (y=0;y<(int)inFile.line.size();y++)
					{
						lin.push_back(inFile.line.at(y));
						singleL.push_back(inFile.line.at(y));
					}
				}
				else
				{
					for (y=0;y<(int)inFile.line.size();y++)
					{
						lin.push_back(inFile.line.at(y));
					}
				}
				lin.push_back('\n');
			}
		}
	}
}

bool lineHastContent(string lineIn)
{
	int i, content;
	char curChar;

	if (lineIn.size() <= 0) {return false;}
	content = 0;

	for (i=0;i<(int)lineIn.size();i++)
	{
		curChar = lineIn.at(i);
		if ((curChar != 0x09) && (curChar != 0x0D) && (curChar != 0x0A) && (curChar != ' '))
		{
			content++;
		}
	}
	return (content>0);
}

#define INPUT_OPEN 0
#define INPUT_CRDETECTED 1

jassFile * fillArray(string fileIn)
{
	unsigned int i, x;
	char curChar;
	int curState;
	int lineNumber;
	vector<line> * file;
	line lineBuffer;
	line * curLine;
	string sPart;
	jassFile * retVal;

	//Prepare to grab lines
	curState = INPUT_OPEN;
	lineNumber = 0;
	file = new vector<line>;
	lineBuffer.line = "";

	//This splits up the file into individual lines
	for (i=0;i<fileIn.size();i++)
	{
		curChar = fileIn.at(i);
		if (curChar == 13)
		{
			curState = INPUT_CRDETECTED;
		}
		else if (curChar == 10)
		{
			if (curState == INPUT_OPEN)
			{
				if (isVerbose) {printf("101 Warning: Found a mismatched LF character\n");}
			}
			if (lineHastContent(lineBuffer.line))
			{
				file->push_back(lineBuffer);
				lineBuffer.line = "";
				lineNumber++;
			}
			curState = INPUT_OPEN;
		}
		else if (curState == INPUT_CRDETECTED)
		{
			if (isVerbose) {printf("102 Warning: Found a mismatched CR character\n");}
			if (lineHastContent(lineBuffer.line))
			{
				file->push_back(lineBuffer);
				lineBuffer.line = "";
				lineNumber++;
			}
			curState = INPUT_OPEN;
		}
		else
		{
			lineBuffer.line += curChar;
		}
	}
	if (lineHastContent(lineBuffer.line))//Incase there isn't a newline at the end of the file, this if statement grabs the last line of code
	{
		file->push_back(lineBuffer);
		lineBuffer.line = "";
		lineNumber++;
	}
	for (i=0;i<(unsigned int)lineNumber;i++)
	{
		curLine = &file->at(i);
		sPart = "";
		for (x=0;x<curLine->line.size();x++)
		{
			curChar = curLine->line.at(x);
			if ((curChar == ' ') || (curChar == 0x09))
			{
				if (sPart.compare(""))
				{
					curLine->line_delimited.push_back(sPart);
					curLine->line_nospace.append(sPart);
					sPart = "";
				}
			}
			else
			{
				sPart +=curChar;
			}
		}
		if (sPart.compare(""))
		{
			curLine->line_delimited.push_back(sPart);
			curLine->line_nospace.append(sPart);
			sPart = "";
		}
	}
	retVal = new jassFile;
	retVal->amountofLines = lineNumber;
	retVal->lines = file;
	return retVal;
}


string cleanString(string in) //this helps with sql
{
	unsigned int x;
	string out, test;
	for (x=0;x<in.size();x++)
	{
		test = in.at(x);
		if (!test.compare("'"))
		{
			test = "''";
		}
		out.append(test);
	}
	return out;
}

string fixspacing(string in) //this gets rid of consecutive spacing in natives & constants
{
	unsigned int x;
	string out, test;
	bool lastwasspace = false;

	for (x=0;x<in.size();x++)
	{
		test = in.at(x);
		if ((!test.compare(" ")) || (in.at(x) == 9))
		{
			if (lastwasspace)
			{
				test = "";
			}
			else
			{
				lastwasspace = true;
			}
		}
		else
		{
			lastwasspace = false;
		}
		out.append(test);
	}
	return out;
}

string doFormatFor(string functionLine)
{
	int x;
	char c;
	string val, retline, output;
	int curStep = 0;
	bool onVarType;

	retline = cleanString(functionLine);
	for (x=0;x<retline.size();x++)
	{
		c = retline.at(x);
		if (c == ' ')
		{
			if (!val.compare("constant"))
			{
				curStep = 0;
			}
			else if ((!val.compare("function")) || (!val.compare("native")))
			{
				curStep = 1;
			}
			else if (curStep == 1)
			{
				output = val;
				curStep = 2;
			}
			else if (!val.compare("takes"))
			{
				curStep = 3;
				onVarType = true;
				output.append("(");
			}
			else if ((curStep == 3) || (curStep == 4))
			{
				if (!val.compare("nothing"))
				{
					output.append("nothing)");
					curStep = 5;
				}
				else if (!val.compare("returns"))
				{
					curStep = 6;
					output.append(")");
				}
				else if (onVarType)
				{
					if (curStep == 4)
					{
						output.append(" ");
					}
					else
					{
						curStep = 4;
					}
					output.append(val);
					output.append(" ");
					onVarType = false;
				}
				else
				{
					output.append(val);
					onVarType = true;
				}
			}
			else if (!val.compare("returns"))
			{
				curStep = 6;
			}
			else if (curStep == 6)
			{
				if (val.compare("nothing"))
				{
					output.append("returns ");
					output.append(val);
				}
				curStep = 7;
			}
			val = "";
		}
		else
		{
			val += c;
		}
	}
	if (curStep == 6)
	{
		if (val.compare("nothing"))
		{
			output.append("returns ");
			output.append(val);
		}
		curStep = 7;
	}
	return output;
}