/***************************************************************************
**
**  This file is part of QGpCoreStat.
**
**  QGpCoreStat 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.
**
**  QGpCoreStat 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: 2008-12-17
**  Copyright: 2008-2019
**    Marc Wathelet
**    Marc Wathelet (LGIT, Grenoble, France)
**
***************************************************************************/

#include <QGpCoreTools.h>

#include "HistogramReader.h"
#include "SampleFilter.h"

namespace QGpCoreStat {

  /*!
    \class HistogramReader HistogramReader.h
    \brief Brief description of class still missing

    Full description of class still missing
  */

  /*!
    Description of constructor still missing
  */
  HistogramReader::HistogramReader()
      : ArgumentStdinReader()
  {
    TRACE;
    _ignoreStdin=true;
    _action=Gui;
    _xSampling=nullptr;
    _xLogSampling=false;
    _ySampling.setCount(200);
    _yFactor=1.0;
    _manual.flags=0;
    _xColumn=0;
    _yColumn=1;
    _validColumn=-1;
    _maxVariables=SlownessFrequency;
    _powerColumn=-1;
    _relativePowerThreshold=0.0;
    _noiseThreshold=std::numeric_limits<double>::infinity();
    _ignoreValid=false;
    _normalize=true;
    _xScaleType=Scale::Linear;
    _yScaleType=Scale::Linear;
  }

  HistogramReader::~HistogramReader()
  {
    TRACE;
    delete _xSampling;
  }

