//

#include <iostream>
#include <iomanip>
#include <boost/scoped_array.hpp>
#include <errno.h>
#define NDEBUG
#include <cassert>
#include "writeengine.h"

using namespace std;

void timespec_sub(const struct timespec& tv1,
                  const struct timespec& tv2,
                  struct timespec& diff)
{
    if (tv2.tv_nsec < tv1.tv_nsec)
    {
        diff.tv_sec = tv2.tv_sec - tv1.tv_sec - 1;
        diff.tv_nsec = tv1.tv_nsec - tv2.tv_nsec;
    }
    else
    {
        diff.tv_sec = tv2.tv_sec - tv1.tv_sec;
        diff.tv_nsec = tv2.tv_nsec - tv1.tv_nsec;
    }
}

// TODO:
// add getopt for CLAs
// add threads for reading a file
// add threads for reading multiple files

// main()
//
int main(int argc, char** argv)
{
    uint64_t acc = 0;
    uint64_t readBlocks = 0; // read size ib blocks
    uint64_t readSize = 0; // read size ib bytes
    uint64_t readBufferSz = 0;
    const uint64_t blockSize = 8192;
    char* alignedbuff = 0;
    boost::scoped_array<char> realbuff;
    const unsigned pageSize = 4096; //getpagesize();
    WriteEngine::FileOp fFileOp;
    BRM::OID_t oid;
    char fname[256];
    struct timespec tm;
    struct timespec tm2;
    struct timespec tm3;
    struct timespec starttm;
    struct timespec endtm;
    struct timespec tottm;
    bool odirect = true;
    int fd = 0;
    char response = 'Y';

    if (argc <= 1)
    {
        cerr << "usage: testread <oid> <buffer size in blocks>" << endl;
        return -1;
    }

    oid = atoi(argv[1]);

    if (oid <= 0)
        exit(-1);

    if (argc >= 2)
    {
        readBlocks = atoi(argv[2]);

        if (readBlocks <= 0)
            readBlocks = 8;
    }

    if (argc >= 4)
    {
        odirect = false;
    }

    readSize = readBlocks * blockSize;
    readBufferSz = readSize + pageSize;

    realbuff.reset(new char[readBufferSz]);

    if (realbuff.get() == 0)
    {
        cerr << "thr_popper: Can't allocate space for a whole extent in memory" << endl;
        return 0;
    }

    if (fFileOp.getFileName(oid, fname) != WriteEngine::NO_ERROR)
    {
        fname[0] = 0;
        throw std::runtime_error("fileOp.getFileName failed");
    }
    else
    {
        cout << "Reading oid: " << oid << " od: " << odirect << " file: " << fname << endl;
    }

#if __LP64__
    alignedbuff = (char*)((((ptrdiff_t)realbuff.get() >> 12) << 12) + pageSize);
#else
    alignedbuff = (char*)(((((ptrdiff_t)realbuff.get() >> 12) << 12) & 0xffffffff) + pageSize);
#endif
    idbassert(((ptrdiff_t)alignedbuff - (ptrdiff_t)realbuff.get()) < (ptrdiff_t)pageSize);
    idbassert(((ptrdiff_t)alignedbuff % pageSize) == 0);

    if (odirect)
        fd = open(fname, O_RDONLY | O_DIRECT | O_LARGEFILE | O_NOATIME);
    else
        fd = open(fname, O_RDONLY | O_LARGEFILE | O_NOATIME);

    if (fd < 0)
    {
        cerr << "Open failed" << endl;
        perror("open");
        throw runtime_error("Error opening file");
    }

    while (toupper(response) != 'N')
    {
        uint64_t i = 1;
        uint64_t rCnt = 0;
        clock_gettime(CLOCK_REALTIME, &starttm);

        while (i != 0)
        {
            //clock_gettime(CLOCK_REALTIME, &tm);
            i = pread(fd, alignedbuff, readSize, acc);
            //clock_gettime(CLOCK_REALTIME, &tm2);
            idbassert(i == 0 || i == readSize);
            idbassert(i % pageSize == 0);
            idbassert(acc % pageSize == 0);

            if (i < 0 && errno == EINTR)
            {
                timespec_sub(tm, tm2, tm3);
                cout << "* "
                     << i << " "
                     << right << setw(2) << setfill(' ') << tm3.tv_sec << "."
                     << right << setw(9) << setfill('0') << tm3.tv_nsec
                     << endl;
                continue;
            }
            else if (i < 0)
            {
                timespec_sub(tm, tm2, tm3);
                cout << "* i: "
                     << i << " sz: " << readSize << " acc: " << acc
                     << right << setw(2) << setfill(' ') << tm3.tv_sec << " "
                     << right << tm3.tv_nsec
                     << endl;
                perror("pread");
                //make loop exit
                i = 0;
            }

            acc += i;

            if (i > 0)
                rCnt++;

            //timespec_sub(tm, tm2, tm3);
            //cout
            //	<< i << " "
            //	<< right << setw(2) << setfill(' ') << tm3.tv_sec << " "
            //	<< right << tm3.tv_nsec
            //	<< endl;

        } // while(acc...

        clock_gettime(CLOCK_REALTIME, &endtm);
        timespec_sub(starttm, endtm, tottm);

        cout << "Total reads: " << rCnt
             << " sz: " << acc / (1024 * 1024) << "MB"
             << " tm: " << tottm.tv_sec << "secs "
             << tottm.tv_nsec << "ns"
             << endl;

        cout << "Repeat the last scan[Y,N]?" << endl;
        cin >> response;
        acc = 0;

    } // while response...

    close(fd);
    return 0;

} //main

