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

#include <QGpCoreWave.h>
#include "SpacReader.h"

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

  Full description of class still missing
*/

/*!
  Description of constructor still missing
*/
SpacReader::SpacReader()
    : ArgumentStdinReader()
{
  TRACE;
  _nModes=1;
  _vertical=false;
  _radial=false;
  _transverse=false;
  _alpha = 0.5;
  _oneMode=false;

  _dispersionFactory=0;
  _autocorrFactory=0;
}

SpacReader::~SpacReader()
{
  TRACE;
  delete _dispersionFactory;
  delete _autocorrFactory;
}

bool SpacReader::setOptions(int& argc, char ** argv)
{
  TRACE;
  // Options
  SamplingOption samplingType=LogScale;
  int nSamples=100;
  double minRange=0.2;
  double maxRange=20.0;
  // Check arguments
  int i, j=1;
  for (i=1; i<argc; i++) {
    QByteArray arg=argv[i];
    if(arg[0]=='-') {
      if(arg=="-vertical") {
        _vertical=true;
      } else if(arg=="-radial") {
        _radial=true;
      } else if(arg=="-transverse") {
        _transverse=true;
      } else if(arg=="-M") {
        CoreApplication::checkOptionArg(i, argc, argv);
        _nModes = atoi(argv[i]);
      } else if(arg=="-one-mode") {
        _oneMode=true;
      } else if(arg=="-alpha") {
        CoreApplication::checkOptionArg(i, argc, argv);
        _alpha=atof(argv[i]);
      } else if(arg=="-s") {
        CoreApplication::checkOptionArg(i, argc, argv);
        if(strcmp(argv[i],"period")==0) {
          samplingType=InversedScale;
        } else if(strcmp(argv[i],"frequency")==0) {
          samplingType=LinearScale;
        } else {
          samplingType=LogScale;
        }
      } else if(arg=="-min") {
        CoreApplication::checkOptionArg(i, argc, argv);
        minRange=atof(argv[i]);
      } else if(arg=="-max") {
        CoreApplication::checkOptionArg(i, argc, argv);
        maxRange=atof(argv[i]);
      } else if(arg=="-n") {
        CoreApplication::checkOptionArg(i, argc, argv);
        nSamples=atoi(argv[i]);
        if(nSamples<=0) {
          App::log(tr("gpspac: negative or null number of samples (option -n)\n") );
          return false;
        }
      } else if(arg=="-r") {
        CoreApplication::checkOptionArg(i, argc, argv);
        QFile f(argv[i]);
        if(f.open(QIODevice::ReadOnly)) {
          QTextStream s(&f);
          QString buf;
          int iLine=0;
          while(!s.atEnd()) {
            buf=s.readLine();
            iLine++;
            if(!buf.isEmpty() && buf[0]!='\n' && buf[0]!='#') {
              StringSection fields(buf);
              StringSection field;
              const QChar * p=0;
              double r1,r2;
              field=fields.nextField(p);
              if(!field.isValid()) {
                App::log(tr("gpspac: error reading minimum radius at line %1 in file %1\n").arg(iLine).arg(argv[i]) );
                return false;
              }
              r1=field.toDouble();
              field=fields.nextField(p);
              if(!field.isValid()) {
                App::log(tr("gpspac: error reading maximum radius at line %1 in file %1\n").arg(iLine).arg(argv[i]) );
                return false;
              }
              r2=field.toDouble();
              _rings.append(AutocorrRing( r1, r2) );
            }
          }
        } else {
          App::log(tr("gpspac: cannot open ring file %1\n").arg(argv[i]) );
          return false;
        }
      } else if(arg=="-rmin") {
        CoreApplication::checkOptionArg(i, argc, argv);
        double r=atof(argv[i]);
        _rings.append(AutocorrRing( r, r) );
      } else if(arg=="-rmax") {
        CoreApplication::checkOptionArg(i, argc, argv);
        double r=atof(argv[i]);
        if(_rings.isEmpty()) {
          _rings.append(AutocorrRing( r, r) );
        } else {
          _rings.last().setRadii(_rings.last().minRadius(), r);
        }
      } else {
        App::log(tr("gpspac: bad option %1, see -help\n").arg(argv[i]) );
        return false;
      }
    } else {
      argv[j++]=argv[i];
    }
  }
  if(j < argc) {
    argv[j]=nullptr;
    argc=j;
  }
  // Check that at least a component has been choosen
  if(!_vertical && !_radial && !_transverse) {
    _vertical=true;
  }
  // Control rings
  if(_rings.isEmpty()) {
    App::log(tr("gpspac: missing ring definition, see -help\n") );
    return false;
  }
  // Compute common sampling scale
  ModalCurve c;
  c.line(FactoryPoint(minRange, 0), FactoryPoint(maxRange, 0));
  c.resample(nSamples, minRange, maxRange, samplingType);
  // Init autocorr factory
  _autocorrFactory=new AutocorrFactory(&_rings);
  _autocorrFactory->setX(c);
  _autocorrFactory->linkX(c);
  if (_vertical>0) {
    for (int i=_rings.count()-1; i>=0; i--) {
      _autocorrFactory->setMode(Mode(Mode::Vertical, i, _nModes-1));
    }
  }
  if (_radial || _transverse) {
    for (int i=_rings.count()-1; i>=0; i--) {
      _autocorrFactory->setMode(Mode(Mode::Radial, i, _nModes-1));
      _autocorrFactory->setMode(Mode(Mode::Transverse, i, _nModes-1));
    }
  }
  _dispersionFactory=new DispersionFactory;
  _dispersionFactory->setX( *_autocorrFactory);
  _dispersionFactory->validate(0, _autocorrFactory);
  _dispersionFactory->setAngularFrequency();
  _autocorrFactory->setAngularFrequency();
  _autocorrFactory->init();
  return true;
}

