/*
 * PtokaX MultiLib - Compatibility layer allowing same lib to work on PXLua based on Lua 5.1, 5.2 and 5.3.

 * Copyright (C) 2012-2013  Petr Kozelka, PPK at PtokaX dot org

 * This code is licensed under a Creative Commons Attribution-NonCommercial 4.0 International License.

 * See http://creativecommons.org/licenses/by-nc/4.0/ for license details.
 */

//--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
#include <windows.h>
//--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
#include "lua.h"
#include "lauxlib.h"
//--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
int LUA_REGISTRYINDEX = 0;
//--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

lua_Number lua52_tonumber(lua_State *L, int iValue) {
    return lua_tonumberx(L, iValue, NULL);
}
//--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

lua_Integer lua52_tointeger(lua_State *L, int idx) {
    return lua_tointegerx(L, idx, NULL);
}
//--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

void lua52_call(lua_State *L, int nargs, int nresults) {
    lua_callk(L, nargs, nresults, 0, NULL);
}
//--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

int lua52_pcall(lua_State *L, int nargs, int nresults, int errfunc) {
    return lua_pcallk(L, nargs, nresults, errfunc, 0, NULL);
}
//--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

int luaL52_typerror(lua_State *L, int narg, const char *tname) {
  const char *msg = lua_pushfstring(L, "%s expected, got %s", tname, luaL_typename(L, narg));
  return luaL_argerror(L, narg, msg);
}
//--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

char * luaL52_prepbuffer(luaL_Buffer *B) {
    return luaL_prepbuffsize(B, LUAL_BUFFERSIZE);
}
//--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

int lua51_upvalueindex(int i) {
    return ((-10002)-i);
}
//--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

int lua52_upvalueindex(int i) {
    return (LUA_REGISTRYINDEX-i);
}
//--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

void luaL51_addchar(luaL_Buffer * B, const char c) {
    ((void)(((luaL_Buffer51 *)B)->p < (((luaL_Buffer51 *)B)->buffer+LUAL_BUFFERSIZE) || luaL_prepbuffer(B)), (*((luaL_Buffer51 *)B)->p++ = (char)(c)));
}
//--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

void luaL52_addchar(luaL_Buffer * B, const char c) {
    ((void)(((luaL_Buffer52 *)B)->n < ((luaL_Buffer52 *)B)->size || luaL_prepbuffsize((B), 1)), (((luaL_Buffer52 *)B)->b[((luaL_Buffer52 *)B)->n++] = (c)));
}
//--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

void luaL51_addsize(luaL_Buffer * B, const size_t s) {
    ((luaL_Buffer51 *)B)->p += s;
}
//--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

void luaL52_addsize(luaL_Buffer * B, const size_t s) {
    ((luaL_Buffer52 *)B)->n += s;
}
//--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

