/***************************************************************************
**
**  This file is part of gprandom.
**
**  gprandom is free software: you can redistribute it and/or modify
**  it under the terms of the GNU General Public License as published by
**  the Free Software Foundation, either version 3 of the License, or
**  (at your option) any later version.
**
**  gprandom is distributed in the hope that it will be useful,
**  but WITHOUT ANY WARRANTY; without even the implied warranty of
**  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
**  GNU General Public License for more details.
**
**  You should have received a copy of the GNU General Public License
**  along with Foobar.  If not, see <http://www.gnu.org/licenses/>
**
**  See http://www.geopsy.org for more information.
**
**  Created: 2009-02-24
**  Copyright: 2009-2019
**    Marc Wathelet
**    Marc Wathelet (LGIT, Grenoble, France)
**
***************************************************************************/

#include <QGpCoreMath.h>

#include "gprandomVersion.h"
#include "gprandomInstallPath.h"

PACKAGE_INFO("gprandom", GPRANDOM)

ApplicationHelp * help();

int main(int argc, char ** argv)
{
  CoreApplication a(argc, argv, help);

  // Options
  QString cacheFile("random.cache");
  int seed=0;
  int generationCount=1;
  double min=0.0, max=1.0;
  enum Mode {Generate, TestPeriod, TestDistribution};
  Mode mode=Generate;
  double distributionMax=1.0;
  qint64 distributionReset=268435456;
  // Check arguments
  int i, j=1;
  for(i=1; i<argc; i++) {
    QByteArray arg=argv[i];
    if(arg[0]=='-') {
      if(arg=="-cache") {
        CoreApplication::checkOptionArg(i, argc, argv);
        cacheFile=argv[i];
      } else if(arg=="-seed") {
        CoreApplication::checkOptionArg(i, argc, argv);
        seed=atoi(argv[i]);
      } else if(arg=="-n") {
        CoreApplication::checkOptionArg(i, argc, argv);
        generationCount=atoi(argv[i] );
      } else if(arg=="-min") {
        CoreApplication::checkOptionArg(i, argc, argv);
        min=atof(argv[i] );
      } else if(arg=="-max") {
        CoreApplication::checkOptionArg(i, argc, argv);
        max=atof(argv[i] );
      } else if(arg=="-test-period") {
        mode=TestPeriod;
      } else if(arg=="-test-distribution") {
        mode=TestDistribution;
      } else if(arg=="-distribution-max") {
        CoreApplication::checkOptionArg(i, argc, argv);
        distributionMax=atof(argv[i]);
      } else if(arg=="-distribution-reset") {
        CoreApplication::checkOptionArg(i, argc, argv);
        distributionReset=atoi(argv[i]);
      } else {
        App::log(tr("gprandom: bad option %1, see -help\n").arg(argv[i]) );
        return 2;
      }
    } else {
      argv[j++]=argv[i];
    }
  }
  if(j < argc) {
    argv[j]=nullptr;
    argc=j;
  }


  switch (mode) {
  case TestPeriod: {
      Random engine(seed);
      engine.testPeriod();
      return 0;
    }
  case TestDistribution: {
      Random engine(seed);
      engine.testDistribution(distributionMax, distributionReset);
      return 0;
    }
  case Generate:
    Random * engine=nullptr;
    // Restoring state or intializing
    QFile f(cacheFile);
    if(f.open(QIODevice::ReadOnly)) {
      QByteArray buf=f.readAll();
      if(buf.size()==RANDOM_STATE_SIZE) {
        engine=new Random(buf);
      } else {
        App::log(tr("gprandom: bad format for cache file %1, intializing a new random generator.\n").arg(cacheFile) );
        engine=new Random(seed);
      }
      f.close();
    } else {
      engine=new Random(seed);
    }
    QTextStream sOut(stdout);
    for(int i=0; i<generationCount; i++) {
      sOut << engine->uniform(min, max) << " ";
    }
    sOut << endl;
    // Saving state
    if(f.open(QIODevice::WriteOnly)) {
      f.write(engine->state());
      f.close();
      delete engine;
      return 0;
    } else {
      App::log(tr("gprandom: cannot save cache file %1.\n").arg(cacheFile) );
      delete engine;
      return 2;
    }
  }
}

ApplicationHelp * help()
{
  TRACE;
  ApplicationHelp * h=new ApplicationHelp;
  h->setOptionSummary("[OPTIONS]");
  h->setComments("Compute random values with ran2 (uniform probability, support for lot of computations). To force initialization "
                 "of random generation remove the cache file (see option '-cache').");
  h->addGroup("gprandom", "gprandom");
  h->addOption("-n <N>","Generates N pseudo random numbers.");
  h->addOption("-min <MIN>","Minimum range for random number (default=0).");
  h->addOption("-max <MAX>","Maximum range for random number (default=1).");
  h->addOption("-cache <FILE>","Store cache value in FILE (default='random.cache').");
  h->addOption("-seed <SEED>","Init random generation with SEED (default=current time). This option is ignored "
                              "if a valid cache file is found.");
  h->addOption("-test-period","Calculate the return period of the pseudo random generator.");
  h->addOption("-test-distribution <RATIO>","Calculate the histogram of the pseudo random generator.");
  h->addOption("-distribution-max <MAX>", "MAX can vary from 0 to 1 to zoom only on a precise part of the histogram. "
                                          "1 means no zoom, display the entire historgram, 0.1 means display only the "
                                          "range from 0 to 0.1 (default=1.0).");
  h->addOption("-distribution-reset <N>", "Reset the histogram counters after generating N samples (default=268435456).");
  return h;
}