  bool HistogramReader::setOptions(int& argc, char ** argv)
  {
    TRACE;
    // Check arguments
    int i, j=1;
    for(i=1; i<argc; i++) {
      QByteArray arg=argv[i];
      if(arg[0]=='-') {
        if(arg=="-x-column") {
          CoreApplication::checkOptionArg(i, argc, argv);
          _xColumn=atoi(argv[i]);
          _manual.x.column=true;
        } else if(arg=="-x-sampling") {
          CoreApplication::checkOptionArg(i, argc, argv);
          if(!_xSampling) {
            _xSampling=new SamplingParameters;
          }
          if(strcmp(argv[i],"inversed")==0) {
            _xSampling->setInversed(true);
            _xLogSampling=false;
          } else if(strcmp(argv[i],"log")==0) {
            _xSampling->setType(SamplingParameters::Log);
            _xLogSampling=true;
          } else if(strcmp(argv[i],"asis")==0) {
            delete _xSampling;
            _xSampling=nullptr;
            _manual.xValuesAsIs=true;
          } else {
            _xSampling->setType(SamplingParameters::Linear);
            _xLogSampling=false;
          }
          _manual.x.gridScaleType=true;
        } else if(arg=="-x-min") {
          CoreApplication::checkOptionArg(i, argc, argv);
          if(!_xSampling) {
            _xSampling=new SamplingParameters;
          }
          _xSampling->setMinimum(atof(argv[i]));
          _manual.x.minimum=true;
        } else if(arg=="-x-max") {
          CoreApplication::checkOptionArg(i, argc, argv);
          if(!_xSampling) {
            _xSampling=new SamplingParameters;
          }
          _xSampling->setMaximum(atof(argv[i]));
          _manual.x.maximum=true;
        } else if(arg=="-x-count") {
          CoreApplication::checkOptionArg(i, argc, argv);
          if(!_xSampling) {
            _xSampling=new SamplingParameters;
          }
          _xSampling->setCount(atoi(argv[i]));
          _manual.x.count=true;
          if(_xSampling->count()<=0) {
            App::log(tr("negative or null number of X samples (option -x-count)\n") );
            return false;
          }
        } else if(arg=="-x-title") {
          CoreApplication::checkOptionArg(i, argc, argv);
          _xTitle=argv[i];
        } else if(arg=="-y-column") {
          CoreApplication::checkOptionArg(i, argc, argv);
          _yColumn=atoi(argv[i]);
          _manual.y.column=true;
        } else if(arg=="-y-sampling") {
          CoreApplication::checkOptionArg(i, argc, argv);
          if(strcmp(argv[i],"inversed")==0) {
            _ySampling.setInversed(true);
          } else if(strcmp(argv[i],"log")==0) {
            _ySampling.setType(SamplingParameters::Log);
          } else {
            _ySampling.setType(SamplingParameters::Linear);
          }
          _manual.y.gridScaleType=true;
        } else if(arg=="-no-normalize") {
          _normalize=false;
        } else if(arg=="-y-min") {
          CoreApplication::checkOptionArg(i, argc, argv);
          _ySampling.setMinimum(atof(argv[i]));
          _manual.y.minimum=true;
        } else if(arg=="-y-max") {
          CoreApplication::checkOptionArg(i, argc, argv);
          _ySampling.setMaximum(atof(argv[i]));
          _manual.y.maximum=true;
        } else if(arg=="-y-count") {
          CoreApplication::checkOptionArg(i, argc, argv);
          _ySampling.setCount(atoi(argv[i]));
          _manual.y.count=true;
          if(_ySampling.count()<=0) {
            App::log(tr("negative or null number of Y samples (option -y-count)\n") );
            return false;
          }
        } else if(arg=="-y-factor") {
          CoreApplication::checkOptionArg(i, argc, argv);
          _yFactor=atof(argv[i]);
        } else if(arg=="-y-title") {
          CoreApplication::checkOptionArg(i, argc, argv);
          _yTitle=argv[i];
        } else if(arg=="-valid-column") {
          CoreApplication::checkOptionArg(i, argc, argv);
          _validColumn=atoi(argv[i]);
          _manual.validColumn=true;
        } else if(arg=="-grid") {
          _action=Grid;
        } else if(arg=="-mean") {
          _action=Mean;
        } else if(arg=="-median") {
          _action=Median;
        } else if(arg=="-mode") {
          _action=Mode;
        } else if(arg=="-gauss-mix") {
          _action=GaussMix;
        } else if(arg=="-plot") {
          _action=Plot;
        } else if(arg=="-max") {
          CoreApplication::checkOptionArg(i, argc, argv);
          _exportMaxFileName=argv[i];
          _action=Max;
        } else if(arg=="-pattern" || arg=="-p") {
          CoreApplication::checkOptionArg(i, argc, argv);
          _pattern=argv[i];
        } else if(arg=="-ignore-valid") {
          _ignoreValid=true;
        } else if(arg=="-rel-power") {
          CoreApplication::checkOptionArg(i, argc, argv);
          _relativePowerThreshold=atof(argv[i]);
        } else if(arg=="-max-noise") {
          CoreApplication::checkOptionArg(i, argc, argv);
          _noiseThreshold=atof(argv[i]);
        } else if(arg=="-gm-type") {
          CoreApplication::checkOptionArg(i, argc, argv);
          _gaussMix.setInversionType(argv[i]);
        } else if(arg=="-gm-count") {
          CoreApplication::checkOptionArg(i, argc, argv);
          _gaussMix.setModeCount(atoi(argv[i]));
        } else if(arg=="-gm-kmin") {
          CoreApplication::checkOptionArg(i, argc, argv);
          _gaussMix.setKmin(atof(argv[i]));
        } else if(arg=="-gm-kmax") {
          CoreApplication::checkOptionArg(i, argc, argv);
          _gaussMix.setKmax(atof(argv[i]));
        } else if(arg=="-gm-relerr") {
          CoreApplication::checkOptionArg(i, argc, argv);
          _gaussMix.setRelativeError(atof(argv[i]));
        } else if(arg=="-gm-abserr") {
          CoreApplication::checkOptionArg(i, argc, argv);
          _gaussMix.setAbsoluteError(atof(argv[i]));
        } else if(arg=="-gm-verbose") {
          _gaussMix.setVerbose(true);
        } else if(arg=="-curve") {
          CoreApplication::checkOptionArg(i, argc, argv);
          _curveFileNames.append(argv[i]);
        } else if(arg=="-curve-format") {
          CoreApplication::checkOptionArg(i, argc, argv);
          _curveFormatFileName=argv[i];
        } else if(arg=="-s-f") {
          _maxVariables=SlownessFrequency;
        } else if(arg=="-azimuth") {
          _maxVariables=AzimuthFrequency;
        } else if(arg=="-ell") {
          _maxVariables=EllipticityFrequency;
        } else if(arg=="-ell-abs") {
          _maxVariables=EllipticityAbsFrequency;
        } else if(arg=="-ell-deg") {
          _maxVariables=EllipticityDegFrequency;
        } else if(arg=="-ell-inv-deg") {
          _maxVariables=EllipticityDegInversedFrequency;
        } else if(arg=="-ell-rad") {
          _maxVariables=EllipticityRadFrequency;
        } else if(arg=="-ell-inv-rad") {
          _maxVariables=EllipticityRadInversedFrequency;
        } else if(arg=="-noise") {
          _maxVariables=NoiseFrequency;
        } else if(arg=="-s-k") {
          _maxVariables=SlownessWaveNumber;
        } else if(arg=="-s-l") {
          _maxVariables=SlownessWaveLength;
        } else if(arg=="-s-p") {
          _maxVariables=SlownessPeriod;
        } else if(arg=="-k-f") {
          _maxVariables=WaveNumberFrequency;
        } else if(arg=="-f-k") {
          _maxVariables=FrequencyWaveNumber;
        } else if(arg=="-l-f") {
          _maxVariables=WaveLengthFrequency;
        } else if(arg=="-stdin") {
          _ignoreStdin=false;
        } else {
          argv[j++]=argv[i];
        }
      } else {
        argv[j++]=argv[i];
      }
    }
    if(j<argc) {
      argv[j]=nullptr;
      argc=j;
    }
    return true;
  }

