// https://github.com/vinniefalco/LuaBridge
// Copyright 2019, Dmitry Tarakanov
// Copyright 2012, Vinnie Falco <vinnie.falco@gmail.com>
// Copyright 2007, Nathan Reed
// SPDX-License-Identifier: MIT

#pragma once

#include "Lua/LuaLibrary.h"

#include "LuaBridge/LuaBridge.h"

#include <gtest/gtest.h>

#include <stdexcept>

// traceback function, adapted from lua.c
// when a runtime error occurs, this will append the call stack to the error message
//
inline int traceback(lua_State* L)
{
    // look up Lua's 'debug.traceback' function
    lua_getglobal(L, "debug");
    if (!lua_istable(L, -1))
    {
        lua_pop(L, 1);
        return 1;
    }
    lua_getfield(L, -1, "traceback");
    if (!lua_isfunction(L, -1))
    {
        lua_pop(L, 2);
        return 1;
    }
    lua_pushvalue(L, 1); /* pass error message */
    lua_pushinteger(L, 2); /* skip this function and traceback */
    lua_call(L, 2, 1); /* call debug.traceback */
    return 1;
}

/// Base test class. Introduces the global 'result' variable,
/// used for checking of C++ - Lua interoperation.
///
struct TestBase : public ::testing::Test
{
    lua_State* L = nullptr;

    void SetUp() override
    {
        L = nullptr;
        L = luaL_newstate();
        luaL_openlibs(L);
        lua_pushcfunction(L, &traceback);
    }

    void TearDown() override
    {
        if (L != nullptr)
        {
            lua_close(L);
        }
    }

    void runLua(const std::string& script) const
    {
        if (luaL_loadstring(L, script.c_str()) != 0)
        {
            throw std::runtime_error(lua_tostring(L, -1));
        }

        if (lua_pcall(L, 0, 0, -2) != 0)
        {
            throw std::runtime_error(lua_tostring(L, -1));
        }
    }

    template<class T = luabridge::LuaRef>
    T result() const
    {
        return luabridge::getGlobal(L, "result").cast<T>();
    }

    void resetResult() const { luabridge::setGlobal(L, luabridge::LuaRef(L), "result"); }

    void printStack() const
    {
        std::cerr << "===== Stack =====\n";
        for (int i = 1; i <= lua_gettop(L); ++i)
        {
            std::cerr << "@" << i << " = " << luabridge::LuaRef::fromStack(L, i) << "\n";
        }
    }
};
