#include "EncodeCp936.h"

#define ConvError_Skip	0
#define ConvError_Exception	1
#define ConvError_Replace	2
#define ConvError_FillEmpty	3

const unsigned int ConvErrorProcMode = ConvError_Replace;

const static unsigned int cp936unodered[255] = {
	0xa2ab,0xa2ac,0xa2ad,0xa2ae,0xa2af,0xa2b0,0xa2e3,0xa2e4,0xa2ef,0xa2f0,0xa2fd,0xa2fe,0xa4f4,0xa4f5,0xa4f6,0xa4f7,
	0xa4f8,0xa4f9,0xa4fa,0xa4fb,0xa4fc,0xa4fd,0xa4fe,0xa5f7,0xa5f8,0xa5f9,0xa5fa,0xa5fb,0xa5fc,0xa5fd,0xa5fe,0xa6b9,
	0xa6ba,0xa6bb,0xa6bc,0xa6bd,0xa6be,0xa6bf,0xa6c0,0xa6d9,0xa6da,0xa6db,0xa6dc,0xa6dd,0xa6de,0xa6df,0xa6ec,0xa6ed,
	0xa6f3,0xa6f6,0xa6f7,0xa6f8,0xa6f9,0xa6fa,0xa6fb,0xa6fc,0xa6fd,0xa6fe,0xa7c2,0xa7c3,0xa7c4,0xa7c5,0xa7c6,0xa7c7,
	0xa7c8,0xa7c9,0xa7ca,0xa7cb,0xa7cc,0xa7cd,0xa7ce,0xa7cf,0xa7d0,0xa7f2,0xa7f3,0xa7f4,0xa7f5,0xa7f6,0xa7f7,0xa7f8,
	0xa7f9,0xa7fa,0xa7fb,0xa7fc,0xa7fd,0xa7fe,0xa896,0xa897,0xa898,0xa899,0xa89a,0xa89b,0xa89c,0xa89d,0xa89e,0xa89f,
	0xa8a0,0xa8bc,0xa8bf,0xa8c1,0xa8c2,0xa8c3,0xa8c4,0xa8ea,0xa8eb,0xa8ec,0xa8ed,0xa8ee,0xa8ef,0xa8f0,0xa8f1,0xa8f2,
	0xa8f3,0xa8f4,0xa8f5,0xa8f6,0xa8f7,0xa8f8,0xa8f9,0xa8fa,0xa8fb,0xa8fc,0xa8fd,0xa8fe,0xa958,0xa95b,0xa95d,0xa95e,
	0xa95f,0xa989,0xa98a,0xa98b,0xa98c,0xa98d,0xa98e,0xa98f,0xa990,0xa991,0xa992,0xa993,0xa994,0xa995,0xa997,0xa998,
	0xa999,0xa99a,0xa99b,0xa99c,0xa99d,0xa99e,0xa99f,0xa9a0,0xa9a1,0xa9a2,0xa9a3,0xa9f0,0xa9f1,0xa9f2,0xa9f3,0xa9f4,
	0xa9f5,0xa9f6,0xa9f7,0xa9f8,0xa9f9,0xa9fa,0xa9fb,0xa9fc,0xa9fd,0xa9fe,0xd7fa,0xd7fb,0xd7fc,0xd7fd,0xd7fe,0xfe50,
	0xfe51,0xfe52,0xfe53,0xfe54,0xfe55,0xfe56,0xfe57,0xfe58,0xfe59,0xfe5a,0xfe5b,0xfe5c,0xfe5d,0xfe5e,0xfe5f,0xfe60,
	0xfe61,0xfe62,0xfe63,0xfe64,0xfe65,0xfe66,0xfe67,0xfe68,0xfe69,0xfe6a,0xfe6b,0xfe6c,0xfe6d,0xfe6e,0xfe6f,0xfe70,
	0xfe71,0xfe72,0xfe73,0xfe74,0xfe75,0xfe76,0xfe77,0xfe78,0xfe79,0xfe7a,0xfe7b,0xfe7c,0xfe7d,0xfe7e,0xfe80,0xfe81,
	0xfe82,0xfe83,0xfe84,0xfe85,0xfe86,0xfe87,0xfe88,0xfe89,0xfe8a,0xfe8b,0xfe8c,0xfe8d,0xfe8e,0xfe8f,0xfe90,0xfe91,
	0xfe92,0xfe93,0xfe94,0xfe95,0xfe96,0xfe97,0xfe98,0xfe99,0xfe9a,0xfe9b,0xfe9c,0xfe9d,0xfe9e,0xfe9f,0xfea0};	
const static unsigned int  cp936unoderedstart = 0xe766;

static int GetCharLen(char *c)
{
	int i;
	for(i=0; i<1024; i++){
		if(*c++ == '\0'){
			return i;
		}
	}
	return 0;
}

static int GetMaxVal(const unsigned short *aValArray)
{
	int i, tResult;
	const unsigned short *tPtr = aValArray;
	tResult = 0;
	for(i=0; i<22047; i++){
		if(*tPtr >tResult)tResult = *tPtr;
		tPtr++;
	}
	return tResult;
}

