#include "UnitTest++.h"

#include "test_utils.hh"

#include "geners/VarPack.hh"
#include "geners/StringArchive.hh"
#include "geners/Record.hh"
#include "geners/Reference.hh"

using namespace gs;
using namespace std;

#ifdef CPP11_STD_AVAILABLE

namespace {
    TEST(tupleIO)
    {
        //
        // Tuple-related ops
        //
        // tuple_cat -- concatenate two tuples
        // tie -- make tuple of references
        // swap
        // tuple_size<tuple>::value
        // tuple_element<N, tuple>::type
        // std::get<N>(tuple)
        // make_tuple
        //
        StringArchive ar0;

        // Test that archive works with bare tuples
        auto tuple1 = make_tuple(2, 3.f, 4.0, 5LL);
        auto tuplecopy0(tuple1);
        ar0 << Record(tuple1, "tuple1_0", "top");

        tuple1 = make_tuple(6, 7.f, 8.0, 9LL);
        ar0 << Record(tuple1, "tuple1_1", "top");

        // Get them back
        std::tuple<int, float, double, long long> tuple2;
        Reference<decltype(tuple2)>(ar0, "tuple1_0", "top").restore(0,&tuple2);
        CHECK(tuplecopy0 == tuple2);
        Reference<decltype(tuple2)>(ar0, "tuple1_1", "top").restore(0,&tuple2);
        CHECK(tuple1 == tuple2);

        // Create a vector of tuples of PODs. For these, the
        // amount of space taken in the archive should be just
        // the size of the PODs (plus some header).
        std::vector<std::tuple<int,float,double> > v1, v2;
        for (unsigned i=0; i<3; ++i)
        {
            auto tmp2 = std::make_tuple(
                static_cast<int>(test_rng()*10000),
                static_cast<float>(test_rng()),
                static_cast<double>(test_rng())
            );
            v1.push_back(tmp2);
            if (i < 2)
            {
                v2.push_back(tmp2);
                CHECK(v1 == v2);
            }
        }
        const unsigned long lenc = ar0.dataSize();
        ar0 << Record(v1, "v1", "top");
        const unsigned long lenv1 = ar0.dataSize();
        const unsigned long sv1 = lenv1 - lenc;
        ar0 << Record(v2, "v2", "top");
        const unsigned long lenv2 = ar0.dataSize();
        const unsigned long sv2 = lenv2 - lenv1;
        CHECK_EQUAL(sizeof(int) + sizeof(float) + sizeof(double), sv1 - sv2);

        // Make sure we can read these back
        std::vector<std::tuple<int,float,double> > r1, r2;
        Reference<decltype(r1)>(ar0, "v1", "top").restore(0, &r1);
        Reference<decltype(r2)>(ar0, "v2", "top").restore(0, &r2);

        CHECK(r1 == v1);
        CHECK(r2 == v2);

        // Make sure we can read these back as VarPack objects as well
        std::vector<VarPack<int,float,double> > r1v;
        Reference<decltype(r1v)>(ar0, "v1", "top").restore(0, &r1v);

        const unsigned nPacks = r1v.size();
        CHECK(nPacks == v1.size());
        for (unsigned i=0; i<nPacks; ++i)
        {
            CHECK(std::get<0>(r1v[i]) == std::get<0>(v1[i]));
            CHECK(std::get<1>(r1v[i]) == std::get<1>(v1[i]));
            CHECK(std::get<2>(r1v[i]) == std::get<2>(v1[i]));
        }
    }
}

#else // CPP11_STD_AVAILABLE

namespace {
    TEST(tupleIO)
    {
    }
}

#endif // CPP11_STD_AVAILABLE