  void HistogramReader::helpX(ApplicationHelp * h)
  {
    h->addGroup("X Values", "x");
    h->addOption("-x-column", "Column index containing X values (0 is the first)." );
    h->addOption("-x-sampling <SAMPLING>","Defines the X sampling type:\n"
                                          "  asis       keep existing sampling\n"
                                          "  linear     regular sampling\n"
                                          "  log        regular sampling on a log scale\n"
                                          "  inversed   regular sampling on an inversed");
    h->addOption("-x-min <MIN>","Minimum of range for X axis (default=deduced from data)");
    h->addOption("-x-max <MAX>","Maximum of range for X axis (default=deduced from data)");
    h->addOption("-x-count <COUNT>","Number of samples along X (default=100)");
    h->addOption("-x-title <TITLE>","Title for X axis");
  }

  void HistogramReader::helpY(ApplicationHelp * h)
  {
    h->addGroup("Y Values", "y");
    h->addOption("-y-column", "Column index containing Y values (0 is the first)." );
    h->addOption("-y-sampling <SAMPLING>","Defines the Y sampling type:\n"
                                          "  linear     regular sampling (default)\n"
                                          "  log        regular sampling on a log scale\n"
                                          "  inversed   regular sampling on an inversed");
    h->addOption("-y-min <MIN>","Minimum of range for Y axis (default=deduced from data)");
    h->addOption("-y-max <MAX>","Maximum of range for Y axis (default=deduced from data)");
    h->addOption("-y-count <COUNT>","Number of samples along Y (default=100)");
    h->addOption("-y-factor <FACTOR>","Multiply all Y value by FACTOR (default=1)");
    h->addOption("-y-title <TITLE>","Title for Y axis");
  }

  void HistogramReader::helpCurves(ApplicationHelp * h)
  {
    h->addGroup("Curves", "curves");
    h->addOption("-curve <FILE>", "Load curve from FILE to plot on top of histogram. FILE can have wildcards '*' or '?' but it "
                                  "must be inside double quotes '\"'.");
    h->addOption("-curve-format <FILE.ctparser>", "Use FILE to define curve format");
  }

