#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <windows.h>

#include "lua.h"
#include "lucode.h"

enum { SURROGATE_LEAD_FIRST = 0xD800 };
enum { SURROGATE_TRAIL_FIRST = 0xDC00 };
enum { SURROGATE_TRAIL_LAST = 0xDFFF };

static unsigned int UTF8Length(const wchar_t *uptr, size_t tlen) {
	unsigned int len = 0;
	size_t i;
	unsigned int uch;
	for (i = 0; i < tlen && uptr[i];) {
		uch = uptr[i];
		if (uch < 0x80) {
			len++;
		} else if (uch < 0x800) {
			len += 2;
		} else if ((uch >= SURROGATE_LEAD_FIRST) &&
			(uch <= SURROGATE_TRAIL_LAST)) {
			len += 4;
			i++;
		} else {
			len += 3;
		}
		i++;
	}
	return len;
}
static void UTF8FromUTF16(const wchar_t *uptr, size_t tlen, char *putf, size_t len) {
	int k = 0;
	size_t i;
	unsigned int uch;
	unsigned int xch;
	for ( i = 0; i < tlen && uptr[i];) {
		uch = uptr[i];
		if (uch < 0x80) {
			putf[k++] = (char)(uch);
		} else if (uch < 0x800) {
			putf[k++] = (char)(0xC0 | (uch >> 6));
			putf[k++] = (char)(0x80 | (uch & 0x3f));
		} else if ((uch >= SURROGATE_LEAD_FIRST) &&
			(uch <= SURROGATE_TRAIL_LAST)) {
			// Half a surrogate pair
			i++;
			xch = 0x10000 + ((uch & 0x3ff) << 10) + (uptr[i] & 0x3ff);
			putf[k++] = (char)(0xF0 | (xch >> 18));
			putf[k++] = (char)(0x80 | ((xch >> 12) & 0x3f));
			putf[k++] = (char)(0x80 | ((xch >> 6) & 0x3f));
			putf[k++] = (char)(0x80 | (xch & 0x3f));
		} else {
			putf[k++] = (char)(0xE0 | (uch >> 12));
			putf[k++] = (char)(0x80 | ((uch >> 6) & 0x3f));
			putf[k++] = (char)(0x80 | (uch & 0x3f));
		}
		i++;
	}
	putf[len] = '\0';
}
size_t UTF16Length(const char *s, unsigned int len) {
	size_t ulen = 0;
	size_t charLen;
	size_t i;
	unsigned char ch;
	for (i=0; i<len;) {
		ch = (unsigned char)(s[i]);
		if (ch < 0x80) {
			charLen = 1;
		} else if (ch < 0x80 + 0x40 + 0x20) {
			charLen = 2;
		} else if (ch < 0x80 + 0x40 + 0x20 + 0x10) {
			charLen = 3;
		} else {
			charLen = 4;
			ulen++;
		}
		i += charLen;
		ulen++;
	}
	return ulen;
}
size_t UTF16FromUTF8(const char *s, size_t len, wchar_t  *tbuf, size_t tlen) {
	size_t ui=0;
	const unsigned char *us = (const unsigned char *)s;
	size_t i=0;
	unsigned char ch;
	int val;
	
	while ((i<len) && (ui<tlen)) {
		ch = us[i++];
		if (ch < 0x80) {
			tbuf[ui] = ch;
		} else if (ch < 0x80 + 0x40 + 0x20) {
			tbuf[ui] = (wchar_t)((ch & 0x1F) << 6);
			ch = us[i++];
			tbuf[ui] = (wchar_t)(tbuf[ui] + (ch & 0x7F));
		} else if (ch < 0x80 + 0x40 + 0x20 + 0x10) {
			tbuf[ui] = (wchar_t)((ch & 0xF) << 12);
			ch = us[i++];
			tbuf[ui] = (wchar_t)(tbuf[ui] + ((ch & 0x7F) << 6));
			ch = us[i++];
			tbuf[ui] = (wchar_t)(tbuf[ui] + (ch & 0x7F));
		} else {
			// Outside the BMP so need two surrogates
			val = (ch & 0x7) << 18;
			ch = us[i++];
			val += (ch & 0x3F) << 12;
			ch = us[i++];
			val += (ch & 0x3F) << 6;
			ch = us[i++];
			val += (ch & 0x3F);
			tbuf[ui] = (wchar_t)(((val - 0x10000) >> 10) + SURROGATE_LEAD_FIRST);
			ui++;
			tbuf[ui] = (wchar_t)((val & 0x3ff) + SURROGATE_TRAIL_FIRST);
		}
		ui++;
	}
	return ui;
}
LUA_API size_t AnsicFromutf8 (const char *s, int len, char *to){
	size_t needLength;
	int sLen = (len == -1) ? strlen(s) : len;
	int wideLen = UTF16Length(s, sLen);
	wchar_t *vgc=(wchar_t *)malloc(sizeof(wchar_t)*(wideLen+1));
	size_t outLen = UTF16FromUTF8(s, sLen, vgc, wideLen);
	vgc[outLen] = 0;
	needLength =(size_t) WideCharToMultiByte(CP_ACP, 0, (LPCWSTR)vgc, -1, NULL, 0, NULL, NULL);

	if (to){
		WideCharToMultiByte(CP_ACP, 0, (LPCWSTR)vgc, -1, to, (int)needLength, NULL, NULL);
	}
	free(vgc);
	return (needLength);
}
LUA_API size_t Utf8FromAnsic (const char *s, int len, char *to){
	size_t narrowLen;
	size_t wideLen;
	wchar_t *vgc;
	size_t sLen = (len == -1) ? strlen(s) : len;

	char *pszMulti =malloc(sLen+1);
	memcpy( pszMulti, s,sLen);
	pszMulti[sLen]=0;

	wideLen=MultiByteToWideChar(CP_ACP, 0, pszMulti, -1, NULL, 0);
	vgc=(wchar_t *)malloc(sizeof(wchar_t)*(wideLen));
	MultiByteToWideChar(CP_ACP, 0, pszMulti, -1, (LPWSTR)vgc, wideLen);
	free(pszMulti);

	sLen = wideLen-1;
	narrowLen = UTF8Length(vgc, sLen);
	if (to){
		UTF8FromUTF16(vgc, sLen, to, narrowLen);
	}
	free(vgc);
	return (narrowLen+1);
}