bool SpacReader::parse(QTextStream& s)
{
  TRACE;
  QTextStream sOut(stdout);
  Seismic1DModel m;
  QString comments;
  if(!m.fromStream(s, &comments)) {
    return false;
  }
  if(m.layerCount()>0) {
    sOut << comments;
    _dispersionFactory->calculate(&m, 0);
    if(_vertical) {
      _autocorrFactory->calculateVertical(_dispersionFactory);
    }
    if(_radial || _transverse) {
      _autocorrFactory->calculateHorizontal(_alpha, _dispersionFactory);
    }
    for(int iRing=0; iRing<_rings.count(); iRing++) {
      if (_vertical) {
        sOut << QString("# Ring %1 (from %2 to %3), %4 Vertical autocorrelation modes\n")
                  .arg(iRing)
                  .arg(_rings.at(iRing).minRadius())
                  .arg(_rings.at(iRing).maxRadius())
                  .arg(_nModes);
        _autocorrFactory->vertical(iRing).toStream(sOut, _oneMode ? _nModes-1 : -1);
      }
      if(_radial) {
        sOut << QString("# Ring %1 (from %2 to %3), %4 Radial autocorrelation modes\n")
                  .arg(iRing)
                  .arg(_rings.at(iRing).minRadius())
                  .arg(_rings.at(iRing).maxRadius())
                  .arg(_nModes);
        _autocorrFactory->radial(iRing).toStream(sOut, _oneMode ? _nModes-1 : -1);
      }
      if(_transverse) {
        sOut << QString("# Ring %1 (from %2 to %3), %4 Transverse autocorrelation modes\n")
                  .arg(iRing)
                  .arg(_rings.at(iRing).minRadius())
                  .arg(_rings.at(iRing).maxRadius())
                  .arg(_nModes);
        _autocorrFactory->transverse(iRing).toStream(sOut, _oneMode ? _nModes-1 : -1);
      }
    }
  }
  return true;
}