  bool HistogramReader::setDefaultXLimits(const QVector<double>& x)
  {
    TRACE;
    _xValues=x;
    std::sort(_xValues.begin(), _xValues.end());
    unique(_xValues);
    double sumLin=0.0, sumLog=0.0;
    double stepLin=std::numeric_limits<double>::infinity(), stepLog=std::numeric_limits<double>::infinity();
    if(!_xSampling || (!_manual.x.gridScaleType && !_manual.x.count)) {
      double dLin, dLog;
      for(int i=2; i<_xValues.count(); i++) {
        dLin=_xValues.at(i-1)-_xValues.at(i-2);
        dLog=_xValues.at(i-1)/_xValues.at(i-2);
        sumLin+=fabs(dLin-(_xValues.at(i)-_xValues.at(i-1)));
        sumLog+=fabs(dLog-(_xValues.at(i)/_xValues.at(i-1)));
        dLin=fabs(dLin);
        dLog=fabs(dLog);
        if(dLin<stepLin) {
          stepLin=dLin;
        }
        if(dLog<stepLog) {
          stepLog=dLog;
        }
      }
      if(sumLin<sumLog) {
        if(!_xSampling) {
          App::log(tr("Identified a linear scale for\n"));
        }
        _xLogSampling=false;
      } else {
        if(!_xSampling) {
          App::log(tr("Identified a log scale for X\n"));
        }
        _xLogSampling=true;
      }
    }
    if(!_manual.xValuesAsIs) {
      _xSampling=new SamplingParameters;
    }
    if(_xSampling) {
      if(_xValues.count()<3) {
        _xSampling->setType(SamplingParameters::Linear);
        _xSampling->setCount(_xValues.count());
      }
      if(!_xValues.isEmpty()) {
        if(!_manual.x.minimum) {
          _xSampling->setMinimum(_xValues.first());
        }
        if(!_manual.x.minimum) {
          _xSampling->setMaximum(_xValues.last());
        }
      }
      if(!_manual.x.gridScaleType && !_manual.x.count) {
        if(sumLin<sumLog) {
          App::log(tr("Identified a linear scale for X with minimum step %1\n").arg(stepLin) );
          _xSampling->setType(SamplingParameters::Linear);
          _xSampling->setStep(stepLin);
        } else {
          App::log(tr("Identified a log scale for X with minimum step %1\n").arg(stepLog) );
          _xSampling->setType(SamplingParameters::Log);
          if(_xSampling->minimum()<=0.0 || _xSampling->maximum()<=0.0) {
            App::log(tr("null or negative values not allowed for log and inversed scales (X axis)\n") );
            return false;
          }
          _xSampling->setStep(stepLog);
        }
      }
      if(_xSampling->type()==SamplingParameters::Log || _xSampling->isInversed()) {
        if(_xSampling->minimum()<=0.0 || _xSampling->maximum()<=0.0) {
          App::log(tr("null or negative values not allowed for log and inversed scales (X axis)\n") );
          return false;
        }
      }
    }
    return true;
  }

  bool HistogramReader::setDefaultYLimits()
  {
    QVector<Histogram2D::Sample>::iterator it;
    int ny=_ySampling.count();
    if(!_manual.y.minimum) {
      it=_samples.begin();
      _ySampling.setMinimum(it->y());
      for(it++; it!=_samples.end(); it++) {
        if(it->y()<_ySampling.minimum()) {
          _ySampling.setMinimum(it->y());
        }
      }
    }
    if(!_manual.y.maximum) {
      it=_samples.begin();
      _ySampling.setMaximum(it->y());
      for(it++; it!=_samples.end(); it++) {
        if(it->y()>_ySampling.maximum()) {
          _ySampling.setMaximum(it->y());
        }
      }
    }
    _ySampling.setCount(ny);

    if(_ySampling.type()==SamplingParameters::Log || _ySampling.isInversed()) {
      if(_ySampling.minimum()<=0.0 || _ySampling.maximum()<=0.0) {
        App::log(tr("null or negative values not allowed for log and inversed scales (Y axis)\n") );
        return false;
      }
    }
    return true;
  }

  /*!
    Computes default limits from samples
  */
  bool HistogramReader::setDefaultLimits()
  {
    TRACE;
    if(_samples.isEmpty()) {
      App::log(tr("No samples available\n") );
      return false;
    }
    QVector<Histogram2D::Sample>::iterator it;
    QVector<double> x;
    for(it=_samples.begin(); it!=_samples.end(); it++) {
      x.append(it->x());
    }
    return setDefaultXLimits(x) && setDefaultYLimits();
  }

  bool HistogramReader::parse(QTextStream& s)
  {
    TRACE;
    QString line;
    // Detect the type of input and eventually set default configurations
    if(!_manual.x.column && !_manual.y.column && !_manual.validColumn) {
      // Used for skipping all header lines even the lines not starting with '#'
      bool maxFormatHeader=false;
      do {
        line=s.readLine();
        if(!maxFormatHeader && line.startsWith("# MAX FORMAT RELEASE")) {
          maxFormatHeader=true;
        }
        if(line.startsWith("# abs_time frequency polarization slowness azimuth ") ||
           line.startsWith("# time frequency polarization slowness azimuth ")) {
          configureMaxFile(line);
          maxFormatHeader=false;
        } else if(line=="# seconds from start | cfreq | slow | az | math-phi | semblance | beampow") {
          configureCapMaxFile();
        }
      } while (maxFormatHeader || line.startsWith("#"));
    }
    // Effectively load samples, last line is provided because we may reading
    // stdin where seek() is not supported.
    return loadSamples(s, line);
  }