int InitLuaLib() {
    HMODULE hLuaLib = GetModuleHandle(
#ifdef _M_X64
        "PXLua-x64.dll"
#else
        "PXLua.dll"
#endif
    );

    if(hLuaLib == NULL) {
        return 0;
    }

    lua_gettop = (plua_gettop)GetProcAddress(hLuaLib, "lua_gettop");
    lua_settop = (plua_settop)GetProcAddress(hLuaLib, "lua_settop");
    lua_pushvalue = (plua_pushvalue)GetProcAddress(hLuaLib, "lua_pushvalue");
    lua_remove = (plua_remove)GetProcAddress(hLuaLib, "lua_remove");
    lua_insert = (plua_insert)GetProcAddress(hLuaLib, "lua_insert");
    lua_replace = (plua_replace)GetProcAddress(hLuaLib, "lua_replace");
    lua_checkstack = (plua_checkstack)GetProcAddress(hLuaLib, "lua_checkstack");
    lua_isnumber = (plua_isnumber)GetProcAddress(hLuaLib, "lua_isnumber");
    lua_isstring = (plua_isstring)GetProcAddress(hLuaLib, "lua_isstring");
    lua_isuserdata = (plua_isuserdata)GetProcAddress(hLuaLib, "lua_isuserdata");
    lua_type = (plua_type)GetProcAddress(hLuaLib, "lua_type");
    lua_typename = (plua_typename)GetProcAddress(hLuaLib, "lua_typename");
    lua_tonumber = (plua_tonumber)GetProcAddress(hLuaLib, "lua_tonumber"); // 5.1 only
    lua_tonumberx = (plua_tonumberx)GetProcAddress(hLuaLib, "lua_tonumberx"); // 5.2+
    lua_tointeger = (plua_tointeger)GetProcAddress(hLuaLib, "lua_tointeger"); // 5.1 only
    lua_tointegerx = (plua_tointegerx)GetProcAddress(hLuaLib, "lua_tointegerx"); // 5.2+
    lua_toboolean = (plua_toboolean)GetProcAddress(hLuaLib, "lua_toboolean");
    lua_tolstring = (plua_tolstring)GetProcAddress(hLuaLib, "lua_tolstring");
    lua_touserdata = (plua_touserdata)GetProcAddress(hLuaLib, "lua_touserdata");
    lua_rawequal = (plua_rawequal)GetProcAddress(hLuaLib, "lua_rawequal");
    lua_pushnil = (plua_pushnil)GetProcAddress(hLuaLib, "lua_pushnil");
    lua_pushnumber = (plua_pushnumber)GetProcAddress(hLuaLib, "lua_pushnumber");
    lua_pushinteger = (plua_pushinteger)GetProcAddress(hLuaLib, "lua_pushinteger");
    lua_pushlstring = (plua_pushlstring)GetProcAddress(hLuaLib, "lua_pushlstring");
    lua_pushstring = (plua_pushstring)GetProcAddress(hLuaLib, "lua_pushstring");
    lua_pushfstring = (plua_pushfstring)GetProcAddress(hLuaLib, "lua_pushfstring");
    lua_pushcclosure = (plua_pushcclosure)GetProcAddress(hLuaLib, "lua_pushcclosure");
    lua_pushboolean = (plua_pushboolean)GetProcAddress(hLuaLib, "lua_pushboolean");
    lua_pushlightuserdata = (plua_pushlightuserdata)GetProcAddress(hLuaLib, "lua_pushlightuserdata");
    lua_gettable = (plua_gettable)GetProcAddress(hLuaLib, "lua_gettable");
    lua_getfield = (plua_getfield)GetProcAddress(hLuaLib, "lua_getfield");
    lua_rawget = (plua_rawget)GetProcAddress(hLuaLib, "lua_rawget");
    lua_rawgeti = (plua_rawgeti)GetProcAddress(hLuaLib, "lua_rawgeti");
    lua_createtable = (plua_createtable)GetProcAddress(hLuaLib, "lua_createtable");
    lua_newuserdata = (plua_newuserdata)GetProcAddress(hLuaLib, "lua_newuserdata");
    lua_getmetatable = (plua_getmetatable)GetProcAddress(hLuaLib, "lua_getmetatable");
    lua_settable = (plua_settable)GetProcAddress(hLuaLib, "lua_settable");
    lua_setfield = (plua_setfield)GetProcAddress(hLuaLib, "lua_setfield");
    lua_rawset = (plua_rawset)GetProcAddress(hLuaLib, "lua_rawset");
    lua_rawseti = (plua_rawseti)GetProcAddress(hLuaLib, "lua_rawseti");
    lua_setmetatable = (plua_setmetatable)GetProcAddress(hLuaLib, "lua_setmetatable");
    lua_call = (plua_call)GetProcAddress(hLuaLib, "lua_call"); // 5.1 only
    lua_callk = (plua_callk)GetProcAddress(hLuaLib, "lua_callk"); // 5.2+
    lua_pcall = (plua_pcall)GetProcAddress(hLuaLib, "lua_pcall"); // 5.1 only
    lua_pcallk = (plua_pcallk)GetProcAddress(hLuaLib, "lua_pcallk"); // 5.2+
    lua_error = (plua_error)GetProcAddress(hLuaLib, "lua_error");
    lua_next = (plua_next)GetProcAddress(hLuaLib, "lua_next");
    lua_concat = (plua_concat)GetProcAddress(hLuaLib, "lua_concat");
    lua_len = (plua_len)GetProcAddress(hLuaLib, "lua_len");
    lua_getallocf = (plua_getallocf)GetProcAddress(hLuaLib, "lua_getallocf");

    luaL_register = (pluaL_register)GetProcAddress(hLuaLib, "luaL_register"); // 5.1 only
    luaL_typerror = (pluaL_typerror)GetProcAddress(hLuaLib, "luaL_typerror"); // 5.1 only
    luaL_argerror = (pluaL_argerror)GetProcAddress(hLuaLib, "luaL_argerror");
    luaL_checklstring = (pluaL_checklstring)GetProcAddress(hLuaLib, "luaL_checklstring");
    luaL_optlstring = (pluaL_optlstring)GetProcAddress(hLuaLib, "luaL_optlstring");
    luaL_checknumber = (pluaL_checknumber)GetProcAddress(hLuaLib, "luaL_checknumber");
    luaL_optnumber = (pluaL_optnumber)GetProcAddress(hLuaLib, "luaL_optnumber");
    luaL_checkinteger = (pluaL_checkinteger)GetProcAddress(hLuaLib, "luaL_checkinteger");
    luaL_optinteger = (pluaL_optinteger)GetProcAddress(hLuaLib, "luaL_optinteger");
    luaL_checktype = (pluaL_checktype)GetProcAddress(hLuaLib, "luaL_checktype");
    luaL_checkstack = (pluaL_checkstack)GetProcAddress(hLuaLib, "luaL_checkstack");
    luaL_newmetatable = (pluaL_newmetatable)GetProcAddress(hLuaLib, "luaL_newmetatable");
    luaL_checkudata = (pluaL_checkudata)GetProcAddress(hLuaLib, "luaL_checkudata");
    luaL_error = (pluaL_error)GetProcAddress(hLuaLib, "luaL_error");
    luaL_checkoption = (pluaL_checkoption)GetProcAddress(hLuaLib, "luaL_checkoption");
    luaL_ref = (pluaL_ref)GetProcAddress(hLuaLib, "luaL_ref");
    luaL_unref = (pluaL_unref)GetProcAddress(hLuaLib, "luaL_unref");
    luaL_buffinit = (pluaL_buffinit)GetProcAddress(hLuaLib, "luaL_buffinit");
    luaL_prepbuffer = (pluaL_prepbuffer)GetProcAddress(hLuaLib, "luaL_prepbuffer"); // 5.1 only
    luaL_prepbuffsize = (pluaL_prepbuffsize)GetProcAddress(hLuaLib, "luaL_prepbuffsize"); // 5.2+
    luaL_addlstring = (pluaL_addlstring)GetProcAddress(hLuaLib, "luaL_addlstring");
    luaL_addstring = (pluaL_addstring)GetProcAddress(hLuaLib, "luaL_addstring");
    luaL_addvalue = (pluaL_addvalue)GetProcAddress(hLuaLib, "luaL_addvalue");
    luaL_pushresult = (pluaL_pushresult)GetProcAddress(hLuaLib, "luaL_pushresult");
    luaL_setfuncs = (pluaL_setfuncs)GetProcAddress(hLuaLib, "luaL_setfuncs"); // 5.2+
    luaL_requiref = (pluaL_requiref)GetProcAddress(hLuaLib, "luaL_requiref"); // 5.2+

    if(luaL_register == NULL && (luaL_setfuncs == NULL || luaL_requiref == NULL)) { // 5.1 VS 5.2+
        return 0;
    }

    if(lua_gettop == NULL || lua_settop == NULL || lua_pushvalue == NULL || lua_remove == NULL || lua_insert == NULL || lua_replace == NULL || lua_checkstack == NULL || lua_isnumber == NULL ||
        lua_isstring == NULL || lua_isuserdata == NULL || lua_type == NULL || lua_typename == NULL || lua_toboolean == NULL || lua_tolstring == NULL || lua_touserdata == NULL ||
        lua_rawequal == NULL || lua_pushnil == NULL || lua_pushnumber == NULL || lua_pushinteger == NULL || lua_pushlstring == NULL || lua_pushstring == NULL || lua_pushfstring == NULL ||
        lua_pushcclosure == NULL || lua_pushboolean == NULL || lua_pushlightuserdata == NULL || lua_gettable == NULL || lua_getfield == NULL || lua_rawget == NULL || lua_rawgeti == NULL ||
        lua_createtable == NULL || lua_newuserdata == NULL || lua_getmetatable == NULL || lua_settable == NULL || lua_setfield == NULL || lua_rawset == NULL || lua_rawseti == NULL ||
        lua_setmetatable == NULL || lua_error == NULL || lua_next == NULL || lua_concat == NULL || lua_getallocf == NULL ||
        luaL_argerror == NULL || luaL_checklstring == NULL || luaL_optlstring == NULL || luaL_checknumber == NULL || luaL_optnumber == NULL || luaL_checkinteger == NULL ||
        luaL_optinteger == NULL || luaL_checktype == NULL || luaL_checkstack == NULL || luaL_newmetatable == NULL || luaL_checkudata == NULL || luaL_error == NULL || luaL_checkoption == NULL ||
        luaL_ref == NULL || luaL_unref == NULL || luaL_buffinit == NULL || luaL_addlstring == NULL || luaL_addstring == NULL || luaL_addvalue == NULL || luaL_pushresult == NULL) {
        return 0;
    }

    if(lua_tonumber == NULL) {
        if(lua_tonumberx == NULL) {
            return 0;
        }
        lua_tonumber = lua52_tonumber;
    }

    if(lua_tointeger == NULL) {
        if(lua_tointegerx == NULL) {
            return 0;
        }
        lua_tointeger = lua52_tointeger;
    }

    if(lua_call == NULL) {
        if(lua_callk == NULL) {
            return 0;
        }
        lua_call = lua52_call;
    }

    if(lua_pcall == NULL) {
        if(lua_pcallk == NULL) {
            return 0;
        }
        lua_pcall = lua52_pcall;
    }

    if(luaL_typerror == NULL) {
        luaL_typerror = luaL52_typerror;
    }

    if(luaL_prepbuffer == NULL) {
        if(luaL_prepbuffsize == NULL) {
            return 0;
        }
        luaL_prepbuffer = luaL52_prepbuffer;
    }

    lua_objlen = (plua_strlen)GetProcAddress(hLuaLib, "lua_objlen"); // 5.1 only
    if(lua_objlen == NULL) {
        lua_strlen = (plua_strlen)GetProcAddress(hLuaLib, "lua_rawlen"); // 5.2+
        lua_len = (plua_len)GetProcAddress(hLuaLib, "lua_rawlen"); // 5.2+
        if(lua_strlen == NULL || lua_len == NULL) {
            return 0;
        }
        lua_rawlen = lua_strlen;
    } else {
        lua_strlen = lua_objlen;
        lua_rawlen = lua_strlen;
    }

    if(luaL_register == NULL) {
        LUA_REGISTRYINDEX = -1001000;
        lua_upvalueindex = lua52_upvalueindex;
        luaL_addchar = luaL52_addchar;
        luaL_addsize = luaL52_addsize;
    } else {
        LUA_REGISTRYINDEX = -10000;
        lua_upvalueindex = lua51_upvalueindex;
        luaL_addchar = luaL51_addchar;
        luaL_addsize = luaL51_addsize;
    }

    return 1;
}
//--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
