#include <fstream.h>
#include <z_store.hpp>
#include <z_i18n.hpp>
#include "makemap.hpp"
//#include <ctype.h>

ZafIChar *toLocalU = ZAF_ITEXT("unicodeToLocal");
ZafIChar *localToU = ZAF_ITEXT("localToUnicode");
ZafNumberID numIDU = 3123;
ZafStringID strngIDU = ZAF_ITEXT("ZafWcharLookup");

ZafIChar *toLocalI = ZAF_ITEXT("isoToLocal");
ZafIChar *localToI = ZAF_ITEXT("localToIso");
ZafStringID strngIDI = ZAF_ITEXT("ZafCharLookup");
ZafNumberID numIDI = 3122;

SubTable::SubTable(ZafUInt16 initialValue, ZafUInt8 position, bool tIsUni):
	isUni(tIsUni)
{
	minValue = maxValue = position;
	memset(mapping, 0, sizeof(mapping));
	mapping[position] = initialValue;
}

void SubTable::AddMapping(ZafUInt16 value, ZafUInt8 position)
{
	if (position < minValue)
		minValue = position;
	else if (position > maxValue)
		maxValue = position;

	mapping[position] = value;
}

void SubTable::WriteData(ZafFile *file)
{
	int i;
	ZafUInt8 value;
	ZafUInt16 value1;

	*file << minValue << maxValue;
	if (isUni)
		for (i = minValue; i <= maxValue; i++)
		{
			value1 = (ZafUInt16)mapping[i];
			*file<<value1;
		}
	else
		for (i = minValue; i <= maxValue; i++)
		{
			value = (ZafUInt8)mapping[i];
			*file<<value;
		}
}

Mapping::Mapping(): noOfLeadins(0), toLocal(toLocalU), localTo(localToU),
	numID(numIDU), strngID(strngIDU), isUni(true)
{
	memset(localToUnicode, 0, sizeof(localToUnicode));
	memset(unicodeToLocal, 0, sizeof(unicodeToLocal));
}

void Mapping::AddMapping(ZafUInt16 codeMap, ZafUInt16 uniMap)
{
	int pageNum;
	pageNum = PageNum(uniMap);
	if (!unicodeToLocal[pageNum])
 	{
 		unicodeToLocal[pageNum] = new SubTable(codeMap, uniMap & 0xff, isUni); 
 	}
	else
		unicodeToLocal[pageNum]->AddMapping(codeMap, uniMap & 0xff);


	pageNum = PageNum(codeMap);
	if (!localToUnicode[pageNum])
 	{
 		localToUnicode[pageNum] = new SubTable(uniMap, codeMap & 0xff, isUni); 
 	}
	else
		localToUnicode[pageNum]->AddMapping(uniMap, codeMap & 0xff);
}

void Mapping::AddLeadin(ZafUInt8 leadByte)
{
	if (noOfLeadins < 0)
		noOfLeadins = 0;

	int i;
	for (i = 0; i < noOfLeadins; i++)
		if (leadin[i].maxValue + 1 == leadByte)
		{
			leadin[i].maxValue = leadByte;
			break;
		}
		else if (leadin[i].minValue - 1 == leadByte)
		if (leadin[i].maxValue + 1 == leadByte)
		{
			leadin[i].minValue = leadByte;
			break;
		}
	if (i == noOfLeadins)
	{
		noOfLeadins++;
		leadin[i].minValue = leadin[i].maxValue = leadByte;
	}
}

int Mapping::PageNum(ZafUInt16 value)
{
	if (isUni)
		return ((value & 0xff00) >> 8);

	return (0);
}

void Mapping::Save(ZafFileSystem	*fileSystem)
{
	ZafFile *file;
	ZafIChar buff[32];
	int i;
	ZafUInt16 defaultChar = 0;

	// Store localToUnicode information.
	file = fileSystem->Open(localTo, ZAF_FILE_CREATE, strngID, numID);

	//
	int leadins = noOfLeadins;
	if (leadins < 0)
		leadins = 1;
	*file << noOfLeadins;
	for (i = 0; i < leadins; i++)
		*file << leadin[i].minValue << leadin[i].maxValue;
	*file << defaultChar;
	fileSystem->Close(file);

	for (i = 0; i < 256; i++)
		if (localToUnicode[i])
		{
			sprintf(buff, ZAF_ITEXT("%s.%x"), localTo, i);
			file = fileSystem->Open(buff, ZAF_FILE_CREATE, strngID, numID);
			localToUnicode[i]->WriteData(file);
			fileSystem->Close(file);
		}

	// Store unicodeToLocal information.
	file = fileSystem->Open(toLocal, ZAF_FILE_CREATE, strngID, numID);

	//
	leadins = 0;
	*file << leadins;
	*file << defaultChar;
	fileSystem->Close(file);

	for (i = 0; i < 256; i++)
		if (unicodeToLocal[i])
		{
			sprintf(buff, ZAF_ITEXT("%s.%x"), toLocal, i);
			file = fileSystem->Open(buff, ZAF_FILE_CREATE, strngID, numID);
			unicodeToLocal[i]->WriteData(file);
			fileSystem->Close(file);
		}
}

void Mapping::SetUnicode(bool isUni)
{
	if (isUni)
	{
		toLocal = toLocalU;
		localTo = localToU;
		numID = numIDU;
		strngID = strngIDU;
	}
	else
	{
		toLocal = toLocalI;
		localTo = localToI;
		numID = numIDI;
		strngID = strngIDI;
	}
}

void ParseFile(istream &file, Mapping &mapping, int localCol, int uniCol);
void ChangeToDirectory(ZafFileSystem *file, const ZafIChar *newName);
void Copy(ZafIChar *dest, char *src);

