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

#include <random>

#include <DinverCore.h>
#include <DinverDCCore.h>
#include <QGpCompatibility.h>
#include <QGpCoreTools.h>
#include <QGpCoreWave.h>
#include "gpdcreportVersion.h"
#include "gpdcreportInstallPath.h"

PACKAGE_INFO("gpdcreport", GPDCREPORT)

/* Compatibility converters

NaViewer      : old .report as output by the ancestor of dinver
BetaRelease   : first release of the new .report format (not yet support for implicit mode guess specification)
                All releases from year 2006 and end of 2005
Current       : .report produced by current releases of dinver (since beginning of year 2007)
                Versioning implemented in this format should guaranty the future support for this format
*/
enum InputType {NaViewer, BetaRelease, Current};

enum OutputType {Parameters, Count, BestCurve, Checksum, GroundModel, TiltGroundModelVp, TiltGroundModelVs, VpProfile,
                 VsProfile, RhoProfile, PitchProfile, ResistivityProfile,
                 DispersionRayleighPhase, DispersionRayleighGroup, DispersionLovePhase, DispersionLoveGroup,
                 SignedEllipticity, AbsoluteEllipticity,
                 AutocorrVertical, AutocorrRadial, AutocorrTransverse, Report, RefractionVp, RefractionVs};

static InputType inputType=Current;                                       // Input type selected by user
static OutputType outputType=GroundModel;                                 // Output type selected by user
static int modeIndex=0;                                                   // Mode index selected by user
static QList<int> ringIndexes;                                            // Ring indexes selected by user
static int sourceIndex=0;                                                 // Source index selected by user
static int iModel=0;                                                      // Number of models output so far
static ReportWriter * outputReport=nullptr;                               // Report to export models
static QList<DCModelInfo *> models;                                       // Temporary storage for models (option -best)
static QList<ReportReader *> reports;                                     // List of loaded reports
static QTextStream sOut(stdout);
static double currentBestMisfit=std::numeric_limits<double>::infinity();  // Used in BestCurve

ApplicationHelp * help();
void outputModel(QDataStream& s, int iReportModel, double misfit, ReportReader * report);
void outputDCModel(QDataStream& s, int iReportModel, double misfit, ReportReader * report);
void outputModel(double misfit, CompatInversionReport * report);

int cleanReturn(int value)
{
  foreach(DCModelInfo * model, models) ReportReader::removeReference(model);
  foreach(ReportReader * report, reports) ReportReader::removeReference(report);
  delete outputReport;
  return value;
}

/*void testGradient()
{
  PowerLawProfile plp;
  plp.setSubLayerCount(10);
  plp.setTopValue(200.0);
  plp.setTopDepth(10.0);
  plp.setBottomDepth(50.0);
  plp.setGradient(1.5);
  plp.setDepths();
  plp.setValues();
  plp.printDebug();
  //Random n(5);
  //for(int i=0; i<1000000; i++) {
  //  plp.setGradient(n.uniform(200.0, 3500.0), n.uniform(10.0, 50.0), 1.0);
  //}
}*/

