#include <iostream>
#include <fstream>
#include <cstdlib>

#include "test_utils.hh"

#include "geners/BinaryFileArchive.hh"
#include "geners/RowPacker.hh"
#include "geners/IOPtr.hh"

using namespace gs;
using namespace std;

#ifdef CPP11_STD_AVAILABLE

static unsigned MB;
static const char* filename;
static const char* objname;

#define MEGABYTE 1048576ULL

#define io_proxy(obj) gs::make_IOProxy( obj , #obj )

void usage_and_exit(const char* prog)
{
    cerr << "\nUsage:  " << parse_progname(prog) 
         << "  archive  object_name  size_in_MB\n" << endl;
    exit(1);
}

void make_big_tuple()
{
    BinaryFileArchive ar(filename, "w:z=z", "Huge pack test");
    if (!ar.isWritable())
    {
        std::cerr << "Failed to open archive \"" << ar.name()
                  << "\" for writing" << std::endl;
        return;
    }

    double d;
    long double ld;
    long li;
    unsigned long uli;
    long long lli;
    unsigned long long ulli;

    auto t = std::make_tuple(
        io_proxy(d),
        io_proxy(ld),
        io_proxy(li),
        io_proxy(uli),
        io_proxy(lli),
        io_proxy(ulli)
    );

    unsigned long rowSize = sizeof(d) + sizeof(ld) + sizeof(li) + 
                            sizeof(uli) + sizeof(lli) + sizeof(ulli);
    unsigned long nrows = MB*MEGABYTE/rowSize;

    RowPacker<decltype(t)> nt("huge", ar, objname, "my category", t);

    for (unsigned long i=0; i<nrows; ++i)
    {
        unsigned long j = std::tuple_size<decltype(t)>::value*nrows;

        d = j++;
        ld = j++;
        li = j++;
        uli = j++;
        lli = j++;
        ulli = j++;
        
        nt.fill(t);
    }

    nt.write();
    ar.flush();
    cout << "Wrote " << nrows << " rows" << endl;
}

int main(int argc, char const* argv[])
{
    if (argc != 4)
        usage_and_exit(argv[0]);

    filename = argv[1];
    objname = argv[2];

    if (parse_unsigned(argv[3], &MB))
        usage_and_exit(argv[0]);

    if (MB == 0)
        usage_and_exit(argv[0]);

    const double t = time_of_run(make_big_tuple, 1);
    std::cout << "Program took " << t/1000000 << " sec" << std::endl;

    return 0;
}

#else // CPP11_STD_AVAILABLE

int main(int argc, char const* argv[])
{
    cout << "Please recompile with C++11 features enabled" << endl;
    return 0;
}

#endif // CPP11_STD_AVAILABLE
