/***************************************************************************
**
**  This file is part of max2curve.
**
**  This file may be distributed and/or modified under the terms of the
**  GNU General Public License version 2 or 3 as published by the Free
**  Software Foundation and appearing in the file LICENSE.GPL included
**  in the packaging of this file.
**
**  This file 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 this program. If not, see <http://www.gnu.org/licenses/>.
**
**  See http://www.geopsy.org for more information.
**
**  Created : 2006-01-13
**  Authors:
**    Marc Wathelet
**    Marc Wathelet (LGIT, Grenoble, France)
**
***************************************************************************/

#include <QGpGuiWave.h>

#include "max2curveVersion.h"
#include "max2curveInstallPath.h"
#include "HistogramWidget.h"

PACKAGE_INFO("max2curve", MAX2CURVE)

ApplicationHelp * help();
void filterValues(HistogramWidget * w);
void filterGrid(HistogramWidget * w);

bool scaleTypeForced=false;
SamplingOption scaleType;
double tmin=0.0, tmax=0.0;
double tfaNppm=-1.0, tfaFactor=-1.0, tfaAmpZ=-1.0, tfaAmpH=-1.0, tfaDelay=std::numeric_limits<double>::infinity();
int tfaNppf=-1;
int filterCurve=-1;
double filterCurveFactor=1.0;
QString curveParser;
QStringList loadFileNames;
ExportOptions exportOptions;

