/***************************************************************************
**
**  This file is part of gpviewmax.
**
**  gpviewmax 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.
**
**  gpviewmax 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: 2018-07-16
**  Copyright: 2018-2019
**    Marc Wathelet (ISTerre, Grenoble, France)
**
***************************************************************************/

#include "Reader.h"

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

  Full description of class still missing
*/

Reader::Reader()
{
  _showPlots=0;

  _dispersionTitle=tr("Slowness (s/m)");
  _ellipticityTitle=tr("Ellipticity");
  _noiseTitle=tr("Relative incoherent noise");
  _azimuthTitle=tr("Azimuth (degrees)");
  _powerTitle=tr("Power");

  _dispersionColumn=3;
  _ellipticityColumn=5;
  _noiseColumn=6;
  _azimuthColumn=4;
  _powerColumn=7;

  _dispersionSampling.setMinimum(1.0/3500.0);
  _dispersionSampling.setMaximum(1.0/100.0);
  _dispersionSampling.setType(SamplingParameters::Log);
  _dispersionSampling.setCount(200);
  _ellipticitySampling.setMinimum(-90.0);
  _ellipticitySampling.setMaximum(90.0);
  _ellipticitySampling.setCount(200);
  _noiseSampling.setMinimum(0.01);
  _noiseSampling.setMaximum(10);
  _noiseSampling.setType(SamplingParameters::Log);
  _noiseSampling.setCount(50);
  _azimuthSampling.setMaximum(360.0);
  _azimuthSampling.setCount(72);
  _powerSampling.setMaximum(1.0);
  _powerSampling.setCount(200);
  _powerSampling.setType(SamplingParameters::Log);

  memset(_manual.flags, 0, sizeof(int)*5);
  _samples=nullptr;
  _relativeRange=0.2;
  _nullEll=true;
  _noiseDev=std::numeric_limits<double>::infinity();
  _slownessDev=0.0;
}

Reader::~Reader()
{
  delete _samples;
}

