/***************************************************************************
**
**  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: 2005-01-06
**  Copyright: 2005-2019
**    Marc Wathelet
**    Marc Wathelet (ULg, Liège, Belgium)
**    Marc Wathelet (LGIT, Grenoble, France)
**    Phil Cummins (RSES, Canberra, Australia)
**
***************************************************************************/

#include <QGpCoreTools.h>

#include "FortranInterface.h"
#include "Seismic1DModel.h"
#include "Rayleigh.h"
#include "Dispersion.h"

// Compatibility layer with Microsoft Visual Studio
// DLL compiled with mingw can be linked in Visual Studio by simply renaming libname.a into name.lib

#ifdef WIN32
void DISPERSION_CURVE_INIT(int * verbose)
{
  dispersion_curve_init_(verbose);
}

void DISPERSION_CURVE_RAYLEIGH(int * nLayers, double * h, double * vp, double * vs, double * rho,
                                         int * nSamples, double * omega,
                                         int * nModes, double * slowness, int * group)
{
  dispersion_curve_rayleigh_(nLayers, h, vp, vs, rho, nSamples, omega, nModes, slowness, group);
}

void DISPERSION_CURVE_LOVE(int * nLayers, double * h, double * vs, double * rho,
                                     int * nSamples, double * omega,
                                     int * nModes, double * slowness, int * group)
{
  dispersion_curve_love_(nLayers, h, vs, rho, nSamples, omega, nModes, slowness, group);
}

void ELLIPTICITY_CURVE(int * nLayers, double * h, double * vp, double * vs, double * rho,
                       int * nSamples, double * omega,
                       int * nModes, double * ell, int * ipeak)
{
  ellipticity_curve_(nLayers, h, vp, vs, rho, nSamples, omega, nModes, ell, ipeak);
}

void DISPERSION_CURVE_INIT_INTEL(int * verbose)
{
  dispersion_curve_init_(verbose);
}

void DISPERSION_CURVE_RAYLEIGH_INTEL(int * nLayers, double * h, double * vp, double * vs, double * rho,
                                     int * nSamples, double * omega,
                                     int * nModes, double * slowness, int * group)
{
  dispersion_curve_rayleigh_(nLayers, h, vp, vs, rho, nSamples, omega, nModes, slowness, group);
}

void DISPERSION_CURVE_LOVE_INTEL(int * nLayers, double * h, double * vs, double * rho,
                                 int * nSamples, double * omega,
                                 int * nModes, double * slowness, int * group)
{
  dispersion_curve_love_(nLayers, h, vs, rho, nSamples, omega, nModes, slowness, group);
}

void ELLIPTICITY_CURVE_INTEL(int * nLayers, double * h, double * vp, double * vs, double * rho,
                             int * nSamples, double * omega,
                             int * nModes, double * ell, int * ipeak)
{
  ellipticity_curve_(nLayers, h, vp, vs, rho, nSamples, omega, nModes, ell, ipeak);
}
#endif

/*!
  If \a verbose is null, warnings are switched off.
*/
void dispersion_curve_init_(int * verbose)
{
  new PluginCoreApplication;
  if(*verbose==0) {
    PluginCoreApplication::instance()->freezeStream(true);
  }
}

/*!
  Computes Rayleigh dispersion curve
*/
void dispersion_curve_rayleigh_(int * nLayers, double * h, double * vp, double * vs, double * rho,
                                int * nSamples, double * omega,
                                int * nModes, double * slowness, int * group)
{
  QGpCoreWave::Seismic1DModel m(*nLayers, h, vp, vs, rho);
  QGpCoreWave::Rayleigh rayleigh(&m);
  //App::log("nSamples=" << *nSamples << ", nModes=" << *nModes << ", group=" << *group << endl;
  QVector<double> x(*nSamples);
  for(int i=0; i<*nSamples; i++) {
    x[i]=omega[i];
    //App::log("frequency[" << i << "]=" << x[i]/(2*M_PI) << " Hz" << endl;
  }
  QGpCoreWave::Dispersion dispersion (*nModes, &x);
  dispersion.calculate(&rayleigh, 0);
  if(*group!=0) {
    dispersion.setGroupSlowness();
  }
  for(int iMode=0; iMode<*nModes; iMode++) {
    //App::log("Mode=" << iMode << endl;
    const RealValue * mode=dispersion.mode(iMode);
    for(int iSample=0; iSample<*nSamples; iSample++) {
      const RealValue& v=mode[iSample];
      if(v.isValid()) {
        *(slowness++)=v.value();
        /*App::log("frequency[" << iSample << "]=" << x[iSample]/(2*M_PI) << " Hz"
                      << ", slowness[" << iSample << "]=" << v.value()
                      << ", velocity[" << iSample << "]=" << 1.0/v.value() << endl;*/
      } else {
        *(slowness++)=0.0;
      }
    }
  }
}

