/***************************************************************************
**
**  This file is part of gpparam2model.
**
**  gpparam2model 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.
**
**  gpparam2model 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: 2007-07-06
**  Copyright: 2007-2019
**    Marc Wathelet
**    Marc Wathelet (LGIT, Grenoble, France)
**
***************************************************************************/

#include <DinverDCCore.h>

#include "gpparam2modelVersion.h"
#include "gpparam2modelInstallPath.h"
#include "ModelGenerator.h"

PACKAGE_INFO("gpparam2model", GPPARAM2MODEL)

ApplicationHelp * help();

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

  // Options
  enum Mode {HumanInfo, Random, Manual};
  Mode mode=Random;
  int nModels=1000;
  // Check arguments
  int i, j=1;
  for(i=1; i<argc; i++) {
    QByteArray arg=argv[i];
    if(arg[0]=='-') {
      if(arg=="-n") {
        CoreApplication::checkOptionArg(i, argc, argv);
        nModels=atoi(argv[i]);
      } else if(arg=="-m") {
        mode=Manual;
      } else if(arg=="-i") {
        mode=HumanInfo;
      } else {
        App::log(tr("gpparam2model: bad option %1, see --help\n").arg(argv[i]) );
        return 2;
      }
    } else {
      argv[j++]=argv[i];
    }
  }
  if(j < argc) {
    argv[j]=nullptr;
    argc=j;
  }

  if(argc<1) {
    App::log(tr("gpparam2mmodel: no parameter file specified, see --help\n") );
    return 2;
  }
  ParamGroundModel gm;
  XMLVirtualPlugin plugin(&gm, "DispersionCurve");
  XMLDinverHeader hdr(&plugin);
  if(hdr.xml_restoreFile(argv[1] )!=XMLClass::NoError) {
    App::log(tr("gpparam2mmodel: error loading parameterization file %1\n").arg(argv[1]) );
    return 2;
  }
  if(gm.isEmpty()) {
    App::log(tr("gpparam2mmodel: parametrized model is empty or at least one of its profiles is missing.\n") );
    return 2;
  }
  gm.initFinalProfiles();
  switch(mode) {
  case HumanInfo: {
      RealSpace ps;
      gm.toParameters(ps);
      ps.setVariableParameters();
      ps.humanInfo();
    }
    return 0;
  case Manual: {
      RealSpace ps;
      gm.toParameters(ps);
      ps.setVariableParameters();
      if(ps.variableParameterCount()==0) {
        gm.updateFinalProfiles();
        ParamProfile * vp=gm.find("Vp");
        ParamProfile * vs=gm.find("Vs");
        ParamProfile * rho=gm.find("Rho");
        Seismic1DModel * m =DCReportBlock::surfaceWaveModel(vp->resampledProfile().depths(),
                                                          vp->resampledProfile().values(),
                                                          vs->resampledProfile().values(),
                                                          rho->resampledProfile().values());
        QTextStream s(stdout);
        m->toStream(s);
        delete m;
        return 0;
      }
      int nLines=0;
      while(!feof(stdin)) {
        // Fill in parameter values with stdin
        QString l=File::readLine(true);
        nLines++;
        if(l.isEmpty() || l[0]=='#') continue;
        LineParser lp(l);
        bool ok=true;
        for(int i=0;i<ps.variableParameterCount();) {
          double v=lp.toDouble(i, ok);
          if(ok) {
            const Parameter * p=ps.variableParameter(i);
            double min, max;
            p->getRectangularLimits(min, max);
            if(v>=min && v<=max) {
              ps.variableParameter(i)->setRealValue(v);
              i++;
            } else {
              App::log(tr("gpparam2mmodel: parameter %1(%2) out of range [ %3, %4 ] at line %5\n")
                                  .arg(p->name()).arg(v).arg(min).arg(max).arg(nLines));
              return 2;
            }
          } else {
            App::log(tr("gpparam2mmodel: not enough parameters at line %1, %2 are required\n")
                                .arg(nLines).arg(ps.variableParameterCount()));
            return 2;
          }
        }
        if(!ps.isOkDebug()) {
          App::log(tr("gpparam2mmodel: parameter set does not satisfy all conditions at line %1\n").arg(nLines));
          return 2;
        }
        // Parameter set now ready for transformation into layered model
        gm.updateFinalProfiles();
        ParamProfile * vp=gm.find("Vp");
        ParamProfile * vs=gm.find("Vs" );
        ParamProfile * rho=gm.find("Rho");
        Seismic1DModel * m =DCReportBlock::surfaceWaveModel(vp->resampledProfile().depths(),
                                                          vp->resampledProfile().values(),
                                                          vs->resampledProfile().values(),
                                                          rho->resampledProfile().values());
        QTextStream s(stdout);
        s << QString("# Parameter set %1:").arg(nLines) << endl;
        m->toStream(s);
        delete m;
      }
    }
    break;
  case Random: {
      ModelGenerator m;
      if(!m.setParamSpace(&gm)) {
        App::log(tr("gpparam2model: error in initializationof parameter space.\n"));
        return 2;
      }
      m.parameterSpace().setVariableParameters();
      if(!m.parameterSpace().adjustRanges()) {
        App::log(tr("gpparam2model: error adjusting ranges\n"));
        return 2;
      }
      if(m.parameterSpace().variableParameterCount()==0) {
        App::log(tr("gpparam2model: no variable parameters(fixed parameters=%1).\n")
                               .arg(m.parameterSpace().allParameterCount()));
        bool ok;
        gm.updateFinalProfiles();
        m.misfit(ok);
        return 2;
      }
      ModelRepository na;
      na.setForward(&m);
      na.setStorage();
      na.setMaximumModelCount(nModels);
      na.start(1, Generator::MonteCarlo);
      na.wait();
    }
    break;
  }
  return 0;
}

ApplicationHelp * help()
{
  ApplicationHelp * h=new ApplicationHelp;
  h->setOptionSummary( "[options] FILE.param" );
  h->setComments( "Generate ground models from parameterization given by FILE.param. If option '-m' is selected "
                  "no random model is generated but parameter values are read from stdin. Each line must contain "
                  "the same number of columns as the number of parameters in parameter space defined by FILE.param. "
                  "Use option '-i' to get the number of parameters." );
  h->addGroup("Gpparam2model", "gpparam2model");
  h->addOption("-n","Number of random model to generate (if option -m not selected, default=1000).");
  h->addOption("-m","Read parameter values from stdin (one set of parameters per line). See option '-i' for "
                    "parameter list and count.");
  h->addOption("-i","Ouput human readable information about parameterized ground model.");
  return h;
}