int main(int argc, char ** argv)
{
  CoreApplication a(argc, argv, help);
  //testGradient();
  //return 0;

  // Options
  int nModels=INT_MAX;
  int bestModelCount=0;
  bool randomize=false;
  bool readIndexes=false;
  double maxMisfit=std::numeric_limits<double>::infinity();      // Maximum misfit selected by user
  // 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") {
        CoreApplication::checkOptionArg(i, argc, argv);
        maxMisfit=atof(argv[i])*(1.0+1e-15);
      } else if(arg=="-best") {
        randomize=false;
        CoreApplication::checkOptionArg(i, argc, argv);
        bestModelCount=atoi(argv[i]);
      } else if(arg=="-rand") {
        randomize=true;
        CoreApplication::checkOptionArg(i, argc, argv);
        bestModelCount=atoi(argv[i]);
#if __GNUC__ < 6 && ! defined(__clang__)
#error
        App::log(tr("gpdcreport: old compiler, randomize option not available.\n") );
        return cleanReturn(2);
#endif
      } else if(arg=="-best-curve") {
        outputType=BestCurve;
      } else if(arg=="-index") {
        readIndexes=true;
      } else if(arg=="-compat") {
        CoreApplication::checkOptionArg(i, argc, argv);
        if(strcmp(argv[i],"na_viewer")==0) {
          inputType=NaViewer;
        } else if(strcmp(argv[i],"beta")==0) {
          inputType=BetaRelease;
        } else if(strcmp(argv[i],"current")==0) {
          inputType=Current;
        } else {
          App::log(tr("gpdcreport: bad compatibility type, see -h.\n") );
          return cleanReturn(2);
        }
         // else do nothing for compatibility, read current .report format
      } else if(arg=="-o" || arg=="-report") {  // Kept for compatibility
        if(outputReport) {
          App::log(tr("gpdcreport: only one option '-o' is accepted.\n") );
          return cleanReturn(2);
        }
        CoreApplication::checkOptionArg(i, argc, argv);
        QFileInfo fi(argv[i]);
        if(fi.exists()) {
          App::log(tr("gpdcreport: report %1 already exists (option -o), remove it before.\n").arg(argv[i]) );
          return cleanReturn(2);
        }
        outputReport=new ReportWriter(fi.fileName());
        if(!outputReport->open()) {
          App::log(tr("gpdcreport: cannot open file %1 for writing.\n").arg(argv[i]) );
          return cleanReturn(2);
        }
        outputType=Report;
      } else if(arg=="-pm") {
        outputType=Parameters;
      } else if(arg=="-count") {
        outputType=Count;
      } else if(arg=="-checksum") {
        outputType=Checksum;
      } else if(arg=="-gm") {
        outputType=GroundModel;
      } else if(arg=="-tgmVp") {
        outputType=TiltGroundModelVp;
      } else if(arg=="-tgmVs") {
        outputType=TiltGroundModelVs;
      } else if(arg=="-vp") {
        outputType=VpProfile;
      } else if(arg=="-vs") {
        outputType=VsProfile;
      } else if(arg=="-rho") {
        outputType=RhoProfile;
      } else if(arg=="-pitch") {
        outputType=PitchProfile;
      } else if(arg=="-res") {
        outputType=ResistivityProfile;
      } else if(arg=="-pR") {
        outputType=DispersionRayleighPhase;
        CoreApplication::checkOptionArg(i, argc, argv);
        modeIndex=atoi(argv[i]);
      } else if(arg=="-gR") {
        outputType=DispersionRayleighGroup;
        CoreApplication::checkOptionArg(i, argc, argv);
        modeIndex=atoi(argv[i]);
      } else if(arg=="-pL") {
        outputType=DispersionLovePhase;
        CoreApplication::checkOptionArg(i, argc, argv);
        modeIndex=atoi(argv[i]);
      } else if(arg=="-gL") {
        outputType=DispersionLoveGroup;
        CoreApplication::checkOptionArg(i, argc, argv);
        modeIndex=atoi(argv[i]);
      } else if(arg=="-se") {
        outputType=SignedEllipticity;
        CoreApplication::checkOptionArg(i, argc, argv);
        modeIndex=atoi(argv[i]);
      } else if(arg=="-ae") {
        outputType=AbsoluteEllipticity;
        CoreApplication::checkOptionArg(i, argc, argv);
        modeIndex=atoi(argv[i]);
      } else if(arg=="-aV") {
        outputType=AutocorrVertical;
        CoreApplication::checkOptionArg(i, argc, argv);
        modeIndex=atoi(argv[i]);
      } else if(arg=="-aR") {
        outputType=AutocorrRadial;
        CoreApplication::checkOptionArg(i, argc, argv);
        modeIndex=atoi(argv[i]);
      } else if(arg=="-aT") {
        outputType=AutocorrTransverse;
        CoreApplication::checkOptionArg(i, argc, argv);
        modeIndex=atoi(argv[i]);
      } else if(arg=="-ring") {
        CoreApplication::checkOptionArg(i, argc, argv);
        ringIndexes.append(atoi(argv[i]));
      } else if(arg=="-refraVp") {
        outputType=RefractionVp;
        CoreApplication::checkOptionArg(i, argc, argv);
        sourceIndex=atoi(argv[i]);
      } else if(arg=="-refraVs") {
        outputType=RefractionVs;
        CoreApplication::checkOptionArg(i, argc, argv);
        sourceIndex=atoi(argv[i]);
      } else {
        App::log(tr("gpdcreport: bad option %1, see --help\n").arg(argv[i]) );
        return cleanReturn(2);
      }
    } else {
      argv[j++]=argv[i];
    }
  }
  if(j < argc) {
    argv[j]=nullptr;
    argc=j;
  }
  if(bestModelCount>0) {
    if(inputType==NaViewer) {
      App::log(tr("gpdcreport: option '-best' is not supported for old .report formats\n"
                                 "            First convert it to current .report format (option '-report')\n"));
      return cleanReturn(2);
    }
    if(readIndexes) {
      App::log(tr("gpdcreport: option '-best' is not compatible with option '-index'.\n"));
      return cleanReturn(2);
    }
  }
  // Initialize list of indexes, if requested
  QList<int> indexes;
  if(readIndexes) {
    while(!feof(stdin)) {
      QString l=File::readLine(true);
      if(!l.isEmpty() && l[0]!='#') {
        bool ok;
        int i=l.toInt(&ok);
        if(ok) {
          indexes << i;
        } else {
          App::log(tr("Bad model index %1\n").arg(l) );
        }
      }
    }
    App::log(tr("# Received %1 indexes\n").arg(indexes.count()) );
  }
  i=1;
  if(inputType==NaViewer) {
    while(i<argc) {
      CompatInversionReport * report=CompatReportFactory::open(argv[i]);
      if(!report->isValid()) {
        delete report;
        App::log(tr("gpdcreport: cannot open file %1 for reading.\n").arg(argv[i]) );
        return cleanReturn(2);
      }
      int nReportModels=report->countModels();
      sOut << "# "  << nReportModels << " " << argv[i] << endl;
      if(outputType==Count) {
        i++;
        continue;
      }
      if(indexes.isEmpty()) {
        for(int iReportModel=0 ; iReportModel<nReportModels; iReportModel++) {
          report->loadDispersion(iReportModel);
          double misfit=report->currentCost();
          if(misfit<=maxMisfit) {
            outputModel(misfit, report);
          }
          if(iModel>=nModels) {
            return cleanReturn(0);
          }
        }
      } else {
        for(QList<int>::iterator it=indexes.begin(); it!=indexes.end(); it++ ) {
          report->loadModel(*it);
          outputModel(report->currentCost(), report);
        }
      }
      delete report;
      i++;
    }
  } else {
    while(i<argc) {
      ReportReader * report=new ReportReader(argv[i]);
      report->addReference();
      reports << report;
      if(!report->open()) {
        App::log(tr("gpdcreport: cannot open file %1 for reading.\n").arg(argv[i]) );
        return cleanReturn(2);
      }
      report->synchronize();
      int nReportModels=report->nModels();
      sOut << "# "  << nReportModels << " " << argv[i] << endl;
      if(outputType==Count) {
        i++;
        continue;
      }
      QDataStream& s=report->stream();
      if(indexes.isEmpty()) {
        for(int iReportModel=0; iReportModel<nReportModels; iReportModel++) {
          double misfit=report->misfit(iReportModel);
          if(bestModelCount>0) {
            if(misfit<=maxMisfit) {
              DCModelInfo * info=new DCModelInfo;
              info->setReport(report);
              info->setIndexInReport(iReportModel);
              info->setMisfit(misfit);
              info->addReference();
              models << info;
            }
          } else {
            if(misfit<=maxMisfit) {
              outputModel(s, iReportModel, misfit, report);
            }
            if(iModel>=nModels) {
              return cleanReturn(0);
            }
          }
        }
      } else {
        for(QList<int>::iterator it=indexes.begin(); it!=indexes.end(); it++ ) {
          outputModel(s, *it, report->misfit(*it), report);
        }
      }
      i++;
    }
    if(bestModelCount>0) {
      if(randomize) {
#if __GNUC__ >= 6 || __clang__
        std::random_device rd;
        std::mt19937_64 g(rd()); // a random number engine based on Mersenne Twister algorithm
        std::shuffle(models.begin(), models.end(), g);
#endif
      } else {
        std::sort(models.begin(), models.end(), DCModelInfo::misfitLessThan);
      }
      int iStop=models.count()-bestModelCount;
      if(iStop<0) iStop=0;
      for(int i=models.count()-1; i>=iStop; i--) {
        DCModelInfo * info=models.at(i);
        ReportReader * report=info->report();
        QDataStream& s=report->stream();
        report->misfit(info->indexInReport());
        outputModel(s, info->indexInReport( ), info->misfit(), report);
      }
    }
  }
  return cleanReturn(0);
}

