#ifdef __MINGW32__
#define MINGW
#endif

#define LUA_LIB
#define LUA_BUILD_AS_DLL

#ifdef MINGW
#define _WIN32_WINNT	_WIN32_WINNT_WINXP
#endif

#include <stdio.h>
#include <string.h>

#include "lua.h"
#include "lauxlib.h"

#ifdef MINGW
#include <windows.h>
#include "msapi_utf8.h"
#endif

#define LIBNAME       "mmext"

#define true 1
#define false 0

static char *getStrArg(lua_State *L, int index)
{
	if(lua_isnil(L, index)){
		return NULL;
	}
	return (char *)strdup(lua_tostring (L, index));
}

static HWND wclient;

static BOOL CALLBACK EnumWindowsProc(HWND hwnd,  LPARAM lParam)
{
	DWORD pid = (DWORD)lParam;
	DWORD tPid;
	
	// Check the pid of the window
	GetWindowThreadProcessId(hwnd, &tPid);
	if(tPid == pid){
		LPTSTR lpClassName = (LPTSTR)calloc(256, sizeof(char));
		
		// Check class name
		GetClassNameA(hwnd, lpClassName, 256);
		
		// We found it?
		if(!strcmp(lpClassName, "Radiant Arcana")){
			wclient = hwnd;
			return false;
		}
	}
	return true;
}

int mmext_client(lua_State *L)
{
	// Get Lua argument (check for nil => NULL)
	char *cmd  = getStrArg(L, 1);
	char *args = getStrArg(L, 2);
	char *dir  = getStrArg(L, 3);
		
	// Setup the executable
	SHELLEXECUTEINFO ShExecInfo = {0};
	ShExecInfo.cbSize = sizeof(ShExecInfo);
	ShExecInfo.fMask = SEE_MASK_NOCLOSEPROCESS | SEE_MASK_NO_CONSOLE | SEE_MASK_FLAG_DDEWAIT ;
	ShExecInfo.hwnd = NULL;
	ShExecInfo.lpVerb = NULL;
	ShExecInfo.lpFile = cmd;        
	ShExecInfo.lpParameters = args;   
	ShExecInfo.lpDirectory = dir;
	ShExecInfo.nShow = SW_SHOWNORMAL;
	ShExecInfo.hInstApp = NULL; 
	
	// Start up client
	if(!ShellExecuteExU(&ShExecInfo)){
		lua_pushnumber(L, (long)0);
		return 1;
	}
	
	// Wait for client to idle, not really a good one but
	// Window is initialised when this call returns
	DWORD ret = WaitForInputIdle(ShExecInfo.hProcess, 10000);
	if(ret != 0){
		printf("Wait error: %d\n", (int)ret);
		lua_pushnumber(L, (long)0);
		return 1;
	}
	
	// Extract PID
	DWORD pid = GetProcessId(ShExecInfo.hProcess);
	CloseHandle(ShExecInfo.hProcess);
	
	// Find the window (assuming one)
	wclient = (HWND)0;
	EnumDesktopWindows(NULL, EnumWindowsProc, (LPARAM)pid);
	
	// Did we find window?
	if(wclient == (HWND)0){
		fprintf(stderr, "We didn't find the HWND for PID %d\n", (int)pid);
		lua_pushnumber(L, (long)pid);
		return 1;
	}
	
	// Return pid/window
	lua_pushnumber(L, (long)pid);
	lua_pushnumber(L, (long)wclient);
	
	return 2;
}

int mmext_killclient(lua_State *L)
{
	DWORD dwDesiredAccess = PROCESS_TERMINATE;
	int pid = lua_tonumber(L, 1);
	
	HANDLE hProcess = OpenProcess(dwDesiredAccess, false, pid);
	if(hProcess){
		TerminateProcess(hProcess, 0);
	}
	return 0;
}

int mmext_MoveWindow (lua_State *L)
{
    long hwnd    = (long)lua_tonumber (L, 1);
	long x       = (long)lua_tonumber (L, 2);
	long y       = (long)lua_tonumber (L, 3);
    long ret;
	RECT wrect;
	
	ret = GetWindowRect ( (HWND)hwnd, &wrect );
	if ( ret ) {
		ret = (long)MoveWindow ( (HWND)hwnd, x, y, wrect.right-wrect.left, wrect.bottom-wrect.top, 1 );
	}

    ret ? lua_pushnumber ( L, (long)ret ) : lua_pushnil (L);
    return 1;
}

int mmext_ResizeWindow (lua_State *L)
{
    long hwnd    = (long)lua_tonumber (L, 1);
	long width   = (long)lua_tonumber (L, 2);
	long height  = (long)lua_tonumber (L, 3);
    long ret;
	RECT wrect;
	
	ret = GetWindowRect ( (HWND)hwnd, &wrect );
	if ( ret ) {
		ret = (long)MoveWindow ( (HWND)hwnd, wrect.left, wrect.top, width, height, 1 );
	}

    ret ? lua_pushnumber ( L, (long)ret ) : lua_pushnil (L);
    return 1;
}

static const luaL_Reg extlib[] = {
	{"MoveWindow",   	mmext_MoveWindow},
	{"ResizeWindow", 	mmext_ResizeWindow},
	{"Client", 			mmext_client},
	{"KillClient",		mmext_killclient},
	{NULL, NULL}
};

/*
** Register to LUA
*/
LUALIB_API int luaopen_mmext (lua_State *L) {
#if LUA_VERSION_NUM > 501
  luaL_newlibtable(L, extlib);
  luaL_setfuncs(L, extlib, 0);
#else
  luaL_register(L, LIBNAME, extlib);
#endif
  return 1;
}