  bool HistogramReader::loadSamples(QTextStream& s, const QString& lastLine)
  {
    ConsoleProgress progress;
    progress.setCaption(tr("Loading"));
    bool ok=true;
    QString line;
    if((_powerColumn<0 || _relativePowerThreshold==0.0) &&
       (_noiseColumn<0 || _noiseThreshold==std::numeric_limits<double>::infinity()) &&
       !_manual.x.minimum && !_manual.x.maximum && !_manual.y.minimum && !_manual.y.maximum) {
      Histogram2D::Sample sample;
      if(_yFactor==1.0 && _maxVariables==SlownessFrequency) {
        if(File::isDataLine(lastLine, _pattern)) {
          if(sample.read(lastLine, _xColumn, _yColumn, _validColumn)) {
            _samples.append(sample);
          } else {
            ok=false;
          }
        }
        while(!s.atEnd()) {
          if(File::readLine(s, line, _pattern)) {
            if(sample.read(line, _xColumn, _yColumn, _validColumn)) {
              _samples.append(sample);
            } else {
              ok=false;
              break;
            }
            progress.setValue(_samples.count());
          }
        }
      } else {
        if(File::isDataLine(lastLine, _pattern)) {
          if(sample.read(lastLine, _xColumn, _yColumn, _validColumn)) {
            sample.setY(sample.y()*_yFactor);
            _samples.append(transformSample(sample));
          } else {
            ok=false;
          }
        }
        while(!s.atEnd()) {
          if(File::readLine(s, line, _pattern)) {
            if(sample.read(line, _xColumn, _yColumn, _validColumn)) {
              sample.setY(sample.y()*_yFactor); // For .max, factor is used for old files in s/km
              _samples.append(transformSample(sample));
            } else {
              ok=false;
              break;
            }
            progress.setValue(_samples.count());
          }
        }
      }
      progress.end(_samples.count());
    } else {
      SampleFilter::Sample sample;
      SampleFilter filter;
      if(File::isDataLine(lastLine, _pattern)) {
        if(sample.read(lastLine, _xColumn, _yColumn, _noiseColumn, _powerColumn, _validColumn)) {
          filter.append(sample);
        } else {
          ok=false;
        }
      }
      while(!s.atEnd()) {
        if(File::readLine(s, line, _pattern)) {
          if(sample.read(line, _xColumn, _yColumn, _noiseColumn, _powerColumn, _validColumn)) {
            filter.append(sample);
          } else {
            ok=false;
            break;
          }
          progress.setValue(filter.count());
        }
      }
      progress.end(filter.count());
      if(ok) {
        App::log(tr("Loaded %1 samples\n").arg(filter.count()) );
        if(_relativePowerThreshold>0.0) {
          App::log(tr("Filtering power data < %1\n").arg(_relativePowerThreshold) );
          filter.filterPower(_relativePowerThreshold);
        }
        if(_noiseThreshold<std::numeric_limits<double>::infinity()) {
          App::log(tr("Filtering noisy data > %1\n").arg(_noiseThreshold) );
          filter.filterNoise(_noiseThreshold);
        }
        if(_manual.x.minimum) {
          ASSERT(_xSampling);
          App::log(tr("Filtering X < %1\n").arg(_xSampling->minimum()));
          filter.filterXMinimum(_xSampling->minimum());
        }
        if(_manual.x.maximum) {
          ASSERT(_xSampling);
          App::log(tr("Filtering X > %1\n").arg(_xSampling->maximum()));
          filter.filterXMaximum(_xSampling->maximum());
        }
        if(_manual.y.minimum) {
          App::log(tr("Filtering Y < %1\n").arg(_ySampling.minimum()));
          filter.filterYMinimum(_ySampling.minimum());
        }
        if(_manual.y.maximum) {
          App::log(tr("Filtering Y > %1\n").arg(_ySampling.maximum()));
          filter.filterYMaximum(_ySampling.maximum());
        }
        QVector<Histogram2D::Sample> samples=filter.samples();
        if(_maxVariables!=SlownessFrequency) {
          for(QVector<Histogram2D::Sample>::iterator it=samples.begin();
              it!=samples.end(); it++) {
            Histogram2D::Sample& s=*it;
            s=transformSample(s);
            _samples.append(s);
          }
        } else {
          _samples.append(samples);
        }
      }
    }
    return ok;
  }

