#include <cstdio>
#include <cstdlib>
#include <string>
#include <fstream>
#include <assert.h>

using namespace std;

int iLineNum = 0;

typedef wchar_t UnicodeChar; // Unicode type

class CCharCodePoint // a code point. Translates into unicode and conatins a list of possible char conmbinations
{
	CCharCodePoint * FollowingChar[256]; // list of all chars which might follow. NULL if one char might not follow.
	char cCodePoint; // code point value
	UnicodeChar uUnicodeChar; // translated unicode value
public:
	CCharCodePoint(char c);
	~CCharCodePoint();
	char GetCodePoint() const {return cCodePoint;}
	CCharCodePoint & SetUnicodeChar(UnicodeChar u) {uUnicodeChar = u; return *this;}
	UnicodeChar GetUnicodeChar() const {return uUnicodeChar;}
	CCharCodePoint * SeekFollowingChar(char c) const;
	CCharCodePoint * CreateFollowingChar(char c);
	CCharCodePoint * SeekFollowingUnicode(UnicodeChar u) const;
	bool ConvertChar(ifstream & fin, ofstream & fout);
};


CCharCodePoint::CCharCodePoint(char c)
{
	for (unsigned int i = 0; i < sizeof(FollowingChar)/sizeof(FollowingChar[0]); i++) 
		FollowingChar[i] = NULL;
	cCodePoint = c;
	uUnicodeChar = 0;
}

CCharCodePoint::~CCharCodePoint()
{
	for (unsigned int i = 0; i < sizeof(FollowingChar)/sizeof(FollowingChar[0]); i++) 
		if (FollowingChar[i])
			delete FollowingChar[i];
}

CCharCodePoint * CCharCodePoint::SeekFollowingChar(char c) const
{
	if (!FollowingChar[(unsigned char)c])
		return NULL;
	assert (FollowingChar[(unsigned char)c]->GetCodePoint() == c);
	return FollowingChar[(unsigned char)c];
}

CCharCodePoint * CCharCodePoint::CreateFollowingChar(char c)
{
	CCharCodePoint * CodePoint = SeekFollowingChar(c);
	if (CodePoint)
		return CodePoint;
	CodePoint = FollowingChar[(unsigned char)c] = new CCharCodePoint(c);
	return CodePoint;
}

CCharCodePoint * CCharCodePoint::SeekFollowingUnicode(UnicodeChar u) const
{
	for (unsigned int i = 0; i < sizeof(FollowingChar)/sizeof(FollowingChar[0]); i++)
		if (FollowingChar[i] && FollowingChar[i]->GetUnicodeChar() == u)
			return FollowingChar[i];
	return NULL;
}

bool CCharCodePoint::ConvertChar(ifstream & fin, ofstream & fout)
{
	char c = fin.get();
	CCharCodePoint * cp = SeekFollowingChar(c);
	if (!cp)
	{
		fin.putback(c);
		return false;
	}
	if (!cp->ConvertChar(fin, fout))
	{
		UnicodeChar u = cp->GetUnicodeChar();
		if (u)
		{
			fout.write((const char *)&u, 2);
			return true;
		}
		fin.putback(c);
		return false;
	}
	return true;
}


void readln(FILE * f, char * s)
{
	if (!fscanf(f, "%[^\n]", s))
		*s = 0;
	fgetc(f);
	iLineNum++;
}

void readlnIgnoringComments(FILE * f, char * s, char ** Comment = NULL)
{
	do
	{
		readln(f, s);
	}
	while (!feof(f) && strlen(s) && (*s == '#'));
	if (feof(f))
	{
		*s = 0;
		return;
	}
	char * c = strchr(s, '#');
	if (c)
	{
		if (Comment)
			*Comment = c+1;
		*c = 0;
	}
}


long splitstr(char *& sBuffer, char & cTerminator)
{
	cTerminator = 0;
	char * endptr = NULL;
	long lRet = strtol(sBuffer, & endptr, 16);
	if (endptr == sBuffer)
		return 0;
	cTerminator = *endptr;
	sBuffer = cTerminator?endptr+1:NULL;
	return lRet;
}


int main(int argc, char *argv[ ])
{
	if (argc != 3)
	{
		printf("Usage:\n\nAns2UniConv source target\nsource: Ansel file to be read\n"
			"target: unicode file to be created\n\n");
		return -1;
	}
	FILE * fConversionData = fopen("ans2uni.con", "rb");
	if (!fConversionData)
	{
		printf("\nAns2UniConv: can not open file ans2uni.con for reading.\n");
		return -2;
	}
	ifstream fin(argv[1], ios::in | ios::binary);
	if (!fin.is_open())
	{
		printf("\nAns2UniConv: can not open file %s for reading.\n", argv[1]);
		return -2;
	}
	ofstream fout(argv[2], ios::out | ios::binary);
	if (!fout.is_open())
	{
		printf("\nAns2UniConv: can not open file %s for writing.\n", argv[2]);
		return -2;
	}


	CCharCodePoint CodePointRoot(0);
	char sBuffer[2048];

	// read the ansel to unicode table
	while (!feof(fConversionData))
	{
		readlnIgnoringComments(fConversionData, sBuffer);
		if (*sBuffer)
		{
			char * str = sBuffer, cTerminator = '+';
			CCharCodePoint * cp = & CodePointRoot;
			while (cTerminator == '+')
			{
				long Ansel = splitstr(str, cTerminator);
				cp = cp->CreateFollowingChar((char)Ansel);
			}
			long Unicode = splitstr(str, cTerminator);
			cp->SetUnicodeChar((UnicodeChar)Unicode);
		}
	}
	for (int i = 0; i < 0x20; i++)
	{
		CCharCodePoint * cp = CodePointRoot.CreateFollowingChar((char)i);
		cp->SetUnicodeChar((UnicodeChar)i);
	}

	while (!fin.eof())
	{
		if (!CodePointRoot.ConvertChar(fin, fout))
		{
			char c = fin.get();
			printf("Can not convert character %02x\n", (unsigned char)c);
		}
	}

	fout.close();
	fin.close();
	fclose(fConversionData);
	return 0;
}
