//*************************************************************************
//
//  Cacher (Tool) - C++ language
//
//  Cacher.cpp
//
//  Developed by Armando Giuseppe BONATTO MINELLA.
//
//*************************************************************************
#include <QtCore>
#include <cstdlib>
#include <iostream>
#include <time.h>
//*************************************************************************
#define BYTE_PER_GIBIBYTE 1073741824ull
#define GIBIBYTES_MIN              1ull
#define GIBIBYTES_MAX             40ull
//*************************************************************************
typedef enum : std::int32_t
{
  Executable     = 0,
  SourceFilename = 1,
  GibiBytes      = 2
} Arguments_t;
//*************************************************************************
std::int32_t main (std::int32_t   argc   ,
                   char         * argv [] )
{
  bool valid = false;
  //-------------------------------------
  if (!((argc                                                            >  Arguments_t::GibiBytes)                                                      &&
        QFile::exists (QString (argv [Arguments_t::SourceFilename]))                                                                                     &&
        (QString (argv [Arguments_t::GibiBytes]).toULongLong (& (valid)) >= GIBIBYTES_MIN)                                                               &&
        valid                                                                                                                                            &&
        (QString (argv [Arguments_t::GibiBytes]).toULongLong (& (valid)) <= GIBIBYTES_MAX)                                                               &&
        valid                                                                                                                                            &&
        (QFile (QString (argv [Arguments_t::SourceFilename])).size ()    >= ((std::int64_t) (QString (argv [Arguments_t::GibiBytes]).toULongLong () *
                                                                                             BYTE_PER_GIBIBYTE                                       )))   ))
  {
    std::cout << ""                                                                                                                                                         << std::endl
              << "WARNING: invalid command line arguments"                                                                                                                  << std::endl
              << ""                                                                                                                                                         << std::endl
              << "Usage: Cacher <Source filename> <GibiBytes>"                                                                                                              << std::endl
              << "       +--> GibiBytes"                                                                                                                                    << std::endl
              << "            +--> From " << QString::asprintf ("%'llu",GIBIBYTES_MIN).toStdString () << " to " << QString::asprintf ("%'llu",GIBIBYTES_MAX).toStdString () << std::endl
              << ""                                                                                                                                                         << std::endl;
  }
  else
  {
    QString         sourceFilename = QString (argv [Arguments_t::SourceFilename])          ;
    std::uint64_t   gibiBytes      = QString (argv [Arguments_t::GibiBytes]).toULongLong ();
    std::uint64_t * buffer         = (std::uint64_t *) std::malloc (BYTE_PER_GIBIBYTE)     ;
    //-------------------------------------
    if (buffer != nullptr)
    {
      QFile sourceFile (sourceFilename);
      //-------------------------------------
      if (sourceFile.open (QIODeviceBase::ReadOnly))
      {
        timespec      timeStart = { 0,0 }              ;
        timespec      timeStop  = { 0,0 }              ;
        std::uint64_t size      = (gibiBytes         *
                                   BYTE_PER_GIBIBYTE  );
        //-------------------------------------
        clock_gettime (CLOCK_PROCESS_CPUTIME_ID,
                       (& (timeStart))          );
        for (std::uint64_t gibiByte = 0ull;(gibiByte < gibiBytes);gibiByte += 1ull)
        {
          sourceFile.read (((char *) buffer)                 ,
                           ((std::int64_t) BYTE_PER_GIBIBYTE) );
        }
        clock_gettime (CLOCK_PROCESS_CPUTIME_ID,
                       (& (timeStop))           );
        //-------------------------------------
        std::uint64_t timeElapsed = (((((std::uint64_t) timeStop.tv_sec) *
                                       1000000000ull                      ) +
                                      ((std::uint64_t) timeStop.tv_nsec)     )  -
                                     ((((std::uint64_t) timeStart.tv_sec) *
                                       1000000000ull                       ) +
                                      ((std::uint64_t) timeStart.tv_nsec)     )  );
        //-------------------------------------
        std::cout << ""                                                                                                                   << std::endl
                  << "Caching completed: " << QString::asprintf ("%'llu",((unsigned long long) size)).toStdString () << " bytes read in "
                                           << QString::asprintf ("%'.9f",(((double) timeElapsed) /
                                                                          1000000000.0            )).toStdString ()  << " seconds"        << std::endl;
        sourceFile.close ();
      }
      else
      {
        std::cout << "ERROR: cannot open source file ('" << sourceFilename.toStdString () << "')" << std::endl;
        std::free ((void *) buffer);
        return (EXIT_FAILURE);
      }
      std::free ((void *) buffer);
    }
    else
    {
      std::cout << "ERROR: cannot allocate buffer" << std::endl;
      return (EXIT_FAILURE);
    }
    std::cout << std::endl;
  }
  return (EXIT_SUCCESS);
}
//*************************************************************************