  Histogram2D::Sample HistogramReader::transformSample(const Histogram2D::Sample& s)
  {
    TRACE;
    // variable selection is specific for .max files, available only in slowness versus frequency
    Histogram2D::Sample ns(s);
    switch(_maxVariables) {
    case SlownessFrequency:
    case AzimuthFrequency:
    case EllipticityFrequency:
    case NoiseFrequency:
      break;
    case EllipticityAbsFrequency:
      ns.setY(fabs(s.y()));
      break;
    case EllipticityDegFrequency:
      ns.setY(Angle::radiansToDegrees(::atan(s.y())));
      break;
    case EllipticityDegInversedFrequency:
      ns.setY(Angle::radiansToDegrees(-::atan(s.y())));
      break;
    case EllipticityRadFrequency:
      ns.setY(::atan(s.y()));
      break;
    case EllipticityRadInversedFrequency:
      ns.setY(-::atan(s.y()));
      break;
    case SlownessWaveNumber:
      ns.setX(2.0*M_PI*s.x()*s.y());
      ns.setY(s.y());
      break;
    case SlownessWaveLength:
      ns.setX(1.0/(s.x()*s.y()));
      break;
    case SlownessPeriod:
      ns.setX(1.0/(s.x()));
      break;
    case WaveNumberFrequency:
      ns.setY(2.0*M_PI*s.x()*s.y());
      break;
    case FrequencyWaveNumber:
      ns.setX(2.0*M_PI*s.x()*s.y());
      ns.setY(s.x());
      break;
    case WaveLengthFrequency:
      ns.setX(s.x());
      ns.setY(1.0/(s.x()*s.y()));
      break;
    }
    return ns;
  }

  Histogram2D * HistogramReader::histogram() const
  {
    TRACE;
    Histogram2D * h;
    static const QString msg=tr("X axis from %1 to %2 with %3 bins\n");
    if(_xSampling) {
       h=new Histogram2D(_xSampling->count(), _ySampling.count());
       App::log(msg.arg(_xSampling->minimum())
                   .arg(_xSampling->maximum())
                   .arg(_xSampling->count()));
       if(!h->setXSampling(*_xSampling)) {
         delete h;
         return nullptr;
       }
    } else {
      h=new Histogram2D(_xValues.count(), _ySampling.count());
      App::log(msg.arg(_xValues.first())
                  .arg(_xValues.last())
                  .arg(_xValues.count()));
      if(!h->setXSampling(_xValues)) {
        delete h;
        return nullptr;
      }
    }
    h->setXTitle(_xTitle);
    h->setXTitleInversedScale(_xTitleInversedScale);
    if(_manual.x.viewScaleType) {
      h->setXScaleType(_xScaleType);
    } else {
      if(_xLogSampling) {
        h->setXScaleType(Scale::Log);
      } else {
        h->setXScaleType(Scale::Linear);
      }
    }
    App::log(tr("Y axis from %1 to %2 with %3 bins\n")
             .arg(_ySampling.minimum())
             .arg(_ySampling.maximum())
             .arg(_ySampling.count()));
    if(!h->setYSampling(_ySampling)) {
      delete h;
      return nullptr;
    }
    h->setYTitle(_yTitle);
    h->setYTitleInversedScale(_yTitleInversedScale);
    if(_manual.y.viewScaleType) {
      h->setYScaleType(_yScaleType);
    } else {
      if(_ySampling.type()==SamplingParameters::Log) {
        h->setYScaleType(Scale::Log);
      } else {
        h->setYScaleType(Scale::Linear);
      }
    }

    h->setSamples(_samples);
    return h;
  }