void outputModel(QDataStream& s, int iReportModel, double misfit, ReportReader * report)
{
  switch(outputType) {
  case Parameters: {
      int nd;
      uint cs;
      double val;
      s >> nd;
      s >> cs;
      sOut <<  iReportModel << " " << nd << " ";
      sOut.setRealNumberPrecision(20);
      for(int id=0 ; id<nd; id++ ) {
        s >> val;
        sOut << val << " ";
      }
      sOut << misfit << endl;
      iModel++;
    }
    break;
  case Checksum: {
      int nd;
      uint cs;
      s >> nd;
      s >> cs;
      sOut <<  iReportModel << " " << cs << "\n";
    }
    break;
  case BestCurve: {
      if(misfit<currentBestMisfit) {
        sOut <<  iReportModel << " " << misfit << "\n";
        currentBestMisfit=misfit;
      }
    }
    break;
  case Report: {
      int nParams;
      uint cs;
      double val;
      qint64 blockOffset=s.device()->pos();
      s >> nParams;
      s >> cs;
      Parameter ** params=new Parameter*[nParams];
      for(int id=0 ; id<nParams; id++ ) {
        s >> val;
        params[id]=new Parameter;
        params[id]->setRealValue(val);
      }
      outputReport->addModel(misfit, cs, nParams, params);
      for(int id=0; id<nParams; id++) {
        delete params[id];
      }
      delete [] params;
      s.device()->seek(blockOffset);
      switch(inputType) {
      case BetaRelease:
        DCReportBlock::writeBeta(outputReport, report);
        break;
      case Current:
        DCReportBlock::write(outputReport, report);
        break;
      case NaViewer: // Not possible here
        break;
      }
      iModel++;
    }
    break;
  default:
    outputDCModel(s, iReportModel, misfit, report);
    break;
  }
}

