#include "tile/base/internal/meta.h"

#include "gtest/gtest.h"

namespace tile {
namespace internal {

// no ret function
void
F0()
{}

void
F1(int)
{}

void
F2(int, char)
{}

int
F3(int, char)
{
    return 0;
}

static auto LF0 = []() {};
static auto LF1 = [](int) {};
static auto LF2 = [](int, char) {};

class A {
public:
    void F0() {}

    int F1(int v) { return v; }

    std::pair<int, char> F2(int v, char c) { return {v, c}; }
};

struct VoidFunctor {
    void operator()() {}
};

struct IntFunctor {
    int operator()(int a) { return a; }
};

TEST(apply, Function)
{

    auto p0 = std::make_tuple();
    auto p1 = std::make_tuple(1);
    auto p2 = std::make_tuple(1, 'a');
    apply(F0, std::make_tuple());
    apply(F1, std::make_tuple(1));
    apply(F2, std::make_tuple(1, 'a'));
    apply(LF0, std::make_tuple());
    apply(LF1, std::make_tuple(1));
    apply(LF2, std::make_tuple(1, 'a'));
}

TEST(apply, MemFunction)
{
    A a;
    auto p0 = std::make_tuple();
    auto p1 = std::make_tuple(1);
    auto p2 = std::make_tuple(1, 'a');
    apply(&A::F0, std::make_tuple(a));
    auto r1 = apply(&A::F1, std::make_tuple(a, 1));
    auto r2 = apply(&A::F2, std::make_tuple(&a, 1, 'a'));
    ASSERT_EQ(1, r1);
    ASSERT_EQ(1, r2.first);
    ASSERT_EQ('a', r2.second);
}

TEST(apply, IntFunctor)
{
    IntFunctor f;
    auto p1 = std::make_tuple(1);
    auto r1 = apply(f, p1);
    ASSERT_EQ(1, r1);
}

}// namespace internal
}// namespace tile
