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

#include "TestBase.h"

#include <cstdio>
#include <ctime>
#include <iomanip>
#include <iostream>
#include <string>
#include <vector>

using namespace std;
using namespace luabridge;

//------------------------------------------------------------------------------
/**
  Simple stopwatch for measuring elapsed time.
*/
class Stopwatch
{
private:
    clock_t m_start;

public:
    Stopwatch() { start(); }

    void start() { m_start = clock(); }

    double getElapsedSeconds()
    {
        clock_t now;

        now = clock();

        return (double(now - m_start)) / CLOCKS_PER_SEC;
    }
};

//------------------------------------------------------------------------------
/**
  Classes used for performance tests.
*/

struct A
{
    A() : data(0), prop(0) {}

    void mf1() {}

    void mf2(A*) {}

    void mf3(A&) {}

    virtual void vf1() {}

    int data;

    int prop;
    int getprop() const { return prop; }
    void setprop(int v) { prop = v; }
};

//------------------------------------------------------------------------------

void addToState(lua_State* L)
{
    getGlobalNamespace(L)
        .beginClass<A>("A")
        .addConstructor<void (*)(void)>()
        .addFunction("mf1", &A::mf1)
        .addFunction("mf2", &A::mf2)
        .addFunction("mf3", &A::mf3)
        .addFunction("vf1", &A::vf1)
        .addData("data", &A::data)
        .addProperty("prop", &A::getprop, &A::setprop)
        .endClass();
}

void runTests(lua_State* L)
{
    cout.precision(4);

    int result;

    luaL_dostring(L, "a = A()");

    int const trials = 5;

    for (int trial = 0; trial < trials; ++trial)
    {
        result = luaL_loadstring(L, "a:mf1 ()");
        if (result != 0)
            lua_error(L);

        int const N = 10000000;

        Stopwatch sw;

        sw.start();
        for (int i = 0; i < N; ++i)
        {
            lua_pushvalue(L, -1);
            lua_call(L, 0, 0);
        }

        double const seconds = sw.getElapsedSeconds();

        cout << "Elapsed time: " << seconds << endl;
    }
}

void runPerformanceTests()
{
    lua_State* L = luaL_newstate();
    luaL_openlibs(L);

    addToState(L);
    runTests(L);

    lua_close(L);
}

struct PerformanceTests : TestBase
{
};

TEST_F(PerformanceTests, AllTests)
{
    addToState(L);
    runTests(L);
}