void outputDCModel(QDataStream& s, int iReportModel, double misfit, ReportReader * report)
{
  int reportDCVersion=report->userBlockVersion("DISP");
  if(reportDCVersion>=0) {
    DCReportBlock dcBlock(s);
    dcBlock.readProfiles(reportDCVersion);
    switch (outputType) {
    case VpProfile:
      if(dcBlock.vp()) {
        Profile p;
        p.readReport(s);
        sOut << QString("# Vp profile %1: value=%2").arg(iReportModel).arg(misfit) << endl;
        p.toStream(sOut, true);
        iModel++;
      }
      break;
    case VsProfile:
      if(dcBlock.vs()) {
        Profile p;
        p.readReport(s);
        sOut << QString("# Vs profile %1: value=%2").arg(iReportModel).arg(misfit) << endl;
        p.toStream(sOut, true);
        iModel++;
      }
      break;
    case RhoProfile:
      if(dcBlock.rho()) {
        Profile p;
        p.readReport(s);
        sOut << QString("# Rho profile %1: value=%2").arg(iReportModel).arg(misfit) << endl;
        p.toStream(sOut, true);
        iModel++;
      }
      break;
    case PitchProfile:
      if(dcBlock.pitch()) {
        Profile p;
        p.readReport(s);
        sOut << QString("# Pitch profile %1: value=%2").arg(iReportModel).arg(misfit) << endl;
        p.toStream(sOut, true);
        iModel++;
      }
      break;
    case ResistivityProfile:
      if(dcBlock.res()) {
        Profile p;
        p.readReport(s);
        sOut << QString("# Resistivity profile %1: value=%2").arg(iReportModel).arg(misfit) << endl;
        p.toStream(sOut, true);
        iModel++;
      }
      break;
    case GroundModel: {
        Seismic1DModel * m=dcBlock.readSeismicModel();
        if(!m) return;
        sOut << QString("# Layered model %1: value=%2").arg(iReportModel).arg(misfit) << endl;
        m->toStream(sOut);
        delete m;
        iModel++;
      }
      break;
     case TiltGroundModelVp: {
        Profile vp,pitch;
        if(dcBlock.vp()) vp.readReport(s); else return;
        if(dcBlock.pitch()) pitch.readReport(s); else return;
        RefractionFactory f;
        if(dcBlock.refractionVp()) f.readReport(dcBlock.stream()); else return;
        QVector<double> depths;
        depths << vp.depths();
        depths << pitch.depths();
        std::sort(depths.begin(), depths.end());
        unique(depths);
        vp.resample(depths);
        pitch.resample(depths);
        Seismic1DModel * m;
        m=DCReportBlock::vpModel(depths ,vp.values());
        RefractionDippingModel tm(m->layerCount());
        tm.setXLeft(f.xLeft());
        tm.setXRight(f.xRight());
        tm.fromSeismic1DModel( *m, pitch.values(), Seismic1DModel::P);
        sOut << QString("# Vp Tilt Layered model %1: value=%2").arg(iReportModel).arg(misfit) << endl;
        tm.toStream(sOut);
        delete m;
        iModel++;
      }
      break;
     case TiltGroundModelVs: {
        Profile vs,pitch;
        if(dcBlock.vs()) vs.readReport(s); else return;
        if(dcBlock.pitch()) pitch.readReport(s); else return;
        RefractionFactory f;
        if(dcBlock.refractionVs()) f.readReport(dcBlock.stream()); else return;
        QVector<double> depths;
        depths << vs.depths();
        depths << pitch.depths();
        std::sort(depths.begin(), depths.end());
        unique(depths);
        vs.resample(depths);
        pitch.resample(depths);
        Seismic1DModel * m;
        m=DCReportBlock::vsModel(depths ,vs.values());
        RefractionDippingModel tm(m->layerCount());
        tm.setXLeft(f.xLeft());
        tm.setXRight(f.xRight());
        tm.fromSeismic1DModel( *m, pitch.values(), Seismic1DModel::S);
        sOut << QString("# Vs Tilt Layered model %1: value=%2").arg(iReportModel).arg(misfit) << endl;
        tm.toStream(sOut);
        delete m;
        iModel++;
      }
      break;
   case DispersionRayleighPhase:
      if(dcBlock.dispersion()) {
        switch(inputType) {
        case BetaRelease: { /* Nothing else than phase Love and phase Rayleigh were available at that time
                               Rayleigh and Love was indicated by nRayleighModes */
            ModalStorageReader m(s); // Read number of modes
            m.setBetaReleaseOffsets(s, true); // Read number of Rayleigh modes
            if(m.seek(s, modeIndex)) {
              QTextStream sOut(stdout);
              sOut << QString("# Rayleigh Phase dispersion curve (mode=%1) %2: value=%3")
                             .arg(modeIndex).arg(iReportModel).arg(misfit) << endl;
              ModalStorageReader::toStream(s, sOut);
              iModel++;
            }
          }
          break;
        case Current:
          dcBlock.readModalStorages(4);
          if(dcBlock.modalStorage(0)) {
            ModalStorageReader m(s);
            if(m.seek(s, modeIndex)) {
              sOut << QString("# Rayleigh Phase dispersion curve (mode=%1) %2: value=%3")
                             .arg(modeIndex).arg(iReportModel).arg(misfit) << endl;
              ModalStorageReader::toStream(s, sOut);
              iModel++;
            }
          }
          break;
        case NaViewer: // Not possible here
          break;
        }
      }
      break;
    case DispersionRayleighGroup:
      if(dcBlock.dispersion()) {
        switch(inputType) {
        case Current:
          dcBlock.readModalStorages(4);
          if(dcBlock.modalStorage(1)) {
            ModalStorageReader m(s);
            if(m.seek(s, modeIndex)) {
              sOut << QString("# Rayleigh Group dispersion curve (mode=%1) %2: value=%3")
                             .arg(modeIndex).arg(iReportModel).arg(misfit) << endl;
              ModalStorageReader::toStream(s, sOut);
              iModel++;
            }
          }
          break;
        case BetaRelease: // Not possible here
        case NaViewer: // Not possible here
          break;
        }
      }
      break;
    case DispersionLovePhase:
      if(dcBlock.dispersion()) {
        switch(inputType) {
        case BetaRelease: { /* Nothing else than phase Love and phase Rayleigh were available at that time
                               Rayleigh and Love was indicated by nRayleighModes */
            ModalStorageReader m(s); // Read number of modes
            m.setBetaReleaseOffsets(s, false); // Read number of Rayleigh modes
            if(m.seek(s, modeIndex)) {
              sOut << QString("# Love Phase dispersion curve (mode=%1) %2: value=%3")
                             .arg(modeIndex).arg(iReportModel).arg(misfit) << endl;
              ModalStorageReader::toStream(s, sOut);
              iModel++;
            }
          }
          break;
        case Current:
          dcBlock.readModalStorages(4);
          if(dcBlock.modalStorage(2)) {
            ModalStorageReader m(s);
            if(m.seek(s, modeIndex)) {
              sOut << QString("# Love Phase dispersion curve (mode=%1) %2: value=%3")
                             .arg(modeIndex).arg(iReportModel).arg(misfit) << endl;
              ModalStorageReader::toStream(s, sOut);
              iModel++;
            }
          }
          break;
        case NaViewer: // Not possible here
          break;
        }
      }
      break;
    case DispersionLoveGroup:
      if(dcBlock.dispersion()) {
        switch(inputType) {
        case Current:
          dcBlock.readModalStorages(4);
          if(dcBlock.modalStorage(3)) {
            ModalStorageReader m(s);
            if(m.seek(s, modeIndex)) {
              sOut << QString("# Love Group dispersion curve (mode=%1) %2: value=%3")
                             .arg(modeIndex).arg(iReportModel).arg(misfit) << endl;
              ModalStorageReader::toStream(s, sOut);
              iModel++;
            }
          }
          break;
        case BetaRelease: // Not possible here
        case NaViewer:    // Not possible here
          ASSERT(false);
          break;
        }
      }
      break;
    case SignedEllipticity:
      if(dcBlock.ellipticity()) {
        dcBlock.readModalStorages(1);
        if(dcBlock.modalStorage(0)) {
          ModalStorageReader m(s);
          if(m.seek(s, modeIndex)) {
            sOut << QString("# Signed ellipticity curve (mode=%1) %2: value=%3")
                            .arg(modeIndex).arg(iReportModel).arg(misfit) << endl;
            ModalStorageReader::toStream(s, sOut);
            iModel++;
          }
        }
      }
      break;
    case AbsoluteEllipticity:
      if(dcBlock.ellipticity()) {
        dcBlock.readModalStorages(1);
        if(dcBlock.modalStorage(0)) {
          ModalStorageReader m(s);
          if(m.seek(s, modeIndex)) {
            sOut << QString("# Absolute ellipticity curve (mode=%1) %2: value=%3")
                            .arg(modeIndex).arg(iReportModel).arg(misfit) << endl;
            ModalStorageReader::toAbsoluteStream(s, sOut);
            iModel++;
          }
        }
      }
      break;
    case AutocorrVertical:
      if(dcBlock.autocorr()) {
        int nRings;
        s >> nRings;
        dcBlock.readModalStorages(nRings*3);
        for(QList<int>::iterator it=ringIndexes.begin();it!=ringIndexes.end();it++) {
          if(*it<nRings) {
            if(dcBlock.modalStorage(*it*3)) {
              ModalStorageReader m(s);
              if(m.seek(s,modeIndex)) {
                sOut << QString("# Vertical autocorrelation curve (ring=%1,mode=%2) %3: value=%4")
                                .arg(*it).arg(modeIndex).arg(iReportModel).arg(misfit) << endl;
                m.toStream(s, sOut);
                iModel++;
              }
            }
          }
        }
      }
      break;
    case AutocorrRadial:
      if(dcBlock.autocorr()) {
        int nRings;
        s >> nRings;
        dcBlock.readModalStorages(nRings*3);
        for(QList<int>::iterator it=ringIndexes.begin();it!=ringIndexes.end();it++) {
          if(*it<nRings) {
            if(dcBlock.modalStorage(*it*3+1)) {
              ModalStorageReader m(s);
              if(m.seek(s,modeIndex)) {
                sOut << QString("# Radial autocorrelation curve (ring=%1,mode=%2) %3: value=%4")
                                .arg(*it).arg(modeIndex).arg(iReportModel).arg(misfit) << endl;
                m.toStream(s, sOut);
                iModel++;
              }
            }
          }
        }
      }
      break;
    case AutocorrTransverse:
      if(dcBlock.autocorr()) {
        int nRings;
        s >> nRings;
        dcBlock.readModalStorages(nRings*3);
        for(QList<int>::iterator it=ringIndexes.begin();it!=ringIndexes.end();it++) {
          if(*it<nRings) {
            if(dcBlock.modalStorage(*it*3+2)) {
              ModalStorageReader m(s);
              if(m.seek(s,modeIndex)) {
                sOut << QString("# Transverse autocorrelation curve (ring=%1,mode=%2) %3: value=%4")
                                .arg(*it).arg(modeIndex).arg(iReportModel).arg(misfit) << endl;
                m.toStream(s, sOut);
                iModel++;
              }
            }
          }
        }
      }
      break;
    case RefractionVp:
      if(dcBlock.refractionVp() && inputType==Current) {
        RefractionFactory f;
        f.readReport(dcBlock.stream());
        if(sourceIndex<f.sourceCount()) {
          sOut << QString("# Vp refraction curve (source %1 at %2 m) %3: value=%4")
                          .arg(sourceIndex).arg(f.sources()[sourceIndex]).arg(iReportModel).arg(misfit) << endl;
          f.toStream(sourceIndex, sOut);
          iModel++;
        }
      }
      break;
    case RefractionVs:
      if(dcBlock.refractionVs() && inputType==Current) {
        RefractionFactory f;
        f.readReport(dcBlock.stream());
        if(sourceIndex<f.sourceCount()) {
          sOut << QString("# Vs refraction curve (source %1 at %2 m) %3: value=%4")
                          .arg(sourceIndex).arg(f.sources()[sourceIndex]).arg(iReportModel).arg(misfit) << endl;
          f.toStream(sourceIndex, sOut);
          iModel++;
        }
      }
      break;
    default:
      break;
    }
  }
}