int main(int argc, char ** argv)
{
  Application a(argc, argv, help);
  SciFigsGlobal s;

  // Options
  QString maxFile;
  QString fileType;
  enum Mode {Gui, Mean, Median, Max, Plot};
  Mode mode=Gui;
  double minValue=0.0, maxValue=0.0;
  int nClasses=200;
  // Check arguments
  int i, j=1;
  for(i=1; i<argc; i++) {
    QByteArray arg=argv[i];
    if(arg[0]=='-') {
      if(arg=="-type") {
        CoreApplication::checkOptionArg(i, argc, argv);
        fileType=argv[i];
      } else if(arg=="-min") {
        CoreApplication::checkOptionArg(i, argc, argv);
        minValue=atof(argv[i]);
      } else if(arg=="-max") {
        CoreApplication::checkOptionArg(i, argc, argv);
        maxValue=atof(argv[i]);
      } else if(arg=="-n") {
        CoreApplication::checkOptionArg(i, argc, argv);
        nClasses=atoi(argv[i] );
      } else if(arg=="-mean") {
        mode=Mean;
      } else if(arg=="-median") {
        mode=Median;
      } else if(arg=="-m") {
        mode=Max;
      } else if(arg=="-linear") {
        scaleTypeForced=true;
        scaleType=LinearScale;
      } else if(arg=="-log") {
        scaleTypeForced=true;
        scaleType=LogScale;
      } else if(arg=="-tmin") {
         CoreApplication::checkOptionArg(i, argc, argv);
         tmin=atof(argv[i]);
      } else if(arg=="-tmax") {
         CoreApplication::checkOptionArg(i, argc, argv);
         tmax=atof(argv[i]);
      } else if(arg=="-curve-parser") {
        CoreApplication::checkOptionArg(i, argc, argv);
        curveParser=argv[i];
      } else if(arg=="-load-curve") {
        CoreApplication::checkOptionArg(i, argc, argv);
        loadFileNames.append(argv[i]);
      } else if(arg=="-filter-curve") {
        if(CoreApplication::checkOptionArg(i, argc, argv, false)) {
          filterCurve=atoi(argv[i]);
          if(filterCurve<0) {
            App::log(tr("max2curve: filter-curve index must by greater or equal to 0\n") );
            return 2;
          }
        } else {
          filterCurve=0;
        }
      } else if(arg=="-filter-curve-factor") {
        CoreApplication::checkOptionArg(i, argc, argv);
        filterCurveFactor=atof(argv[i]);
      } else if(arg=="-tfaNppm") {
        CoreApplication::checkOptionArg(i, argc, argv);
        tfaNppm=atof(argv[i] );
      } else if(arg=="-tfaNppf") {
        CoreApplication::checkOptionArg(i, argc, argv);
        tfaNppf=atoi(argv[i] );
      } else if(arg=="-tfaFactor") {
        CoreApplication::checkOptionArg(i, argc, argv);
        tfaFactor=atof(argv[i] );
      } else if(arg=="-tfaAmpZ") {
        CoreApplication::checkOptionArg(i, argc, argv);
        tfaAmpZ=atof(argv[i] );
      } else if(arg=="-tfaAmpH") {
        CoreApplication::checkOptionArg(i, argc, argv);
        tfaAmpH=atof(argv[i] );
      } else if(arg=="-tfaDelay") {
        CoreApplication::checkOptionArg(i, argc, argv);
        tfaDelay=atof(argv[i] );
      } else {
        argv[j++]=argv[i];
      }
    } else {
      argv[j++]=argv[i];
    }
  }
  if(!exportOptions.read(argc, argv)) {
    return 2;
  }
  if(j<argc) {
    argv[j]=0;
    argc=j;
  }
  if(!CoreApplication::checkRemainingArgs(argc, argv)) {
    return 2;
  }
  if(argc>1) {
    maxFile=argv[1];
  }

  // Check options  
  if(!exportOptions.exportFile().isEmpty()) {
    mode=Plot;
  }
  if(mode!=Gui) {
    a.setConsoleMessage();
  }

  if(maxFile.isEmpty()) {
    maxFile=Message::getOpenFileName(tr("Loading max file"),
                                     tr("max file (*.max)"));
    if(maxFile.isEmpty()) return 2;
  }
  MaxEntryList * maxList=new MaxEntryList;
  if(!fileType.isEmpty()) {
    if(fileType=="FK") maxList->setType(MaxEntryList::FK);
    else if(fileType=="SPAC") maxList->setType(MaxEntryList::SPAC);
    else if(fileType=="TFA") maxList->setType(MaxEntryList::TFA);
    else if(fileType=="CURVE") maxList->setType(MaxEntryList::CURVE);
  }
  if(!maxList->load(maxFile)) {
    delete maxList;
    return 2;
  }
  if(maxList->isEmpty()) {
    Message::warning(MSG_ID, tr("Loading max file"),
                         tr("No max result can be loaded"));
    delete maxList;
    return 2;
  }

  HistogramWidget * w=new HistogramWidget(maxList);
  w->setWindowTitle(QString("max2curve - %2 - %1").arg(maxFile));
  // %2 is reserved for other comments: Type,...

  if(!loadFileNames.isEmpty()) {
    for(QStringList::iterator it=loadFileNames.begin(); it!=loadFileNames.end(); it++) {
      w->loadCurves(*it, curveParser);
    }
  }

  int appReturn=0;
  if(mode==Gui) {
    filterValues(w);
    if(!w->select(scaleTypeForced, scaleType) ) {
      delete w;
      return 2;
    }
    filterGrid(w);
    fprintf(stderr,"Filtering: remaining %i entries\n",w->maxEntryList()->count());
    // exportFile is empty, the next function won't export any file but it will
    // use makeup and other information
    w->exportPlot(exportOptions);
    w->show();
    appReturn=a.exec();
  } else {
    filterValues(w);
    if(minValue>=maxValue) {
      App::log(tr("Minimum and maximum are equal. These values must be defined by "
                  "options -min and -max, see -help for details.\n"));
      delete w;
      return 2;
    }
    w->initGrid(nClasses, minValue, maxValue, scaleType);
    filterGrid(w);
    fprintf(stderr,"Filtering: remaining %i entries\n",w->maxEntryList()->count());
    if(w->maxEntryList()->isEmpty()) {
      fprintf(stderr,"No entries, abort\n");
      delete w;
      return 2;
    }
    w->fillGrid();
    // User output mode
    QTextStream sOut(stdout);
    switch (mode) {
    case Max:
      w->maxEntryList()->toStream(sOut);
      break;
    case Mean:
      w->addMeanCurve();
      sOut << QString("# Mean curve from file %1\n").arg(maxFile);
      w->toStream(sOut, -1);
      break;
    case Median:
      w->addMedianCurve();
      sOut << QString("# Median curve from file %1\n").arg(maxFile);
      w->toStream(sOut, -1);
    case Plot:
      w->exportPlot(exportOptions);
      break;
    case Gui:
      break;
    }
  }
  delete w;
  return appReturn;
}

