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

#include <QGpCoreWave.h>

#include "DispersionReader.h"

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

  Full description of class still missing
*/

/*!
  Description of constructor still missing
*/
DispersionReader::DispersionReader()
    : ArgumentStdinReader()
{
  TRACE;
  _nRayleigh=1;
  _nLove=0;
  _groupSlowness=false;
  _sampling.setType(SamplingParameters::Log);
  _sampling.setRange(0.2, 20.0);
  _sampling.setStep(1.025);
  _vNSamples=100;
  _vMinRange=100.0;
  _vMaxRange=3000.0;
  _mode=CurveMode;
  _oneMode=false;
  _force=false;
  _deltaK=false;
  _halfSpaceIndex=0;
}

DispersionReader::~DispersionReader()
{

}

bool DispersionReader::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=="-grid") {
        CoreApplication::checkOptionArg(i, argc, argv);
        if(argv[i][0]=='R') {
          _nLove=0;
          _nRayleigh=1;
        } else {
          _nLove=1;
          _nRayleigh=0;
        }
        _mode=GridMode;
      } else if(arg=="-vmin") {
        CoreApplication::checkOptionArg(i, argc, argv);
        _vMinRange=atof(argv[i]);
        if(_vMinRange<=0) {
          App::log(tr("gpdc: negative or null value for -vmin\n") );
          return false;
        }
      } else if(arg=="-vmax") {
        CoreApplication::checkOptionArg(i, argc, argv);
        _vMaxRange=atof(argv[i]);
        if(_vMaxRange<=0) {
          App::log(tr("gpdc: negative or null value for -vmax\n") );
          return false;
        }
      } else if(arg=="-vn") {
        CoreApplication::checkOptionArg(i, argc, argv);
        _vNSamples=atoi(argv[i]);
        if(_vNSamples<=0) {
          App::log(tr("gpdc: negative or null number of samples (option -vn)\n") );
          return false;
        }
      } else if(arg=="-R") {
        CoreApplication::checkOptionArg(i, argc, argv);
        _nRayleigh=atoi(argv[i]);
      } else if(arg=="-L") {
        CoreApplication::checkOptionArg(i, argc, argv);
        _nLove=atoi(argv[i]);
      } else if(arg=="-one-mode") {
        _oneMode=true;
      } else if(arg=="-delta-k") {
        _deltaK=true;
      } else if(arg=="-f") {
        _force=true;
      } else if(arg=="-group") {
        _groupSlowness=true;
      } else if(arg=="-s") {
        CoreApplication::checkOptionArg(i, argc, argv);
        _sampling.setType(argv[i]);
      } else if(arg=="-min") {
        CoreApplication::checkOptionArg(i, argc, argv);
        _sampling.setMinimum(atof(argv[i]));
      } else if(arg=="-max") {
        CoreApplication::checkOptionArg(i, argc, argv);
        _sampling.setMaximum(atof(argv[i]));
      } else if(arg=="-step") {
        CoreApplication::checkOptionArg(i, argc, argv);
        _sampling.setStep(atof(argv[i]));
      } else if(arg=="-n") {
        CoreApplication::checkOptionArg(i, argc, argv);
        _sampling.setCount(atoi(argv[i]));
      } else if(arg=="-half-space") {
        CoreApplication::checkOptionArg(i, argc, argv);
        _halfSpaceIndex=atoi(argv[i]);
        _mode=HalfSpace;
      } else {
        App::log(tr("gpdc: bad option %1, see -help\n").arg(argv[i]) );
        return false;
      }
    } else {
      argv[j++]=argv[i];
    }
  }
  if(j < argc) {
    argv[j]=nullptr;
    argc=j;
  }
  // Common sampling scale
  _x=_sampling.values();
  // convert to angular frequency
  for(int i=_x.count()-1; i>=0; i--) {
    _x[i]*=2.0*M_PI;
  }
  // Check arguments
  if(_deltaK) {
    if(_nRayleigh>0) {
      if(_nRayleigh<2) {
        App::log(tr("gpdc: wiht option -delta-k, the minimum number of Rayleigh modes is 2\n") );
        return false;
      }
    }
    if(_nLove>0) {
      if(_nLove<2) {
        App::log(tr("gpdc: wiht option -delta-k, the minimum number of Love modes is 2\n") );
        return false;
      }
    }
  }
  return true;
}