bool Reader::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=="-R") {
        setPattern("R");
        _polarization="Rayleigh";
        _nullEll=false;
      } else if(arg=="-L") {
        setPattern("L");
        _polarization="Love";
        _nullEll=true;
      } else if(arg=="-show-disp") {
        _showPlots|=Dispersion;
      } else if(arg=="-show-ell") {
        _showPlots|=Ellipticity;
      } else if(arg=="-show-noise") {
        _showPlots|=Noise;
      } else if(arg=="-show-az") {
        _showPlots|=Azimuth;
      } else if(arg=="-show-pow") {
        _showPlots|=Power;
      // --------------- DISPERSION
      } else if(arg=="-disp-column" || arg=="-y-column") {
        CoreApplication::checkOptionArg(i, argc, argv);
        _dispersionColumn=atoi(argv[i]);
        _manual.dispersion.column=true;
      } else if(arg=="-disp-sampling" || arg=="-y-sampling") {
        CoreApplication::checkOptionArg(i, argc, argv);
        if(strcmp(argv[i],"inversed")==0) {
          _dispersionSampling.setInversed(true);
        } else if(strcmp(argv[i],"log")==0) {
          _dispersionSampling.setType(SamplingParameters::Log);
        } else {
          _dispersionSampling.setType(SamplingParameters::Linear);
        }
        _manual.dispersion.gridScaleType=true;
      } else if(arg=="-disp-min" || arg=="-y-min") {
        CoreApplication::checkOptionArg(i, argc, argv);
        _dispersionSampling.setMinimum(atof(argv[i]));
        _manual.dispersion.minimum=true;
      } else if(arg=="-disp-max" || arg=="-y-max") {
        CoreApplication::checkOptionArg(i, argc, argv);
        _dispersionSampling.setMaximum(atof(argv[i]));
        _manual.dispersion.maximum=true;
      } else if(arg=="-disp-count" || arg=="-y-count") {
        CoreApplication::checkOptionArg(i, argc, argv);
        _dispersionSampling.setCount(atoi(argv[i]));
        _manual.dispersion.count=true;
        if(_dispersionSampling.count()<=0) {
          App::log(tr("negative or null number of dispersion samples (option -disp-count)\n") );
          return false;
        }
      } else if(arg=="-disp-title" || arg=="-y-title") {
        CoreApplication::checkOptionArg(i, argc, argv);
        _dispersionTitle=argv[i];
      // --------------- ELLIPTICITY
      } else if(arg=="-ell-column") {
        CoreApplication::checkOptionArg(i, argc, argv);
        _ellipticityColumn=atoi(argv[i]);
        _manual.ellipticity.column=true;
      } else if(arg=="-ell-sampling") {
        CoreApplication::checkOptionArg(i, argc, argv);
        if(strcmp(argv[i],"inversed")==0) {
          _ellipticitySampling.setInversed(true);
        } else if(strcmp(argv[i],"log")==0) {
          _ellipticitySampling.setType(SamplingParameters::Log);
        } else {
          _ellipticitySampling.setType(SamplingParameters::Linear);
        }
        _manual.ellipticity.gridScaleType=true;
      } else if(arg=="-ell-min") {
        CoreApplication::checkOptionArg(i, argc, argv);
        _ellipticitySampling.setMinimum(atof(argv[i]));
        _manual.ellipticity.minimum=true;
      } else if(arg=="-ell-max") {
        CoreApplication::checkOptionArg(i, argc, argv);
        _ellipticitySampling.setMaximum(atof(argv[i]));
        _manual.ellipticity.maximum=true;
      } else if(arg=="-ell-count") {
        CoreApplication::checkOptionArg(i, argc, argv);
        _ellipticitySampling.setCount(atoi(argv[i]));
        _manual.ellipticity.count=true;
        if(_ellipticitySampling.count()<=0) {
          App::log(tr("negative or null number of ellipticity samples (option -ell-count)\n") );
          return false;
        }
      } else if(arg=="-ell-title") {
        CoreApplication::checkOptionArg(i, argc, argv);
        _ellipticityTitle=argv[i];
      // --------------- NOISE
      } else if(arg=="-noise-column") {
        CoreApplication::checkOptionArg(i, argc, argv);
        _noiseColumn=atoi(argv[i]);
        _manual.noise.column=true;
      } else if(arg=="-noise-sampling") {
        CoreApplication::checkOptionArg(i, argc, argv);
        if(strcmp(argv[i],"inversed")==0) {
          _noiseSampling.setInversed(true);
        } else if(strcmp(argv[i],"log")==0) {
          _noiseSampling.setType(SamplingParameters::Log);
        } else {
          _noiseSampling.setType(SamplingParameters::Linear);
        }
        _manual.noise.gridScaleType=true;
      } else if(arg=="-noise-min") {
        CoreApplication::checkOptionArg(i, argc, argv);
        _noiseSampling.setMinimum(atof(argv[i]));
        _manual.noise.minimum=true;
      } else if(arg=="-noise-max") {
        CoreApplication::checkOptionArg(i, argc, argv);
        _noiseSampling.setMaximum(atof(argv[i]));
        _manual.noise.maximum=true;
      } else if(arg=="-noise-count") {
        CoreApplication::checkOptionArg(i, argc, argv);
        _noiseSampling.setCount(atoi(argv[i]));
        _manual.noise.count=true;
        if(_noiseSampling.count()<=0) {
          App::log(tr("negative or null number of noise samples (option -noise-count)\n") );
          return false;
        }
      } else if(arg=="-noise-title") {
        CoreApplication::checkOptionArg(i, argc, argv);
        _noiseTitle=argv[i];
      // --------------- AZIMUTH
      } else if(arg=="-az-column") {
        CoreApplication::checkOptionArg(i, argc, argv);
        _azimuthColumn=atoi(argv[i]);
        _manual.azimuth.column=true;
      } else if(arg=="-az-sampling") {
        CoreApplication::checkOptionArg(i, argc, argv);
        if(strcmp(argv[i],"inversed")==0) {
          _azimuthSampling.setInversed(true);
        } else if(strcmp(argv[i],"log")==0) {
          _azimuthSampling.setType(SamplingParameters::Log);
        } else {
          _azimuthSampling.setType(SamplingParameters::Linear);
        }
        _manual.azimuth.gridScaleType=true;
      } else if(arg=="-az-min") {
        CoreApplication::checkOptionArg(i, argc, argv);
        _azimuthSampling.setMinimum(atof(argv[i]));
        _manual.azimuth.minimum=true;
      } else if(arg=="-az-max") {
        CoreApplication::checkOptionArg(i, argc, argv);
        _azimuthSampling.setMaximum(atof(argv[i]));
        _manual.azimuth.maximum=true;
      } else if(arg=="-az-count") {
        CoreApplication::checkOptionArg(i, argc, argv);
        _azimuthSampling.setCount(atoi(argv[i]));
        _manual.azimuth.count=true;
        if(_azimuthSampling.count()<=0) {
          App::log(tr("negative or null number of azimuth samples (option -az-count)\n") );
          return false;
        }
      } else if(arg=="-az-title") {
        CoreApplication::checkOptionArg(i, argc, argv);
        _azimuthTitle=argv[i];
      // --------------- POWER
      } else if(arg=="-pow-column") {
        CoreApplication::checkOptionArg(i, argc, argv);
        _powerColumn=atoi(argv[i]);
        _manual.power.column=true;
      } else if(arg=="-pow-sampling") {
        CoreApplication::checkOptionArg(i, argc, argv);
        if(strcmp(argv[i],"inversed")==0) {
          _powerSampling.setInversed(true);
        } else if(strcmp(argv[i],"log")==0) {
          _powerSampling.setType(SamplingParameters::Log);
        } else {
          _powerSampling.setType(SamplingParameters::Linear);
        }
        _manual.power.gridScaleType=true;
      } else if(arg=="-pow-min") {
        CoreApplication::checkOptionArg(i, argc, argv);
        _powerSampling.setMinimum(atof(argv[i]));
        _manual.power.minimum=true;
      } else if(arg=="-pow-max") {
        CoreApplication::checkOptionArg(i, argc, argv);
        _powerSampling.setMaximum(atof(argv[i]));
        _manual.power.maximum=true;
      } else if(arg=="-pow-count") {
        CoreApplication::checkOptionArg(i, argc, argv);
        _powerSampling.setCount(atoi(argv[i]));
        _manual.power.count=true;
        if(_powerSampling.count()<=0) {
          App::log(tr("negative or null number of power samples (option -pow-count)\n") );
          return false;
        }
      } else if(arg=="-pow-title") {
        CoreApplication::checkOptionArg(i, argc, argv);
        _powerTitle=argv[i];
      } else if(arg=="-curve-rel-range") {
        CoreApplication::checkOptionArg(i, argc, argv);
        _relativeRange=atof(argv[i]);
      } else if(arg=="-ref-disp-layer") {
        CoreApplication::checkOptionArg(i, argc, argv);
        _referenceDispersionLayer=argv[i];
      } else if(arg=="-ref-ell-layer") {
        CoreApplication::checkOptionArg(i, argc, argv);
        _referenceEllipticityLayer=argv[i];
      } else if(arg=="-null-ell") {
        _nullEll=true;
      } else if(arg=="-low-noise") {
        CoreApplication::checkOptionArg(i, argc, argv);
        _noiseDev=atof(argv[i]);
        CoreApplication::checkOptionArg(i, argc, argv);
        _slownessDev=atof(argv[i]);
      } else {
        argv[j++]=argv[i];
      }
    } else {
      argv[j++]=argv[i];
    }
  }
  if(j<argc) {
    argv[j]=nullptr;
    argc=j;
  }
  if(_showPlots==0) {
    _showPlots=Dispersion;
    if(_polarization=="Rayleigh") {
      _showPlots|=Ellipticity;
      _showPlots|=Noise;
    }
    _showPlots|=Azimuth;
    _showPlots|=Power;
  }
  return HistogramReader::setOptions(argc, argv);
}

