﻿#define	WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <stdarg.h>
// #include "DebugOut.h"
#include "VirtuaNESres.h"
#define LUA_BUILD_AS_DLL
#undef  CREATDLL_EXPORTS
#include "LuaExt.h"
#include "ComLog_c.h"
#define  CREATDLL_EXPORTS
#include <map>
#include <string>
#include <vector>

lua_State* luaState = NULL;
static int LuaPanicFunction(lua_State *L) {
	if (L == luaState) {
		lua_close(luaState);
		luaState = NULL;
	}
	// printf("\n> Lua: error occurred in unprotected call.  This is very bad.\n");
	COMLOGF(RT_D_DEBUG, "lua printf(%s) ", "> Lua: error occurred in unprotected call.  This is very bad.");
	return 1;
}

inline void raise_error(lua_State *L, const char *errMsg=NULL) {
	luaL_where(L, 1);
	if (errMsg) {
		lua_pushstring(L, errMsg);
	} else {
		lua_insert(L, -2);
	}
	lua_concat(L, 2);
	lua_error(L);
}

static int cf_global_print(lua_State *L) {

	
	// Utf8_Read convert;
	const char *data;

	const int nargs = lua_gettop(L);

	lua_getglobal(L, "tostring");

	for (int i = 1; i <= nargs; ++i) {
		if (i > 1){
			// printf("\t");  //  host->Trace("\t");
			COMLOGF(RT_D_DEBUG, "lua printf(%s) ", "\n");
		}

		const char *argStr = lua_tostring(L, i);
		// int str_len=strlen(argStr);
		// data=new char[str_len+1];
		// memset(data, NULL, str_len+1);
		// strcpy(data, argStr);
		data = argStr;
		
		if (argStr) {
			// int len = convert.convert(data, str_len);

			// host->Trace(convert.getNewBuf(), len, 1);
			// convert.reset();
			// printf(argStr);
			COMLOGF(RT_D_DEBUG, "lua printf(%s) ", argStr);
		} else {
			lua_pushvalue(L, -1); // tostring
			lua_pushvalue(L, i);
			lua_call(L, 1, 1);
			argStr = lua_tostring(L, -1);
			if (argStr) {
				// host->Trace(argStr);
				// printf(argStr);
				COMLOGF(RT_D_DEBUG, "lua printf(%s) ", argStr);
			} else {
				raise_error(L, "tostring (called from print) returned a non-string");
			}
			lua_settop(L, nargs + 1);
		}
		//delete [] data;
	}

	// host->Trace("\n");
	// printf("\n");
	COMLOGF(RT_D_DEBUG, "lua printf(%s) ", "\n");
	return 0;
}

std::map<std::string,std::vector<ProgPad>>  all_tricks;

void SetKeyTime( lua_State *L, std::vector<ProgPad>  & pProg , std::string & nm)
{
	std::vector<unsigned>  keyTime;
	lua_pushnil(L); // push nil, so lua_next removes it from stack and puts (k, v) on stack
	// dumpStack(L);
	while (lua_next(L, -2) != 0) {
		// dumpStack(L);
		if ((lua_type(L, -2) == LUA_TSTRING) && (strcmp(lua_tostring(L, -2), "name")==0)) {
			nm=lua_tostring(L, -1);
			lua_pop(L, 1);
		}
		else if ( (lua_type(L, -1) == LUA_TSTRING) &&  (lua_type(L, -2) == LUA_TNUMBER)  ){
			nm=lua_tostring(L, -1);
			lua_pop(L, 1);
		}
		else if ( (lua_type(L, -1) == LUA_TNUMBER)  && (lua_type(L, -2) == LUA_TNUMBER) ){
			keyTime.push_back( (unsigned)luaL_checkinteger(L, -1));
			lua_pop(L, 1);
		}
		else if (( lua_type(L, -1) == LUA_TTABLE) ){
			SetKeyTime(L, pProg,  nm );
			lua_pop(L, 1);
		}
	}
	if ( keyTime.size() == 2 ){
		ProgPad   kt;
		kt.pos=keyTime[0];
		kt.dwMilliseconds=(DWORD)keyTime[1];
		pProg.push_back(kt);
	}
	return ;
}