  void HistogramReader::configureMaxFile(const QString& line)
  {
    _validColumn=-1;
    if(line=="# abs_time frequency polarization slowness azimuth ellipticity noise power valid") {
      if(!_ignoreValid) {
        _validColumn=8;
      }
      _noiseColumn=6;
      _powerColumn=7;
    } else if(line=="# time frequency polarization slowness azimuth ellipticity noise power valid") {
      if(!_ignoreValid) {
        _validColumn=8;
      }
      _noiseColumn=6;
      _powerColumn=7;
    } else if(line=="# time frequency polarization slowness azimuth ellipticity power valid") {
      if(!_ignoreValid) {
        _validColumn=7;
      }
      _powerColumn=6;
    } else if(line=="# time frequency polarization slowness azimuth ellipticity valid") {
      // Compatibility for results from October 2017 to February 2018
      if(!_ignoreValid) {
        _validColumn=6;
      }
    } else if(line=="# time frequency polarization slowness azimuth valid") {
      // Compatibility for results from September and October 2017
      // Option -ell* will ouput junk for these results
      if(!_ignoreValid) {
        _validColumn=5;
      }
    }
    _headerLine="# abs_time frequency polarization slowness azimuth ellipticity noise power valid\n";

    // X title
    switch(_maxVariables) {
    case SlownessFrequency:
    case AzimuthFrequency:
    case EllipticityFrequency:
    case EllipticityAbsFrequency:
    case EllipticityDegFrequency:
    case EllipticityDegInversedFrequency:
    case EllipticityRadFrequency:
    case EllipticityRadInversedFrequency:
    case NoiseFrequency:
    case WaveNumberFrequency:
    case WaveLengthFrequency:
      _xTitle=tr("Frequency (Hz)");
      _xTitleInversedScale=tr("Period (s)");
      break;
    case SlownessPeriod:
      _xTitle=tr("Period (s)");
      _xTitleInversedScale=tr("Frequency (Hz)");
      break;
    case SlownessWaveNumber:
    case FrequencyWaveNumber:
      _xTitle=tr("Wave number (rad/m)");
      break;
    case SlownessWaveLength:
      _xTitle=tr("Wave length (m)");
      break;
    }
    _xColumn=1;
    // Y title and column
    switch(_maxVariables) {
    case SlownessFrequency:
    case SlownessPeriod:
    case SlownessWaveNumber:
    case SlownessWaveLength:
      _yTitle=tr("Slowness (s/m)");
      _yTitleInversedScale=tr("Velocity (m/s)");
      _yColumn=3;
      break;
    case WaveNumberFrequency:
      _yTitle=tr("Wave number (rad/m)");
      _yColumn=3;
      break;
    case WaveLengthFrequency:
      _yTitle=tr("Wave length (m)");
      _yColumn=3;
      break;
    case FrequencyWaveNumber:
      _yTitle=tr("Frequency (Hz)");
      _yTitleInversedScale=tr("Period (s)");
      _yColumn=3;
      break;
    case AzimuthFrequency:
      _yTitle=tr("Azimuth (degrees)");
      _yColumn=4;
      break;
    case EllipticityFrequency:
      _yTitle=tr("Ellipticity");
      _yColumn=5;
      break;
    case EllipticityAbsFrequency:
      _yTitle=tr("Ellipticity");
      _yColumn=5;
      break;
    case EllipticityDegFrequency:
    case EllipticityDegInversedFrequency:
      _yTitle=tr("Ellipticity angle (deg)");
      _yColumn=5;
      break;
    case EllipticityRadFrequency:
    case EllipticityRadInversedFrequency:
      _yTitle=tr("Ellipticity angle (rad)");
      _yColumn=5;
      break;
    case NoiseFrequency:
      _yTitle=tr("Relative incoherent noise");
      _yColumn=6;
      break;
    }
    // Y limits and sampling
    switch(_maxVariables) {
    case SlownessFrequency:
    case SlownessPeriod:
    case SlownessWaveLength:
    case SlownessWaveNumber:
      if(!_manual.y.gridScaleType) {
        _ySampling.setType(SamplingParameters::Log);
        _manual.y.gridScaleType=true;
        _yScaleType=Scale::InversedLog;
        _manual.y.viewScaleType=true;
      }
      break;
    case WaveNumberFrequency:
    case WaveLengthFrequency:
    case FrequencyWaveNumber:
      //_yCount=50;
      break;
    case AzimuthFrequency:
      if(!_manual.y.minimum) {
        _ySampling.setMinimum(0.0);
        _manual.y.minimum=true;
      }
      if(!_manual.y.maximum) {
        _ySampling.setMaximum(360.0);
        _manual.y.maximum=true;
      }
      if(!_manual.y.count) {
        _ySampling.setCount(72);
        _manual.y.count=true;
      }
      break;
    case EllipticityFrequency:
      if(!_manual.y.minimum) {
        _ySampling.setMinimum(-10.0);
        _manual.y.minimum=true;
      }
      if(!_manual.y.maximum) {
        _ySampling.setMaximum(10.0);
        _manual.y.maximum=true;
      }
      if(!_manual.y.count) {
        _ySampling.setCount(200);
        _manual.y.count=true;
      }
      break;
    case EllipticityAbsFrequency:
      if(!_manual.y.minimum) {
        _ySampling.setMinimum(0.1);
        _manual.y.minimum=true;
      }
      if(!_manual.y.maximum) {
        _ySampling.setMaximum(10.0);
        _manual.y.maximum=true;
      }
      if(!_manual.y.count) {
        _ySampling.setCount(200);
        _manual.y.count=true;
      }
      _ySampling.setType(SamplingParameters::Log);
      break;
    case EllipticityDegFrequency:
    case EllipticityDegInversedFrequency:
      if(!_manual.y.minimum) {
        _ySampling.setMinimum(-90.0);
        _manual.y.minimum=true;
      }
      if(!_manual.y.maximum) {
        _ySampling.setMaximum(90.0);
        _manual.y.maximum=true;
      }
      if(!_manual.y.count) {
        _ySampling.setCount(200);
        _manual.y.count=true;
      }
      break;
    case EllipticityRadFrequency:
    case EllipticityRadInversedFrequency:
      if(!_manual.y.minimum) {
        _ySampling.setMinimum(-0.5*M_PI);
        _manual.y.minimum=true;
      }
      if(!_manual.y.maximum) {
        _ySampling.setMaximum(0.5*M_PI);
        _manual.y.maximum=true;
      }
      if(!_manual.y.count) {
        _ySampling.setCount(200);
        _manual.y.count=true;
      }
      break;
    case NoiseFrequency:
      if(!_manual.y.minimum) {
        _ySampling.setMinimum(0.1);
        _manual.y.minimum=true;
      }
      if(!_manual.y.maximum) {
        _ySampling.setMaximum(10.0);
        _manual.y.maximum=true;
      }
      if(!_manual.y.count) {
        _ySampling.setCount(50);
        _manual.y.count=true;
      }
      if(!_manual.y.gridScaleType) {
        _ySampling.setType(SamplingParameters::Log);
        _manual.y.gridScaleType=true;
      }
      break;
    }
    switch(_maxVariables) {
    case SlownessFrequency:
    case WaveNumberFrequency:
    case WaveLengthFrequency:
    case SlownessPeriod:
    case AzimuthFrequency:
    case EllipticityFrequency:
    case EllipticityAbsFrequency:
    case EllipticityDegFrequency:
    case EllipticityDegInversedFrequency:
    case EllipticityRadFrequency:
    case EllipticityRadInversedFrequency:
    case NoiseFrequency:
      break;
    case SlownessWaveNumber:
    case SlownessWaveLength:
    case FrequencyWaveNumber:
      //_xCount=50; // min and max should correspond to kmin and kmax, should be manually specified
      //_xSamplingType=LinearScale;
      //_manualXScale=true;
      break;
    }
    if(!_pattern.isEmpty()) {
      App::log(tr("Slowness max file with pattern '%1'\n").arg(_pattern) );
    }
  }

