#include <chrono>
#include <iomanip>
#include <iostream>
#include <string>
#include <vector>

#include "AIO_ExecDriver.h"
#include "ExecDriver.h"
#include "Utilities/BufferAllocator.h"
#include "Utilities/FileUtils.h"
#include "Utilities/SearchLynxConfiguration.h"

std::vector<std::string> UNITS{"b/sec", "KB/s", "MB/s", "GB/s", "TB/s"};

double printRate(double rawRate, uint32_t& unitIdx)
{
    uint32_t idx = 0;
    double rate = rawRate;
    while (rate > 1024)
    {
        rate /= 1024;
        ++idx;
    }
    
    unitIdx = idx;
    return rate;
}


int main(int argc, char** argv) 
{
    std::cout << "Read Test Utility\n" << std::endl;
    
    if (argc < 2)
    {
        std::cerr << "ERROR: please provide an input file" << std::endl;
        return -1;
    }

    // Start time measurement
    auto start = std::chrono::system_clock::now();
    
    // Prime the config object
    auto slConfig = BlackLynx::SearchLynx::SearchLynxConfiguration::getInstance();
    
    // Allocate buffers
    auto bufAllocator = BlackLynx::SearchLynx::BufferAllocator::getInstance();
    bufAllocator->initAllocation();
    
    // Read in the file
    std::string filename(argv[1]);
//    BlackLynx::SearchLynx::ExecDriver driver(filename, 22);
    BlackLynx::SearchLynx::AIO_ExecDriver driver(filename, 3);
    driver.start();
    driver.waitUntilComplete();
    
    // Deallocate buffers
    bufAllocator->deallocateBuffers();
    
    // End time measurement
    auto end = std::chrono::system_clock::now();
    auto diff = end - start;
    double duration = std::chrono::duration <double>(diff).count() ;
    std::cout << "Duration: "  << std::fixed << std::setprecision(3) 
              << duration << " ms" << std::endl;

    uint64_t fileSize = BlackLynx::SearchLynx::FileUtils::getFileSize(filename);
    
    double rawRate = fileSize / duration;
    std::cout << "Raw rate: "  << rawRate << " b/sec" << std::endl;
    
    uint32_t idx = 0;
    double rate = printRate(rawRate, idx);
    std::cout << "Rate:     "  << std::fixed << std::setprecision(3) << rate 
              << " " << UNITS[idx] << std::endl;
    
    return 0;
}