/*!
  Computes Love dispersion curve
*/
void dispersion_curve_love_(int * nLayers, double * h, double * vs, double * rho,
                            int * nSamples, double * omega,
                            int * nModes, double * slowness, int * group)
{
  QGpCoreWave::Seismic1DModel m(*nLayers, h, vs, rho);
  QGpCoreWave::Love love(&m);
  QVector<double> x(*nSamples);
  for(int i=0; i<*nSamples; i++) {
    x[i]=omega[i];
  }
  QGpCoreWave::Dispersion dispersion (*nModes, &x);
  dispersion.calculate(&love, 0);
  if(*group!=0) {
    dispersion.setGroupSlowness();
  }
  for(int iMode=0; iMode<*nModes; iMode++) {
    const RealValue * mode=dispersion.mode(iMode);
    for(int iSample=0; iSample<*nSamples; iSample++) {
      const RealValue& v=mode[iSample];
      if(v.isValid()) {
        *(slowness++)=v.value();
      } else {
        *(slowness++)=0.0;
      }
    }
  }
}

/*!
  Computes Rayleigh ellipticity curve

  \a doPeaks computes exact frequency and refines curve around the peak. Actual peak
  frequencies are currently not returned through this interface.
*/
void ellipticity_curve_(int * nLayers, double * h, double * vp, double * vs, double * rho,
                        int * nSamples, double * omega,
                        int * nModes, double * ellipticity, int * doPeaks)
{
  QGpCoreWave::Seismic1DModel m(*nLayers, h, vp, vs, rho);
  QGpCoreWave::Rayleigh rayleigh(&m);
  QVector<double> x(*nSamples);
  for(int i=0; i<*nSamples; i++) {
    x[i]=omega[i];
  }

  QGpCoreWave::Dispersion dispersion (*nModes, &x);
  dispersion.setPrecision(1e-12);
  QGpCoreWave::Ellipticity ell(*nModes, &x);

  dispersion.calculate(&rayleigh, &ell);
  if(*doPeaks!=0) {
    for(int iMode=0; iMode<*nModes; iMode++) {
       ell.peaks(iMode, dispersion, &rayleigh);
    }
  }
  ell.abs();
  //double xFactor = 0.5/M_PI;
  for (int im=0; im< *nModes; im++) {
    QGpCoreWave::Ellipticity::Iterator it(&ell, im);
    for (int i=0; !it.atEnd(); ++it,i++) {
      const RealValue& val=it.value();
      if (val.isValid()) {
        //std::cout << it.x()*xFactor << " " << val.value() << "\n" << std::flush;
        ellipticity[i]=val.value();
      } else {
        ellipticity[i]=0.;
      }
    }
  }
}

void rayleigh_determinant_(int * nLayers, double * h, double * vp, double * vs, double * rho,
                           double * omega,  double * slowness, double * det)
{
  QGpCoreWave::Seismic1DModel m(*nLayers, h, vp, vs, rho);
  QGpCoreWave::Rayleigh rayleigh(&m);
  rayleigh.setOmega(*omega);
  *det=rayleigh.y(*slowness);
}

void love_determinant_(int * nLayers, double * h, double * vs, double * rho,
                       double * omega,  double * slowness, double * det)
{
  QGpCoreWave::Seismic1DModel m(*nLayers, h, vs, rho);
  QGpCoreWave::Love love(&m);
  love.setOmega(*omega);
  *det=love.y(*slowness);
}