bool DispersionReader::parse(QTextStream& s)
{
  TRACE;
  QTextStream sOut(stdout);
  Seismic1DModel m;
  QString comments;
  if(!m.fromStream(s, &comments)) {
    return false;
  }
  if(m.layerCount()>0) {
    switch(_mode) {
    case GridMode: {
        IrregularGrid2D grid;
        grid.init(_sampling.count(), _vNSamples, 0.0);
        grid.setLinear(YAxis, 1.0/_vMaxRange, 1.0/_vMinRange);
        grid.setLog(XAxis, _sampling.minimum(), _sampling.maximum());
        if(_nLove>_nRayleigh) {
          Love love(&m);
          for(int ix=0; ix<_sampling.count(); ix++) {
            double f=grid.x(ix);
            love.setOmega(2.0*M_PI*f);
            double factor=1.0/(f*f);
            for(int iy=0; iy<_vNSamples; iy++) {
              grid.setValue(ix, iy, love.y(grid.y(iy))*factor);
            }
          }
        } else {
          Rayleigh rayleigh(&m);
          for(int ix=0; ix<_sampling.count(); ix++) {
            double f=grid.x(ix);
            rayleigh.setOmega(2.0*M_PI*f);
            double factor=1.0/(f*f);
            for(int iy=0; iy<_vNSamples; iy++) {
              grid.setValue(ix, iy, rayleigh.y(grid.y(iy))*factor);
            }
          }
        }
        sOut << comments;
        sOut << grid;
      }
      break;
    case CurveMode:
      if(_nRayleigh>0) {
        Rayleigh rayleigh(&m);
        QElapsedTimer t;
        t.start();
        Dispersion dispersion (_nRayleigh, &_x);
        if(dispersion.calculate(&rayleigh, nullptr)) {
          if(_groupSlowness) {
            dispersion.setGroupSlowness();
          }
          int dt=t.elapsed();
          sOut << QString("# %1 Rayleigh dispersion mode(s)\n").arg(_nRayleigh);
          sOut << QString("# CPU Time=%1 ms\n").arg(dt);
          if(_deltaK) {
            ModalStorage * dk=dispersion.deltaK();
            dk->toStream(sOut, _oneMode ? _nRayleigh-2 : -1, comments);
            delete dk;
          } else {
            dispersion.toStream(sOut, _oneMode ? _nRayleigh-1 : -1, comments);
          }
        } else {
          int dt=t.elapsed();
          sOut << comments;
          sOut << QString("# %1 Rayleigh dispersion mode(s)\n").arg(_nRayleigh);
          sOut << QString("# CPU Time=%1 ms\n").arg(dt);
          sOut << QString("# Failed: cannot compute dispersion curves\n");
          if(!_force) {
            return false;
          }
        }
      }
      if(_nLove>0) {
        Love love(&m);
        QElapsedTimer t;
        t.start();
        Dispersion dispersion (_nLove, &_x);
        if(dispersion.calculate(&love)) {
          if(_groupSlowness) {
            dispersion.setGroupSlowness();
          }
          int dt=t.elapsed();
          sOut << comments;
          sOut << QString("# %1 Love dispersion mode(s)\n").arg(_nLove);
          sOut << QString("# CPU Time=%1 ms\n").arg(dt);
          if(_deltaK) {
            ModalStorage * dk=dispersion.deltaK();
            dk->toStream(sOut, _oneMode ? _nLove-2 : -1);
            delete dk;
          } else {
            dispersion.toStream(sOut, _oneMode ? _nLove-1 : -1);
          }
        } else {
          int dt=t.elapsed();
          sOut << comments;
          sOut << QString("# %1 Love dispersion mode(s)\n").arg(_nLove);
          sOut << QString("# CPU Time=%1 ms\n").arg(dt);
          sOut << QString("# Failed: cannot compute dispersion curves\n");
          if(!_force) {
            return false;
          }
        }
      }
      break;
    case HalfSpace:
      if(_halfSpaceIndex<m.layerCount()) {
        sOut << m.halfSpaceRayleighSlowness(_halfSpaceIndex) << "\n";
      } else {
        App::log(tr("gpdc: half-space index out of range\n") );
        return false;
      }
      break;
    }
  }
  return true;
}