void Reader::helpY(ApplicationHelp * h)
{
  h->addGroup("Dispersion Values", "disp");
  h->addOption("-disp-column", "Column index containing dispersion values (0 is the first)." );
  h->addOption("-disp-sampling <SAMPLING>","Defines the slowness sampling type:\n"
                                        "  linear     regular sampling\n"
                                        "  log        regular sampling on a log scale\n"
                                        "  inversed   regular sampling on an inversed");
  h->addOption("-disp-min <MIN>","Minimum of range for slownness axis (default=deduced from data)");
  h->addOption("-disp-max <MAX>","Maximum of range for slowness axis (default=deduced from data)");
  h->addOption("-disp-count <COUNT>","Number of samples along slowness");
  h->addOption("-disp-title <TITLE>","Title for slowness axis");
  h->addGroup("Ellipticity Values", "ell");
  h->addOption("-ell-column", "Column index containing ellipticity values (0 is the first)." );
  h->addOption("-ell-sampling <SAMPLING>","Defines the ellipticity sampling type:\n"
                                        "  linear     regular sampling\n"
                                        "  log        regular sampling on a log scale\n"
                                        "  inversed   regular sampling on an inversed");
  h->addOption("-ell-min <MIN>","Minimum of range for slownness axis (default=deduced from data)");
  h->addOption("-ell-max <MAX>","Maximum of range for ellipticity axis (default=deduced from data)");
  h->addOption("-ell-count <COUNT>","Number of samples along ellipticity");
  h->addOption("-ell-title <TITLE>","Title for ellipticity axis");
  h->addGroup("Noise Values", "ell");
  h->addOption("-noise-column", "Column index containing noise values (0 is the first)." );
  h->addOption("-noise-sampling <SAMPLING>","Defines the noise sampling type:\n"
                                        "  linear     regular sampling\n"
                                        "  log        regular sampling on a log scale\n"
                                        "  inversed   regular sampling on an inversed");
  h->addOption("-noise-min <MIN>","Minimum of range for slownness axis (default=deduced from data)");
  h->addOption("-noise-max <MAX>","Maximum of range for noise axis (default=deduced from data)");
  h->addOption("-noise-count <COUNT>","Number of samples along noise");
  h->addOption("-noise-title <TITLE>","Title for noise axis");
  h->addGroup("Azimuth Values", "az");
  h->addOption("-az-column", "Column index containing azimuth values (0 is the first)." );
  h->addOption("-az-sampling <SAMPLING>","Defines the azimuth sampling type:\n"
                                        "  linear     regular sampling\n"
                                        "  log        regular sampling on a log scale\n"
                                        "  inversed   regular sampling on an inversed");
  h->addOption("-az-min <MIN>","Minimum of range for slownness axis (default=deduced from data)");
  h->addOption("-az-max <MAX>","Maximum of range for azimuth axis (default=deduced from data)");
  h->addOption("-az-count <COUNT>","Number of samples along azimuth");
  h->addOption("-az-title <TITLE>","Title for azimuth axis");
  h->addGroup("Power Values", "pow");
  h->addOption("-pow-column", "Column index containing power values (0 is the first)." );
  h->addOption("-pow-sampling <SAMPLING>","Defines the power sampling type:\n"
                                        "  linear     regular sampling\n"
                                        "  log        regular sampling on a log scale\n"
                                        "  inversed   regular sampling on an inversed");
  h->addOption("-pow-min <MIN>","Minimum of range for slownness axis (default=deduced from data)");
  h->addOption("-pow-max <MAX>","Maximum of range for power axis (default=deduced from data)");
  h->addOption("-pow-count <COUNT>","Number of samples along power");
  h->addOption("-pow-title <TITLE>","Title for power axis");
}