static int luaB_InitTricks(lua_State *L)
{
	int i,j, n,m;
	n = luaL_len(L, 1);
	all_tricks.clear();
	for (i = 1; i <= n; i++){
		lua_geti(L, 1, i);
		int type = lua_type(L, -1);
		if (type == LUA_TTABLE){
			// const char *nm;
			std::string  nm;
			std::vector<ProgPad>  vProg;
			SetKeyTime(L, vProg, nm);
			if(nm.empty()){
				char tmp[256];
				_snprintf(tmp, sizeof(tmp), "%d", i);
				nm=tmp;
			}
			if ( vProg.size() > 0 ){
				all_tricks.insert ( std::pair<std::string, std::vector<ProgPad>>( nm ,vProg) );
			}
		}
		lua_pop(L, 1);
	}
	return 0;
}

/*
enum { SURROGATE_LEAD_FIRST = 0xD800 };
enum { SURROGATE_TRAIL_FIRST = 0xDC00 };
enum { SURROGATE_TRAIL_LAST = 0xDFFF };
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;
}
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);
}
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;
}
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);
}
*/
static int do_utf8_to_ans(lua_State* L)
{
	size_t len;
	char *ansCode;
	const char*  utf8Code = luaL_checkstring(L, 1);
	len = AnsicFromutf8(utf8Code, -1, NULL);
	ansCode = (char *)malloc(len);
	len = AnsicFromutf8(utf8Code, -1, ansCode);
	lua_pushstring(L, ansCode);
	free(ansCode);
	return 1;
}
static int do_ans_to_utf8(lua_State* L)
{
	size_t len;
	char *utf8Code;
	const char*  ansCode = luaL_checkstring(L, 1);
	len = Utf8FromAnsic(ansCode, -1, NULL);
	utf8Code = (char *)malloc(len);
	len = Utf8FromAnsic(ansCode, -1, utf8Code);
	lua_pushstring(L, utf8Code);
	free(utf8Code);
	return 1;
}
// #undef  COMLOGF
// #define   COMLOGF( st, fmt,... )     write_format( st, __FILE__,  __LINE__, __FUNCTION__,  fmt, __VA_ARGS__ )
// extern lua_State* luaState;
// void (*write_format) (int _nStatus, const char * _sFile, int _nLine, const char * _sFunc, const char * sFormat, ...);
#ifdef __cplusplus
extern "C" {
#endif

BOOL WINAPI DllMain( HINSTANCE hInstanceDLL, DWORD fdwReason, LPVOID lpvReserved )
{
    // Perform actions based on the reason for calling.
	// COMLOGF(RT_D_DEBUG, "DllMain    fdwReason(%d) ",fdwReason);
	// memset(buf, 0x00, sizeof(buf));
    switch( fdwReason ) 
    { 
        case DLL_PROCESS_ATTACH:
         // Initialize once for each new process.
         // Return FALSE to fail DLL load.
			COMLOGF(RT_D_DEBUG, "DLL_PROCESS_ATTACH(%d) ",fdwReason);
			// app = GetModuleHandle(NULL);
			// write_format = (void(*)(int _nStatus, const char * _sFile, int _nLine, const char * _sFunc, const char * sFormat, ...))GetProcAddress(app, "CLog_Format" );
			if ( luaState == NULL ) {
				luaState = luaL_newstate();
				lua_atpanic(luaState, LuaPanicFunction);
				//Lua の標準関数を使用できる状態にする
				luaL_openlibs(luaState);

				lua_register(luaState, "print", cf_global_print);
				
				lua_getglobal(luaState, "virtuanes");
				if (lua_isnil(luaState, -1)){
					lua_pop(luaState, 1);
					lua_newtable(luaState);
				}
				lua_pushcfunction(luaState, do_utf8_to_ans);
				lua_setfield(luaState, -2, "utf8_to_ans");
				lua_pushcfunction(luaState, do_ans_to_utf8);
				lua_setfield(luaState, -2, "ans_to_utf8");
				lua_pushcfunction(luaState, luaB_InitTricks);
				lua_setfield(luaState, -2, "init_tricks");

				lua_setglobal(luaState, "virtuanes"); // { tbl };  ⇒  g["spawner"]=tbl
			}
			// strcpy( buf, "DLL_PROCESS_ATTACH" );
            break;

        case DLL_THREAD_ATTACH:
         // Do thread-specific initialization.
			COMLOGF(RT_D_DEBUG, "DLL_THREAD_ATTACH(%d) ",fdwReason);
			// app = GetModuleHandle(NULL);
			// write_format = (void(*)(int _nStatus, const char * _sFile, int _nLine, const char * _sFunc, const char * sFormat, ...))GetProcAddress(app, "CLog_Format" );

			// strcpy( buf, "DLL_PROCESS_ATTACH" );
            break;

        case DLL_THREAD_DETACH:
         // Do thread-specific cleanup.
			COMLOGF(RT_D_DEBUG, "DLL_THREAD_DETACH(%d) ",fdwReason);
            break;

        case DLL_PROCESS_DETACH:
         // Perform any necessary cleanup.
			COMLOGF(RT_D_DEBUG, "DLL_PROCESS_DETACH(%d) ",fdwReason);
			if ( luaState  ) {
				lua_close(luaState);
				luaState = NULL;
			}
            break;
    }
    return TRUE;  // Successful DLL_PROCESS_ATTACH.
}
__declspec(dllexport) INT	VirtuanesInit( const char *lua_file)
{
	lua_State *L=luaState;
	// char *lua_file="../../lua_src/sample_test.lua";
	// const char *lua_file=lua_tostring(L, -1);
	if (luaL_loadfile(L ,lua_file)|| lua_pcall(L, 0, 0, 0)){
		raise_error(L, "sample.lua を開けませんでした");
		char tmp[256];
		_snprintf(tmp, sizeof(tmp), "error : %s\n", lua_tostring(L, -1));
		raise_error(L, tmp);
		// lua_close(luaState);
		return 1;
	}
	lua_settop(L, 0); 
	return 0;
}
__declspec(dllexport) int SetTricks( char *name,ProgPad *prog)
{
	
	std::string  key=name;
	std::vector<ProgPad> &tricks =all_tricks[key];
	if(!prog){
		return tricks.size();
	}
	std::vector<ProgPad>::iterator it;
	for (it = tricks.begin(); it != tricks.end(); it++)
   {
		memcpy(prog, &(*it), sizeof(ProgPad));
		prog++;
   }
	return tricks.size();
}
__declspec(dllexport) void SetTrickName( HWND hCombo,const char *name )
{

	SendMessage(hCombo, CB_RESETCONTENT, 0, 0);
	std::map<std::string,std::vector<ProgPad>>::iterator it;
	int id,i;
	id=0;i=0;
	for (it = all_tricks.begin(); it != all_tricks.end(); it++)
	{
		SendMessage(hCombo, CB_ADDSTRING, 0, (LPARAM)it->first.c_str());
		COMLOGF(RT_D_DEBUG, "hCombo=(%d),_name=[%s]",hCombo,it->first.c_str());
		if ( name && (strcmp(name,it->first.c_str())==0)){
			id=i;
		}
		i++;
	}
	COMLOGF(RT_D_DEBUG, "id=(%d)",id);
   SendMessage(hCombo, CB_SETCURSEL, id, 0);
	return;
}
INT	GetVlpVersion( void )
{

	COMLOGF(RT_D_DEBUG, "GetVlpVersion( void ) ");
	return	VIRTUANES_PLUGIN_VERSION;
}

void	GetVlpLanguage( LPSTR lpLanguege )
{
	COMLOGF(RT_D_DEBUG, "GetVlpLanguage( LPSTR lpLanguege ) ");
	strcpy( lpLanguege, "中国(中文)new" );
}

LCID	GetVlpLocaleID( void )
{
	COMLOGF(RT_D_DEBUG, "GetVlpLocaleID( void ) ");
	return	0x804;
}
#ifdef __cplusplus
}
#endif
