/******************************************************************************
    Project:    MicroMacro
    Author:     SolarStrike Software
    URL:        www.solarstrike.net
    License:    Modified BSD (see license.txt)
******************************************************************************/

#include "global_addon.h"
#include "error.h"
#include "wininclude.h"
#include "filesystem.h"

extern "C"
{
#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>
}

/* Avoid the stupid macros from _mingw_print_pop.h that break the use of any variable names sprintf/printf */
#ifdef sprintf
    #undef sprintf
#endif
#ifdef printf
    #undef printf
#endif

lua_CFunction Global_addon::sprintf = 0;
std::vector<std::string> Global_addon::includedList;

int Global_addon::regmod(lua_State *L)
{
    // Create a shortcut to string.format as sprintf
    lua_getglobal(L, "string");
    lua_getfield(L, -1, "format");
    sprintf = lua_tocfunction(L, -1);
    lua_setglobal(L, "sprintf");
    lua_pop(L, 1); // Pop string module

    // Other functions
    lua_pushcfunction(L, Global_addon::printf);
    lua_setglobal(L, "printf");

    lua_pushcfunction(L, Global_addon::unpack2);
    lua_setglobal(L, "unpack2");

    lua_pushcfunction(L, Global_addon::include);
    lua_setglobal(L, "include");

    // Clear our included list (in case it is dirty from last time)
    includedList.clear();

    return MicroMacro::ERR_OK;
}

/*  printf(string fmt, ...)
    Returns:    nil

    C-style formatted output. That's all.
*/
int Global_addon::printf(lua_State *L)
{
    //int top = lua_gettop(L);
    sprintf(L); // Format it

    if( lua_isstring(L, -1) ) // If we can, print it!
        ::printf("%s", lua_tostring(L, -1));
    return 0;
}

/*  unpack2(...)
    Returns:    table

    Takes varargs and returns them as a table
*/
int Global_addon::unpack2(lua_State *L)
{
    int top = lua_gettop(L);
    if( top == 0 )
        return 0;

    lua_newtable(L);
    for(int i = 1; i <= top; i++)
    {
        lua_pushnumber(L, i); // Key
        lua_pushvalue(L, i); // Value
        lua_settable(L, -3); // Set
    }

    return 1;
}

/*  include(string filename [, boolean force])
    Returns:    result of file

    "include" (dofile) a file.
    If 'force' is false or ungiven, this will not include
    the same file multiple times.

    NOTE: This function modified the CWD, both
    before AND after loading the target file.
*/
int Global_addon::include(lua_State *L)
{
    bool forceRun = false;
    char *filename;
    int top = lua_gettop(L);

    if( top != 1 && top != 2 )
        wrongArgs(L);

    checkType(L, LT_STRING, 1);
    filename = (char *)lua_tostring(L, 1);

    if( top == 2 )
    {
        checkType(L, LT_BOOLEAN, 2);
        forceRun = lua_toboolean(L, 2);
    }

    // Get the full (non-relative) path and filename
    char *newFilenamePtr = NULL; // This will point to the filename in fullNewPath below
    char fullNewPath[MAX_PATH + 1];
    GetFullPathName(filename, MAX_PATH, fullNewPath, &newFilenamePtr);

    // Make sure we even need to run it
    if( !forceRun )
    {
        for(unsigned int i = 0; i < includedList.size(); i++)
        {
            if( includedList.at(i) == fullNewPath )
                return 0; // Already included, skip it
        }
    }

    // Copy current CWD
    char cwdBuffer[MAX_PATH + 1];
    GetCurrentDirectory(MAX_PATH, (LPTSTR)&cwdBuffer);

    // Set temporary CWD
    SetCurrentDirectory(::getFilePath(fullNewPath, false).c_str());

    // And run it!
    if( luaL_dofile(L, newFilenamePtr) != LUA_OK )
    {
        luaL_error(L, lua_tostring(L, -1));
    }

    // Reset original CWD
    SetCurrentDirectory((LPCTSTR)&cwdBuffer);

    // Insert it into our included list
    includedList.push_back(fullNewPath);

    // Return count is stack size - original
    return lua_gettop(L) - top;
}