void outputModel(double misfit, CompatInversionReport * report)
{
  if(outputType==Report) {
    // Old reports have not this var and all parameter list
    int nParams=report->dimension();
    Parameter ** params=new Parameter*[nParams];
    float * pm=report->currentNAModel();
    for(int id=0 ; id<nParams; id++ ) {
      params[id]=new Parameter;
      params[id]->setRealValue(Number::toDouble(pm[id]));
    }
    outputReport->addModel(misfit, 0, nParams, params);
    for(int id=0; id<nParams; id++) {
      delete params[id];
    }
    delete [] params;
    DCReportBlock::writeNaViewer(outputReport, report);
    iModel++;
  }
}

ApplicationHelp * help()
{
  ApplicationHelp * h=new ApplicationHelp;
  h->setOptionSummary( "[OPTIONS] [FILE] ..." );
  h->setComments( "Read .report file produced by inversion and extract to stdout ground models, "
                 "dispersion curves, spac curves... (see options)\n");
  h->addGroup("Gpdcreport","gpdcreport");
  h->addOption("-n <MAX>","Maximum number of models to output (default=no maximum)");
  h->addOption("-m <MAX>","Maximum misfit to output (default=no maximum");
  h->addOption("-best <N>","Output only the N models with the minimum misfit (supported only for current .report format and without "
                       "option '-index'.");
  h->addOption("-rand <N>","Output only N random models (supported only for current .report format and without "
                       "option '-index'.");
  h->addOption("-best-curve","Output best misfit as a function of model index.");
  h->addOption("-index","Select models by index in file. Indexes are passed through stdin.");
  h->addOption("-pm","Output parameters and misfit to stdout");
  h->addOption("-checksum","Output the parameterization checksum to stdout");
  h->addOption("-count","Output the number of models to stdout");
  h->addOption("-gm","Output ground models to stdout (default)");
  h->addOption("-tgmVp","Output Vp tilt ground models to stdout (used for refraction)");
  h->addOption("-tgmVs","Output Vs tilt ground models to stdout (used for refraction)");
  h->addOption("-vp","Output Vp profiles to stdout");
  h->addOption("-vs","Output Vs profiles to stdout");
  h->addOption("-rho","Output Density profiles to stdout");
  h->addOption("-pitch","Output Pitch profiles to stdout");
  h->addOption("-res","Output Resistivity profiles to stdout");
  h->addOption("-pR <mode>","Output mode Rayleigh Phase dispersion curves to stdout");
  h->addOption("-gR <mode>","Output mode Rayleigh Group dispersion curves to stdout");
  h->addOption("-pL <mode>","Output mode Love Phase dispersion curves to stdout");
  h->addOption("-gL <mode>","Output mode Love Group dispersion curves to stdout");
  h->addOption("-se <mode>","Output mode signed ellipticity curve to stdout");
  h->addOption("-ae <mode>","Output mode absolute ellipticity curve to stdout");
  h->addOption("-aV <mode>","Output mode vertical autocorrelation curve to stdout");
  h->addOption("-aR <mode>","Output mode radial autocorrelation curve to stdout");
  h->addOption("-aT <mode>","Output mode transverse autocorrelation curve to stdout");
  h->addOption("-refraVp <source>","Output Vp travel time curve for source index to stdout");
  h->addOption("-refraVs <source>","Output Vs travel time curve for source index to stdout");
  h->addOption("-ring <index>","Add one ring to list of rings to output (default=nothing)");
  h->addOption("-o <FILE>","Export selected models to FILE with the current binary format for "
                           ".report files. This option may be usefull to convert old .report "
                           "files produced by na_viewer or first dinver releases.");
  h->addOption("-compat <TYPE>","Compatibility with previous versions of .report format. TYPE may take the "
                                "following values:\n"
                                "  na_viewer   old .report as output by the ancestor of dinver (before 2005).\n"
                                "  beta        first release of the new .report format (not yet support "
                                              "for implicit mode guess specification). "
                                              "All releases from year 2006 and end of 2005.\n"
                                "  current     .report produced by current releases of dinver. "
                                              "All releases since beginning of year 2007."
                                              "Versioning implemented in this format should guarantee the "
                                              "future support for this format.");
  h->addExample("gpdcreport run_01.report", "Output all ground models.");
  h->addExample("gpdcreport run_01.report -n 1000 -m 1", "Output the first 1000 ground models with a misfit less than 1.");
  h->addExample("gpdcreport run_01.report -n 1000 -m 1 | gpell -c", "Re-compute ellipticity curves for the same set of models.");
  h->addExample("gpdcreport run_01.report -n 1000 -m 1 | gpell -p", "Re-compute peak ellipticity frequencies for the same set of models.");
  h->addExample("gpdcreport run_01.report -n 1000 -m 1 -e 0", "Output ellipticity curves for the same set of models (if any stored).");
  return h;
}
