// 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
 
 
 
// A set of tests of different types' communication with Lua
 
 
 
#include "TestBase.h"
 
 
 
#include <gtest/gtest.h>
 
 
 
#include <cstring>
 
#include <iostream>
 
#include <memory>
 
#include <string>
 
 
 
void printValue(lua_State* L, int index)
 
{
 
    int type = lua_type(L, index);
 
    switch (type)
 
    {
 
    case LUA_TBOOLEAN:
 
        std::cerr << std::boolalpha << (lua_toboolean(L, index) != 0);
 
        break;
 
    case LUA_TSTRING:
 
        std::cerr << lua_tostring(L, index);
 
        break;
 
    case LUA_TNUMBER:
 
        std::cerr << lua_tonumber(L, index);
 
        break;
 
    case LUA_TTABLE:
 
    case LUA_TTHREAD:
 
    case LUA_TFUNCTION:
 
        std::cerr << lua_topointer(L, index);
 
        break;
 
    }
 
    std::cerr << ": " << lua_typename(L, type) << " (" << type << ")" << std::endl;
 
}
 
 
 
struct LuaBridgeTest : TestBase
 
{
 
};
 
 
 
template<class T>
 
T identityCFunction(T value)
 
{
 
    return value;
 
}
 
 
 
TEST_F(LuaBridgeTest, CFunction)
 
{
 
    luabridge::getGlobalNamespace(L)
 
        .addFunction("boolFn", &identityCFunction<bool>)
 
        .addFunction("ucharFn", &identityCFunction<unsigned char>)
 
        .addFunction("shortFn", &identityCFunction<short>)
 
        .addFunction("ushortFn", &identityCFunction<unsigned short>)
 
        .addFunction("intFn", &identityCFunction<int>)
 
        .addFunction("uintFn", &identityCFunction<unsigned int>)
 
        .addFunction("longFn", &identityCFunction<long>)
 
        .addFunction("ulongFn", &identityCFunction<unsigned long>)
 
        .addFunction("longlongFn", &identityCFunction<long long>)
 
        .addFunction("ulonglongFn", &identityCFunction<unsigned long long>)
 
        .addFunction("floatFn", &identityCFunction<float>)
 
        .addFunction("doubleFn", &identityCFunction<double>)
 
        .addFunction("charFn", &identityCFunction<char>)
 
        .addFunction("cstringFn", &identityCFunction<const char*>)
 
        .addFunction("stringFn", &identityCFunction<std::string>);
 
 
 
    {
 
        runLua("result = ucharFn (255)");
 
        ASSERT_EQ(true, result().isNumber());
 
        ASSERT_EQ(255u, result<unsigned char>());
 
    }
 
 
 
    {
 
        runLua("result = boolFn (false)");
 
        ASSERT_EQ(true, result().isBool());
 
        ASSERT_EQ(false, result<bool>());
 
    }
 
    {
 
        runLua("result = boolFn (true)");
 
        ASSERT_EQ(true, result().isBool());
 
        ASSERT_EQ(true, result<bool>());
 
    }
 
 
 
    {
 
        runLua("result = shortFn (-32768)");
 
        ASSERT_EQ(true, result().isNumber());
 
        ASSERT_EQ(-32768, result<int>());
 
    }
 
 
 
    {
 
        runLua("result = ushortFn (32767)");
 
        ASSERT_EQ(true, result().isNumber());
 
        ASSERT_EQ(32767u, result<unsigned int>());
 
    }
 
    {
 
        runLua("result = intFn (-500)");
 
        ASSERT_EQ(true, result().isNumber());
 
        ASSERT_EQ(-500, result<int>());
 
    }
 
 
 
    {
 
        runLua("result = uintFn (42)");
 
        ASSERT_EQ(true, result().isNumber());
 
        ASSERT_EQ(42u, result<unsigned int>());
 
    }
 
 
 
    {
 
        runLua("result = longFn (-8000)");
 
        ASSERT_EQ(true, result().isNumber());
 
        ASSERT_EQ(-8000, result<long>());
 
    }
 
 
 
    {
 
        runLua("result = ulongFn (9000)");
 
        ASSERT_EQ(true, result().isNumber());
 
        ASSERT_EQ(9000u, result<unsigned long>());
 
    }
 
 
 
    {
 
        runLua("result = longlongFn (-8000)");
 
        ASSERT_EQ(true, result().isNumber());
 
        ASSERT_EQ(-8000, result<long long>());
 
    }
 
 
 
    {
 
        runLua("result = ulonglongFn (9000)");
 
        ASSERT_EQ(true, result().isNumber());
 
        ASSERT_EQ(9000u, result<unsigned long long>());
 
    }
 
 
 
    {
 
        runLua("result = floatFn (3.14)");
 
        ASSERT_EQ(true, result().isNumber());
 
        ASSERT_FLOAT_EQ(3.14f, result<float>());
 
    }
 
 
 
    {
 
        runLua("result = doubleFn (-12.3)");
 
        ASSERT_EQ(true, result().isNumber());
 
        ASSERT_DOUBLE_EQ(-12.3, result<double>());
 
    }
 
 
 
    {
 
        runLua("result = charFn ('a')");
 
        ASSERT_EQ(true, result().isString());
 
        ASSERT_EQ('a', result<char>());
 
    }
 
 
 
    {
 
        runLua("result = cstringFn ('abc')");
 
        ASSERT_EQ(true, result().isString());
 
        ASSERT_STREQ("abc", result<const char*>());
 
    }
 
 
 
    {
 
        runLua("result = stringFn ('lua')");
 
        ASSERT_EQ(true, result().isString());
 
        ASSERT_EQ("lua", result<std::string>());
 
    }
 
}
 
 
 