void filterValues(HistogramWidget * w)
{
  TRACE;
  MaxEntryList * entryList=w->maxEntryList();
  // User selection
  switch (entryList->type()) {
  case MaxEntryList::FK:
  case MaxEntryList::SPAC:
  case MaxEntryList::CURVE:
    if(!scaleTypeForced) scaleType=LinearScale;
    break;
  case MaxEntryList::TFA:
    if(tfaDelay!=std::numeric_limits<double>::infinity()) entryList->rejectDelay(tfaDelay);
    if(tfaFactor > -1.0) entryList->rejectAmplitudeVerticalFactor(tfaFactor);
    if(tfaAmpZ > -1.0) entryList->rejectAmplitudeVerticalAbsolute(tfaAmpZ);
    if(tfaAmpH > -1.0) entryList->rejectAmplitudeHorizontalAbsolute(tfaAmpH);
    if(tfaNppm > -1.0) entryList->rejectNPeaksPerMinute(tfaNppm);
    if(tfaNppf > -1.0) entryList->rejectNPeaksPerFrequency(tfaNppf);
    if(!scaleTypeForced) scaleType=LogScale;
    break;
  case MaxEntryList::Undefined:
    break;
  }
  if(tmin<tmax) {
    entryList->rejectTime(tmin,tmax);
  }
}

void filterGrid(HistogramWidget * w)
{
  TRACE;
  if(filterCurve>=0) {
    w->gridFilterCurve(filterCurve, filterCurveFactor);
  }
}

ApplicationHelp * help()
{
  ApplicationHelp * h=new ApplicationHelp;
  h->setOptionSummary( "[OPTIONS] file.max" );
  h->setComments("Filter and calculate statistics for .max files (e.g. generated by tfa).\n"
                 "This is an obsolete tool. It should not be used with recent .max files generated by fk."
                 "Only .max files generated by hvtfa and old .max files can be processed.");
  h->addGroup("Max2curve", "max2curve");
  h->addOption( "-type <TYPE>", "Force file type to TYPE which can be FK, SPAC, TFA, CURVE. If not specified, "
               "the type is automatically deduced from the comment line describing the list of fields." );
  h->addOption("-mean", "Output mean curve and exit." );
  h->addOption("-median", "Output median curve and exit." );
  h->addOption("-m", "Output selected max entries and exit." );
  h->addOption("-min <MIN>", "Minimum value for histogram classes (mandatory option for non-gui modes)." );
  h->addOption("-max <MAX>", "Maximum value for histogram classes (mandatory option for non-gui modes)." );
  h->addOption("-n <N>", "Number of histogram classes (default=200)." );
  h->addOption("-linear", "Force linear scale." );
  h->addOption("-log", "Force log scale." );
  h->addOption("-tmin <T>", "Selects only samples after time T (in seconds)");
  h->addOption("-tmax <T>", "Selects only samples before time T (in seconds)");
  h->addOption("-curve-parser <FILE>", "Defines the parser for importing curves (default=varies according to '-type')");
  h->addOption("-load-curve <FILE>", "Loads curves from FILE. The curve are imported with the parser specified with option '-curve-parser'.");
  h->addOption("-filter-curve [<INDEX>]", "Selects only samples falling inside FACTOR*standard deviation of the curve INDEX. "
                                       "If INDEX is omitted, the fisrt curve (INDEX=0) is used.");
  h->addOption("-filter-curve-factor <FACTOR>", "Factor of standard deviation for option '-filter-curve' (default=1).");
  exportOptions.help(h);
  h->addGroup("FK: statistics of fk results calculated frequency by frequency. The header line must contain:\n"
              "       # seconds from start | cfreq | slow | az | math-phi | semblance | beampow\n","fk");
  h->addOption( "-wavelength", "Use wavelength rather than frequencies for X axis." );
  h->addGroup("SPAC: statistics of spac results calculated frequency by frequency. The header line must contain:\n"
              "       # seconds from start | cfreq | icomp | iring | autocorr\n","spac");
  h->addGroup("CURVE: statistics of a collection of curves. The header line must contain:\n"
              "       # x | y\n","curve");
  h->addGroup("TFA: statistics of spac results calculated frequency by frequency. The header line must contain:\n"
              "       # seconds from start | cfreq | H/V | AmpZ | AmpH | Delay\n ","tfa");
  h->addOption( "-tfaFactor <LEVEL>", "Select all vertical amplitudes above LEVEL relative to maximum." );
  h->addOption( "-tfaAmpZ <LEVEL>", "Select all vertical amplitudes above LEVEL." );
  h->addOption( "-tfaAmpH <LEVEL>", "Select all horizontal amplitudes above LEVEL." );
  h->addOption( "-tfaNppf <N>", "Select a maximum of N vertical peaks per frequency. N is an integer." );
  h->addOption( "-tfaNppm <N>", "Select a maximum of N vertical peaks per minute and per frequency. N is not necessarely an integer." );
  h->addOption( "-tfaDelay <D>", "Select only entries with a delay D between vertical and horizontal." );
  return h;
}