static int UnicodeToUTF8Inline(unsigned int CodePoint, char *Buf)
{
	int tResult;
	if(CodePoint < 0x80){
		tResult =1;
		Buf[0] = (char)((unsigned char)(CodePoint));
      }else
	if(CodePoint < 0x800){
		tResult =2;
		Buf[0] = (char)((unsigned char)(0xc0 | (CodePoint >> 6)));
		Buf[1] = (char)((unsigned char)(0x80 | (CodePoint & 0x3f)));
      }else
	if(CodePoint < 0x10000){
		tResult =3;
		Buf[0] = (char)((unsigned char)(0xe0 | (CodePoint >> 12)));
		Buf[1] = (char)((unsigned char)((CodePoint >> 6) & 0x3f) | 0x80);
		Buf[2] = (char)((unsigned char)(CodePoint & 0x3f) | 0x80);
      }else
	if(CodePoint < 0x110000){
		tResult =4;
		Buf[0] = (char)((unsigned char)(0xf0 | (CodePoint >> 18)));
		Buf[1] = (char)((unsigned char)((CodePoint >> 12) & 0x3f) | 0x80);
		Buf[2] = (char)((unsigned char)((CodePoint >> 6) & 0x3f) | 0x80);
		Buf[3] = (char)((unsigned char)(CodePoint & 0x3f) | 0x80);
      }else{
		tResult =0;
	}
	return tResult;
}

static unsigned short SearchTable(const unsigned short *CodePageArr, unsigned int id)
{
	int idMid;
	int idLow, idHigh;
	unsigned short tResult;

	idLow = 0;
	//idHigh = GetHigh();
	idHigh = 22047;
	while (idLow <= idHigh) {
		if (idLow == idHigh){
			if( CodePageArr[idLow] == id){
				tResult = idLow;
			}else{
				tResult = 0;
			}
			return tResult;
		}
		idMid = (idLow + idHigh) / 2;
		if (CodePageArr[idMid] == id) {
			tResult = idMid;
			return tResult;
		}
		if (CodePageArr[idMid] > id )
			idHigh = idMid - 1;
		if (CodePageArr[idMid] < id )
			idLow = idMid + 1;
	}
	tResult = 0;
	return tResult;
}


	
static int DBWSToUTF8(char *s, const unsigned short *ArrayUni, const unsigned short *ArrayCP, int CodeP, char *Rst)
{
	int len, l,i;
	char *Src, *Dest;
	char c;;
	unsigned short code,code1;
	unsigned char HByte,LByte;
	unsigned int LimitedLen;
	int utf8Bytes;
	int ConvTimes;
	
	ConvTimes = 0;

	if(*s == '\0' ){
		*Rst++ = '\0';
		*Rst = '\0';
		return -1;
	}
	len = GetCharLen(s);
	//SetLength(Result, len * 4);// Asia UTF-8 is at most 4 bytes
	Src = s;
	Dest = Rst;
	while(1){
		c = *Src;
		HByte = (unsigned char)(*Src);
		Src++;

		if(c < 128){
			if( (c == '\0') && ((Src - s) >= len) )break;
			*Dest = c;
			Dest++;
		}else{
			code = ((unsigned short)c) << 8;
			c = *Src;
			LByte = (unsigned char)(*Src);
			if( (c== '\0') && ((Src- s) >=len) ) break;
			code = code + (unsigned char)(c);
			code1 = code;
			Src++;

			code = ArrayUni[SearchTable(ArrayCP, code)];
			if (code == 0){
				if(CodeP == 936){
					if(  (HByte >= 0xAA) && (HByte <= 0xAF) && (LByte >= 0xA1) && (LByte <= 0xFE)  ){
						code = 0xe000 + (94 * (HByte-0xaa)) + (LByte-0xa1);
					}else
					if( (HByte >= 0xF8) && (HByte <= 0xFE) && (LByte >= 0xA1) && (LByte <= 0xFE) ){
						code = 0xe234 + (94 * (HByte-0xf8)) + (LByte-0xa1);
					}else
					if( (HByte >= 0xA1) && (HByte <= 0xA7) && (LByte >= 0x40) && (LByte <= 0xA0) ){
						code = 0xe4C6 + (97 * (HByte-0xA1)) + (LByte-0x40);
					}else{
						LimitedLen = sizeof(cp936unodered) / 4;
						for( i=0; i< LimitedLen; i++){
							if(code1 == cp936unodered[i]){
								code = cp936unoderedstart +i;
								break;
							}
						}
					}
				}else{
					code = 0;
				}
			}
			if (code>0){
				l =UnicodeToUTF8Inline(code,Dest);
				Dest += l;
			}else{
				switch(ConvErrorProcMode){
					case ConvError_Skip:
						break;
					case ConvError_Exception:
						*Dest++ = '\0';
						*Dest = '\0';
						return -1;
						break;
					case ConvError_Replace:
						*Dest = '?';
						Dest++;
						break;
					case ConvError_FillEmpty:
						*Dest++ = ' ';
						break;
				}
			}
		}
		ConvTimes++;
		if(ConvTimes > 1023)break;
	}
	utf8Bytes = (unsigned int)(Dest) - (unsigned int)(Rst);
	*Dest++ = '\0';
	*Dest = '\0';
	//SetLength(Result, {%H-}PtrUInt(Dest) - PtrUInt(Result));
	//SetCodePage(RawByteString(Result), CP_UTF8, False);
	return utf8Bytes;
}

//return UTF8 Bytes Length
int CP936ToUTF8(char *s,  char *d)
{
	return DBWSToUTF8(s, Uni936C, CP936CC, 936, d);
}

int UnicodeToCP936(unsigned int aUnicode)
{
	if(aUnicode < 128)
		return aUnicode;
	else
		return CP936CU[SearchTable(Uni936U, aUnicode)];
}