/*!

*/
bool Reader::loadSamples(QTextStream& s, const QString& lastLine)
{
  TRACE;
  ConsoleProgress progress;
  progress.setCaption(tr("Loading"));
  bool ok=true;
  Sample sample;
  QString line;
  if(File::isDataLine(lastLine, pattern())) {
    if(sample.read(lastLine)) {
      _samples->append(sample);
    } else {
      ok=false;
    }
  }
  while(!s.atEnd()) {
    if(File::readLine(s, line, pattern())) {
      if(sample.read(line)) {
        _samples->append(sample);
      } else {
        ok=false;
      }
    }
    progress.setValue(_samples->count());
  }
  progress.end(_samples->count());
  return ok;
}

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

  ny=_dispersionSampling.count();
  if(!_manual.dispersion.minimum) {
    it=_samples->begin();
    _dispersionSampling.setMinimum(it->slowness());
    for(it++; it!=_samples->end(); it++) {
      if(it->slowness()<_dispersionSampling.minimum()) {
        _dispersionSampling.setMinimum(it->slowness());
      }
    }
  }
  if(!_manual.dispersion.maximum) {
    it=_samples->begin();
    _dispersionSampling.setMaximum(it->slowness());
    for(it++; it!=_samples->end(); it++) {
      if(it->slowness()>_dispersionSampling.maximum()) {
        _dispersionSampling.setMaximum(it->slowness());
      }
    }
  }
  _dispersionSampling.setCount(ny);

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

  ny=_ellipticitySampling.count();
  if(!_manual.ellipticity.minimum) {
    it=_samples->begin();
    _ellipticitySampling.setMinimum(it->ellipticity());
    for(it++; it!=_samples->end(); it++) {
      if(it->ellipticity()<_ellipticitySampling.minimum()) {
        _ellipticitySampling.setMinimum(it->ellipticity());
      }
    }
  }
  if(!_manual.ellipticity.maximum) {
    it=_samples->begin();
    _ellipticitySampling.setMaximum(it->ellipticity());
    for(it++; it!=_samples->end(); it++) {
      if(it->ellipticity()>_ellipticitySampling.maximum()) {
        _ellipticitySampling.setMaximum(it->ellipticity());
      }
    }
  }
  _ellipticitySampling.setCount(ny);

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

  ny=_noiseSampling.count();
  if(!_manual.noise.minimum) {
    it=_samples->begin();
    _noiseSampling.setMinimum(it->noise());
    for(it++; it!=_samples->end(); it++) {
      if(it->noise()<_noiseSampling.minimum()) {
        _noiseSampling.setMinimum(it->noise());
      }
    }
  }
  if(!_manual.noise.maximum) {
    _noiseSampling.setMaximum(50.0);
  }
  _noiseSampling.setCount(ny);

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

  ny=_azimuthSampling.count();
  if(!_manual.azimuth.minimum) {
    it=_samples->begin();
    _azimuthSampling.setMinimum(it->azimuth());
    for(it++; it!=_samples->end(); it++) {
      if(it->azimuth()<_azimuthSampling.minimum()) {
        _azimuthSampling.setMinimum(it->azimuth());
      }
    }
  }
  if(!_manual.azimuth.maximum) {
    it=_samples->begin();
    _azimuthSampling.setMaximum(it->azimuth());
    for(it++; it!=_samples->end(); it++) {
      if(it->azimuth()>_azimuthSampling.maximum()) {
        _azimuthSampling.setMaximum(it->azimuth());
      }
    }
  }
  _azimuthSampling.setCount(ny);

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

  ny=_powerSampling.count();
  if(!_manual.power.maximum) {
    it=_samples->begin();
    _powerSampling.setMaximum(it->power());
    for(it++; it!=_samples->end(); it++) {
      if(it->power()>_powerSampling.maximum()) {
        _powerSampling.setMaximum(it->power());
      }
    }
  }
  if(!_manual.power.minimum) {
    _powerSampling.setMinimum(_powerSampling.maximum()*0.001);
  }
  _powerSampling.setCount(ny);

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