template<class T>
 
struct TestClass
 
{
 
    TestClass(T data) : data(data), constData(data) {}
 
 
 
    T getValue() { return data; }
 
    T* getPtr() { return &data; }
 
    T const* getConstPtr() { return &data; }
 
    T& getRef() { return data; }
 
    T const& getConstRef() { return data; }
 
    T getValueConst() const { return data; }
 
    T* getPtrConst() const { return &data; }
 
    T const* getConstPtrConst() const { return &data; }
 
    T& getRefConst() const { return data; }
 
    T const& getConstRefConst() const { return data; }
 
 
 
    mutable T data;
 
    mutable T constData;
 
};
 
 
 
TEST_F(LuaBridgeTest, ClassFunction)
 
{
 
    typedef TestClass<int> Inner;
 
    typedef TestClass<Inner> Outer;
 
 
 
    luabridge::getGlobalNamespace(L)
 
        .beginClass<Inner>("Inner")
 
        .addConstructor<void (*)(int)>()
 
        .addData("data", &Inner::data)
 
        .endClass()
 
        .beginClass<Outer>("Outer")
 
        .addConstructor<void (*)(Inner)>()
 
        .addFunction("getValue", &Outer::getValue)
 
        .addFunction("getPtr", &Outer::getPtr)
 
        .addFunction("getConstPtr", &Outer::getConstPtr)
 
        .addFunction("getRef", &Outer::getRef)
 
        .addFunction("getConstRef", &Outer::getConstRef)
 
        .addFunction("getValueConst", &Outer::getValueConst)
 
        .addFunction("getPtrConst", &Outer::getPtrConst)
 
        .addFunction("getConstPtrConst", &Outer::getConstPtrConst)
 
        .addFunction("getRefConst", &Outer::getRefConst)
 
        .addFunction("getConstRefConst", &Outer::getConstRefConst)
 
        .endClass();
 
 
 
    Outer outer(Inner(0));
 
    luabridge::setGlobal(L, &outer, "outer");
 
 
 
    outer.data.data = 0;
 
    runLua("outer:getValue ().data = 1");
 
    ASSERT_EQ(0, outer.data.data);
 
 
 
    outer.data.data = 1;
 
    runLua("outer:getPtr ().data = 10");
 
    ASSERT_EQ(10, outer.data.data);
 
 
 
    outer.data.data = 2;
 
    ASSERT_THROW(runLua("outer:getConstPtr ().data = 20"), std::runtime_error);
 
 
 
    outer.data.data = 3;
 
    runLua("outer:getRef().data = 30");
 
    ASSERT_EQ(30, outer.data.data);
 
 
 
    outer.data.data = 4;
 
    ASSERT_THROW(runLua("outer:getConstPtr ().data = 40"), std::runtime_error);
 
 
 
    outer.data.data = 5;
 
    runLua("outer:getValueConst ().data = 50");
 
    ASSERT_EQ(5, outer.data.data);
 
 
 
    outer.data.data = 6;
 
    runLua("outer:getPtrConst ().data = 60");
 
    ASSERT_EQ(60, outer.data.data);
 
 
 
    outer.data.data = 7;
 
    ASSERT_THROW(runLua("outer:getConstPtr ().data = 70"), std::runtime_error);
 
 
 
    outer.data.data = 8;
 
    runLua("outer:getRef().data = 80");
 
    ASSERT_EQ(80, outer.data.data);
 
 
 
    outer.data.data = 9;
 
    ASSERT_THROW(runLua("outer:getConstPtr ().data = 90"), std::runtime_error);
 
}