void Usage(char *name)
{
	cout<<"Usage "<<name<<" [-iso | -uni*] "<<endl<<"\t[<col local> <col uni/iso>] "
		<<endl<<"\t<in file name> <out file name> <destination> "
		<<endl<<"\t[ [<col local> <col uni/iso>] <overide file>]";
}

#define VALIDATE(x) \
	if (argc < x) 		\
	{				  		\
		Usage(argv[0]);\
		return (-1);	\
	}						\


int main(int argc, char **argv)
{
	int localCol = 1;
	int uniCol = 2;
	Mapping mapping;

	char **tArgv = argv;
	int tArgc = argc;

	VALIDATE(4);

	tArgv++;
	tArgc--;

	if (*tArgv[0] == '-')
	{
		if (!strnicmp(&tArgv[0][1], "iso", 3))
			mapping.SetUnicode(false);
		else if (strnicmp(&tArgv[0][1], "uni", 3))
		{
			cout<<"Invalid option: "<<tArgv[0]<<"\n"<<endl;
			Usage(argv[0]);
			return -1;
		}

		tArgv++;
		tArgc--;
	}
	VALIDATE(3);

	if (isdigit(*tArgv[0]))
	{
		if (!sscanf(tArgv[0], "%d", &localCol))
		{
			Usage(argv[0]);
			return -1;
		}
		tArgv++;
		tArgc -= 2;
		VALIDATE(3);

		if (!sscanf(tArgv[0], "%d", &uniCol))
		{
			Usage(argv[0]);
			return -1;
		}
		tArgv++;
//		tArgc--;
	}
	VALIDATE(3);

	ifstream inFile(tArgv[0]);

	if (inFile.bad())
	{
		cout<<"Unable to open file "<<tArgv[0]<<" for input"<<endl;
		return (-1);
	}

	ParseFile(inFile, mapping, localCol, uniCol);

	ZafI18nData::I18nAllocate();

	tArgv++;
	tArgc--;
	ZafIChar buff[32];
	Copy(buff, tArgv[0]);
	ZafStorage outFile(buff, ZAF_FILE_OPENCREATE | ZAF_FILE_READWRITE);

	if (outFile.Error())
	{
		cout<<"Unable to open file "<<tArgv[0]<<" for output"<<endl;
		ZafI18nData::I18nFree(true);
		return (-1);
	}

	tArgv++;
	tArgc--;

	Copy(buff, tArgv[0]);
	ChangeToDirectory(&outFile, buff);

	tArgv++;
	tArgc--;
	if (tArgc > 0) // Override file
	{
		localCol = 1;
		uniCol = 2;

		if (isdigit(*tArgv[0]))
		{
			if (!sscanf(tArgv[0], "%d", &localCol))
			{
				Usage(argv[0]);
				ZafI18nData::I18nFree(true);
				return -1;
			}
			tArgv++;
			tArgc -= 2;
			VALIDATE(3);

			if (!sscanf(tArgv[0], "%d", &uniCol))
			{
				Usage(argv[0]);
				ZafI18nData::I18nFree(true);
				return -1;
			}
			tArgv++;
		}
		inFile.close();
		inFile.open(tArgv[0], ios::in);

		if (inFile.bad())
		{
			cout<<"Unable to open file "<<tArgv[0]<<" for input"<<endl;
			ZafI18nData::I18nFree(true);
			return (-1);
		}

		ParseFile(inFile, mapping, localCol, uniCol);
	}

	mapping.Save(&outFile);
	outFile.Save();

	ZafI18nData::I18nFree(true);

	return (0);
}

ZafIChar *codesetName = ZAF_ITEXT("ZafCodeSetData");
ZafNumberID codesetNumID = 3121;

void ChangeToDirectory(ZafFileSystem *file, const ZafIChar *newName)
{
	file->MkDir(ZAF_ITEXT("~ZafCodeSetData"));
	file->ChDir(ZAF_ITEXT("~ZafCodeSetData"));
	file->MkDir(newName, codesetName, codesetNumID);
	file->ChDir(newName);
	ZafFile *temp = file->Open(ZAF_ITEXT("objectData"), ZAF_FILE_CREATE);
	file->Close(temp);
}

void ParseFile(istream &file, Mapping &mapping, int localCol, int uniCol)
{
	char buff[256];
	char *ptr;
	ZafUInt16 locVal;
	ZafUInt16 uniVal;
	ZafUInt16 value;
	int column;

	while (!file.eof() && !file.bad())
	{
		file.getline(buff, 256, '\n');
		ptr = buff;
		while (isspace(*ptr))
			ptr++;
		if (*ptr == '#' || !*ptr || *ptr == '\n')
			continue;
		column = 1;

		while (1)
		{
			while (isspace(*ptr))
				ptr++;

			if (*ptr == '0' && (ptr[1] == 'x' || ptr[1] == 'X'))
			{
				ptr += 2;
			
				sscanf(ptr, "%x", &value);
				if (localCol == column)
					locVal = value;
				else if (uniCol == column)
					uniVal = value;
			}
			else if (*ptr == '#' || !*ptr)
			{
				if (column == 2)
				{
					if (strnicmp(&ptr[1], "undefine", strlen("undefine")))
						mapping.AddLeadin(value);
				}
				else
					mapping.AddMapping(locVal, uniVal);
				break;
			}
			while (!isspace(*ptr) && *ptr)
				ptr++;

			column++;
		}
	}
}

void Copy(ZafIChar *dest, char *src)
{
	if (!src)
		return;

	while (*src)
		*dest++ = *src++;
	*dest = 0;
}