  void HistogramReader::configureCapMaxFile()
  {
    _headerLine="# abs_time frequency polarization slowness azimuth ellipticity noise power valid\n";
    _headerLine="# time frequency polarization slowness azimuth ellipticity valid\n";
    _xTitle=tr("Frequency (Hz)");
    _xColumn=1;
    switch(_maxVariables) {
    case SlownessFrequency:
      _yTitle=tr("Slowness (s/m)");
      _yFactor=0.001;
      _yColumn=2;
      _ySampling.setMinimum(1.0/3500.0);
      _manual.y.minimum=true;
      _ySampling.setType(SamplingParameters::Log);
      _manual.y.gridScaleType=true;
      _validColumn=-1;
      break;
    case AzimuthFrequency:
      _yTitle=tr("Azimuth (degrees)");
      _yColumn=3;
      _ySampling.setMinimum(0.0);
      _ySampling.setMaximum(360.0);
      _manual.y.minimum=true;
      _manual.y.maximum=true;
      _validColumn=-1;
      break;
    case EllipticityFrequency:
    case EllipticityAbsFrequency:
    case EllipticityDegFrequency:
    case EllipticityDegInversedFrequency:
    case EllipticityRadFrequency:
    case EllipticityRadInversedFrequency:
    case NoiseFrequency:
    case SlownessWaveNumber:
    case SlownessWaveLength:
    case FrequencyWaveNumber:
    case WaveNumberFrequency:
    case WaveLengthFrequency:
    case SlownessPeriod:
      App::log(tr("Unsupported max variables for old slowness max file (before 2017)\n") );
      ::exit(2);
    }
    App::log(tr("Old slowness max file (before 2017)\n") );
  }


} // namespace QGpCoreStat
