/***************************************************************************
**
**  This file is part of QGpCoreWave.
**
**  This library is free software; you can redistribute it and/or
**  modify it under the terms of the GNU Lesser General Public
**  License as published by the Free Software Foundation; either
**  version 2.1 of the License, or (at your option) any later version.
**
**  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 Lesser General Public
**  License for more details.
**
**  You should have received a copy of the GNU Lesser General Public
**  License along with this library; if not, write to the Free Software
**  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
**
**  See http://www.geopsy.org for more information.
**
**  Created: 2004-04-28
**  Copyright: 2004-2019
**    Marc Wathelet
**    Marc Wathelet (ULg, Liège, Belgium)
**    Marc Wathelet (LGIT, Grenoble, France)
**
***************************************************************************/

#ifndef SEISMIC1DMODEL_H
#define SEISMIC1DMODEL_H

#include <QGpCoreTools.h>

#include "QGpCoreWaveDLLExport.h"
#include "GeophysicalModel.h"
#include "Profile.h"

namespace QGpCoreWave {

  class QGPCOREWAVE_EXPORT Seismic1DModel: public GeophysicalModel
{
  TRANSLATIONS("Seismic1DModel")
public:
  Seismic1DModel();
  Seismic1DModel(const Seismic1DModel& o);
  Seismic1DModel(int layerCount);
  Seismic1DModel(int layerCount, double * h, double * vp, double * vs, double * rho);
  Seismic1DModel(int layerCount, double * h, double * vs, double * rho);
  ~Seismic1DModel();

  enum SlownessType {P, S};

  virtual GeophysicalModel * clone() const {return new Seismic1DModel(*this);}

  void operator=(const Seismic1DModel& o);

  double maxSlowR() const {return _maxRayleighSlowness;}
  double minSlowS() const {return _slowSMin;}
  double maxSlowS() const {return _slowSMax;}
  bool initCalculation();
  virtual bool isValid() {return initCalculation();}
  int checkVelocityInversion() const;

  void setH(int iLayer, double v) {_h[iLayer]=v;}
  void setSlowP(int iLayer, double v) {_slowP[iLayer]=v;}
  void setSlowS(int iLayer, double v) {_slowS[iLayer]=v;}
  void setRho(int iLayer, double v) {_rho[iLayer]=v;}
  void setQp(int iLayer, double v) {_qp[iLayer]=v;}
  void setQs(int iLayer, double v) {_qs[iLayer]=v;}
  void setQp(double v);
  void setQs(double v);

  int layerCount() const {return _layerCount;}
  virtual bool isEmpty() const {return layerCount()==0;}
  double h(int iLayer) const {return _h[iLayer];}
  double slowP(int iLayer) const {return _slowP[iLayer];}
  double slowS(int iLayer) const {return _slowS[iLayer];}
  double rho(int iLayer) const {return _rho[iLayer];}
  double mu(int iLayer) const {return _mu[iLayer];}
  double qp(int iLayer) const {return _qp[iLayer];}
  double qs(int iLayer) const {return _qs[iLayer];}
  double poisson(int iLayer) const;
  double depth(int iLayer) const;
  double expGrad(int iLayer) const;
  double halfSpaceRayleighSlowness(int iLayer) const;
  Complex halfSpaceEllipticity(int iLayer) const;
  bool elastic() const;

  double vpAt(double depth) const;
  double vsAt(double depth) const;
  double rhoAt(double depth) const;
  double poissonAt(double depth) const;
  double impedanceAt(double depth) const;
  double slowPAt(double depth) const;
  double slowSAt(double depth) const;
  double roughFundamentalFrequency() const;

  Profile slowpProfile() const;
  inline Profile vpProfile() const;
  Profile slowsProfile() const;
  inline Profile vsProfile() const;
  Profile rhoProfile() const;
  Profile poissonProfile() const;
  Profile impedanceProfile() const;

  static QString formatHelp();
  virtual QString toString() const;
  virtual bool fromStream(QTextStream& s, QString * comments=0);
  virtual void toStream(QTextStream& s) const;
  void exportHerrmann(QTextStream& s) const;
  void toStream(QDataStream& s) const;
  void fromStream(QDataStream& s);

  void changeHKeepOtherDepth(int iLayer,double newH);
  void changeVpKeepPoisson(int iLayer,double newV);
  void changeVsKeepPoisson(int iLayer,double newV);
  void setHFrom(const Seismic1DModel& o);
  bool interpole(const Point& at, const Point * refPoints, const Seismic1DModel * refModels);

  class SeismicContext : public GeophysicalContext
  {
  public:
    SeismicContext();
    Seismic1DModel * model() const {return static_cast<Seismic1DModel *>(_model);}
    virtual QString helpCode() const;
  };

  virtual GeophysicalContext * expressionContext() const {return new SeismicContext;}

  class SeismicStorage : public ExpressionStorage
  {
  public:
    SeismicStorage(SeismicContext * context) {_context=context;}
    virtual bool isReadOnly() const {return false;}
  protected:
    SeismicContext * _context;
  };

  class VariableN : public SeismicStorage
  {
  public:
    VariableN(SeismicContext * context) : SeismicStorage(context) {}
    virtual bool isReadOnly() const {return true;}
    virtual QVariant value(const QString&) const;
    virtual void setValue(const QString&, const QVariant& ) {}
  };

  class VariableH : public SeismicStorage
  {
  public:
    VariableH(SeismicContext * context) : SeismicStorage(context) {}
    virtual QVariant value(const QString&) const;
    virtual void setValue(const QString&, const QVariant& );
  };

  class VariableVp : public SeismicStorage
  {
  public:
    VariableVp(SeismicContext * context) : SeismicStorage(context) {}
    virtual QVariant value(const QString& index) const;
    virtual void setValue(const QString&, const QVariant& );
  };

  class VariableVs : public SeismicStorage
  {
  public:
    VariableVs(SeismicContext * context) : SeismicStorage(context) {}
    virtual QVariant value(const QString& index) const;
    virtual void setValue(const QString&, const QVariant& );
  };

  class VariableRho : public SeismicStorage
  {
  public:
    VariableRho(SeismicContext * context) : SeismicStorage(context) {}
    virtual QVariant value(const QString& index) const;
    virtual void setValue(const QString&, const QVariant& );
  };

  class VariableQp : public SeismicStorage
  {
  public:
    VariableQp(SeismicContext * context) : SeismicStorage(context) {}
    virtual QVariant value(const QString& index) const;
    virtual void setValue(const QString&, const QVariant& );
  };

  class VariableQs : public SeismicStorage
  {
  public:
    VariableQs(SeismicContext * context) : SeismicStorage(context) {}
    virtual QVariant value(const QString& index) const;
    virtual void setValue(const QString&, const QVariant& );
  };
protected:
  void allocateData();
  void deleteData();
private:
  int _layerCount;
  double *_h;
  double *_slowP;
  double *_slowS;
  double *_rho;
  double *_mu;
  double *_qp;
  double *_qs;
  double _maxRayleighSlowness;
  double _slowSMin;
  double _slowSMax;

  bool setValue(double& var, const StringSection& field, int iLayer, const QString& varName, bool optional);
  void initMembers();
  bool checkPoissonRatio();
  void slowMinMax(int& iSlowSMax);
};

inline Profile Seismic1DModel::vpProfile() const
{
  Profile p=slowpProfile();
  p.inverse();
  return p;
}

inline Profile Seismic1DModel::vsProfile() const
{
  Profile p=slowsProfile();
  p.inverse();
  return p;
}

} // namespace QGpCoreWave

#endif // SEISMIC1DMODEL_H
