// https://github.com/vinniefalco/LuaBridge
// Copyright 2019, Dmitry Tarakanov
// SPDX-License-Identifier: MIT

#include "TestBase.h"
#include "TestTypes.h"

#include "LuaBridge/Array.h"

#include <array>

template<class T>
struct ArrayTest : TestBase
{
};

TYPED_TEST_CASE_P(ArrayTest);

TYPED_TEST_P(ArrayTest, LuaRef)
{
    using Traits = TypeTraits<TypeParam>;

    this->runLua("result = {" + Traits::list() + "}");

    std::array<TypeParam, 3> expected;
    std::copy_n(Traits::values().begin(), 3, expected.begin());

    std::array<TypeParam, 3> actual = this->result();
    ASSERT_EQ(expected, actual);
}

REGISTER_TYPED_TEST_CASE_P(ArrayTest, LuaRef);

INSTANTIATE_TYPED_TEST_CASE_P(ArrayTest, ArrayTest, TestTypes);

namespace {

struct Data
{
    /* explicit */ Data(int i = -1000) : i(i) {}

    int i;
};

bool operator==(const Data& lhs, const Data& rhs)
{
    return lhs.i == rhs.i;
}

std::ostream& operator<<(std::ostream& lhs, const Data& rhs)
{
    lhs << "{" << rhs.i << "}";
    return lhs;
}

template<std::size_t S>
std::array<Data, S> processValues(const std::array<Data, S>& data)
{
    return data;
}

template<std::size_t S>
std::array<Data, S> processPointers(const std::array<const Data*, S>& data)
{
    std::array<Data, S> result;
    std::size_t arrayIndex = 0;
    for (const auto* item : data)
    {
        result[arrayIndex] = *item;
        ++arrayIndex;
    }
    return result;
}

} // namespace

struct ArrayTests : TestBase
{
};

TEST_F(ArrayTests, PassFromLua)
{
    luabridge::getGlobalNamespace(L)
        .beginClass<Data>("Data")
        .addConstructor<void (*)(int)>()
        .endClass()
        .addFunction("processValues", &processValues<3>)
        .addFunction("processPointers", &processPointers<3>);

    resetResult();
    runLua("result = processValues ({Data (-1), Data (2), Data (5)})");

    ASSERT_EQ((std::array<Data, 3>({-1, 2, 5})), (result<std::array<Data, 3>>()));

    resetResult();
    runLua("result = processPointers ({Data (-3), Data (4), Data (9)})");

    ASSERT_EQ((std::array<Data, 3>({-3, 4, 9})), (result<std::array<Data, 3>>()));
}
