#include <iostream>
#include <cstdlib>
#include <tuple>
#include <utility>
#include <sstream>

#include "test_utils.hh"

#include "ProxyPack.hh"

using namespace gs;
using namespace std;

int main(int argc, char const* argv[])
{
    typedef CPP11_shared_ptr<double> Dptr;
    typedef CPP11_shared_ptr<int> Iptr;

    auto tuple1 = std::tuple<int, float, double, long long>();
    cout << "Tuple class id is " << ClassId::makeId<decltype(tuple1)>().id()
         << endl;

    std::ostringstream os1;
    std::get<0>(tuple1) = 2;
    std::get<1>(tuple1) = 3.f;
    std::get<2>(tuple1) = 4.0;
    std::get<3>(tuple1) = 5;
    write_item(os1, tuple1);
    auto tuplecopy(tuple1);
    std::get<0>(tuple1) = 6;
    std::get<1>(tuple1) = 7.f;
    std::get<2>(tuple1) = 8.0;
    std::get<3>(tuple1) = 9;
    write_item(os1, tuple1, false);

    std::istringstream is1(os1.str());
    auto tuple2 = std::tuple<int, float, double, long long>();
    restore_item(is1, &tuple2);
    assert(tuplecopy == tuple2);
    assert(tuple1 != tuple2);
    restore_item(is1, &tuple2, false);
    assert(tuple1 == tuple2);

    int i1 = 15;

    IOProxy<int> ib(i1, "i1");
    std::ostringstream os;

    i1 = 10;
    ib.write(os);
    i1 = 15;
    ib.write(os);
    i1 = 20;
    ib.write(os);

    Dptr dp(new double());
    IOProxy<Dptr> bd(dp, "dp");
    *dp = 1.0;
    assert(bd.write(os));
    *dp = 3.0;
    assert(bd.write(os));
    *dp = 123.0;
    assert(bd.write(os));

    auto agw = make_proxy_pack(
        pack_member(i1),
        pack_member(dp)
    );
    i1 = 30;
    *dp = 345.0;
    assert(agw.write(os));

    std::istringstream is(os.str());
    int ival;
    IOProxy<int> ivb(ival, "ival");
    ClassId dummy(ClassId::invalidId());
    IOProxy<int>::restore(dummy, is, &ivb);
    assert(ivb.name() == "i1");
    assert(ival == 10);
    IOProxy<int>::restore(dummy, is, &ivb);
    assert(ival == 15);
    IOProxy<int>::restore(dummy, is, &ivb);
    assert(ival == 20);

    Dptr rbd;
    IOProxy<Dptr> br(rbd, "name should not matter here");
    IOProxy<Dptr>::restore(dummy, is, &br);
    assert(br.name() == "dp");
    assert(*rbd == 1.0);
    IOProxy<Dptr>::restore(dummy, is, &br);
    assert(*rbd == 3.0);
    IOProxy<Dptr>::restore(dummy, is, &br);
    assert(*rbd == 123.0);

    auto rag = make_proxy_pack(
        pack_member(ival),
        pack_member(rbd)
    );

    cout << "ProxyPack class name is " << rag.classId().name() << endl;
    assert(rag.restore(agw.classId(), is, &rag));
    cout << "Var 0 enabled: " << std::get<0>(rag).isEnabled() << endl;
    cout << "Var 1 enabled: " << std::get<1>(rag).isEnabled() << endl;
    assert(ival == 30);
    assert(*rbd == 345.0);

    // Tuple-related ops
    //
    // tuple_cat -- concatenate two tuples
    // tie
    // swap
    // tuple_size<tuple>::value
    // tuple_element<N, tuple>::type
    // get<N, tuple>x
    // make_tuple

    return 0;
}
