/***************************************************************************
**
**  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: 2009-02-04
**  Copyright: 2009-2019
**    Marc Wathelet
**    Marc Wathelet (LGIT, Grenoble, France)
**
***************************************************************************/

#ifndef AUTOCORRENGINE_H
#define AUTOCORRENGINE_H

#include <QGpCoreTools.h>

#include "QGpCoreWaveDLLExport.h"
#include "AutocorrRing.h"

namespace QGpCoreWave {

class QGPCOREWAVE_EXPORT AutocorrEngine
{
public:
  inline void setVerticalRing(const AutocorrRing& r);
  inline void setHorizontalRing(const AutocorrRing& r);

  bool isThinRing() {return fabs(_dr)<0.02*_r0;}

  inline RealValue verticalThinRing(double omega, const RealValue& dispR);
  inline RealValue verticalThickRing(double omega, const RealValue& dispR);

  inline double verticalThinRing(double k);
  inline double verticalThickRing(double k);

  inline bool initHorizontalThinRing(double omega, const RealValue& dispR , const RealValue& dispL);
  inline bool initHorizontalThickRing(double omega, const RealValue& dispR , const RealValue& dispL);

  inline RealValue radial(double alphaRayleigh, double alphaLove);
  inline RealValue transverse(double alphaRayleigh, double alphaLove);

  double rayleighRadial() const {return _jRR;}
  double rayleighTransverse() const {return _jRT;}
  double loveRadial() const {return _jLR;}
  double loveTransverse() const {return _jLT;}
private:
  double _r1, _r2, _r0, _dr, _rFactor;
  double _jRR, _jLR, _jRT, _jLT;
};

inline void AutocorrEngine::setVerticalRing(const AutocorrRing& ring)
{
  TRACE;
  _r1=ring.minRadius();
  _r2=ring.maxRadius();
  _r0=0.5 * (_r1+_r2);
  _dr=_r2-_r1;
  _rFactor=_r0*_dr;  // (r2^2-r1^2)/2
}

inline void AutocorrEngine::setHorizontalRing(const AutocorrRing& ring)
{
  TRACE;
  _r1=ring.minRadius();
  _r2=ring.maxRadius();
  _r0=0.5*(_r1+_r2);
  _dr=_r2-_r1;
  _rFactor=2.0/(_r0*_dr);  // 4/(r2^2-r1^2)
}

inline RealValue AutocorrEngine::verticalThinRing(double omega, const RealValue& dispR)
{
  if(dispR.isValid()) {
    return RealValue(verticalThinRing(omega*dispR.value()));
  } else {
    return RealValue();
  }
}

inline double AutocorrEngine::verticalThinRing(double k)
{
  return j0(k*_r0);
}

inline RealValue AutocorrEngine::verticalThickRing(double omega, const RealValue& dispR)
{
  TRACE;
  if(dispR.isValid()) {
    return RealValue(verticalThickRing(omega*dispR.value()));
  } else {
    return RealValue();
  }
}

inline double AutocorrEngine::verticalThickRing(double k)
{
  return (_r2*j1(k*_r2)-_r1*j1(k*_r1))/(k*_rFactor);
}

inline bool AutocorrEngine::initHorizontalThinRing(double omega,
                                                   const RealValue& dispR,
                                                   const RealValue& dispL)
{
  if(dispR .isValid() && dispL.isValid()) {
    double omegaR=omega*_r0;
    double argR=omegaR*dispR.value();
    double j0R=j0(argR);
    double j2R=jn(2, argR);
    double argL=omegaR*dispL.value();
    double j0L=j0(argL);
    double j2L=jn(2, argL);
    _jRR=j0R-j2R;
    _jLR=j0L+j2L;
    _jRT=j0R+j2R;
    _jLT=j0L-j2L;
    return true;
  } else {
    return false;
  }
}

inline bool AutocorrEngine::initHorizontalThickRing(double omega,
                                                    const RealValue& dispR,
                                                    const RealValue& dispL)
{
  if(dispR.isValid() && dispL.isValid()) {
    double omegaR1=omega*_r1;
    double omegaR2=omega*_r2;

    double invKR=1.0/(omega*dispR.value());
    double argR1=omegaR1*dispR.value();
    double argR2=omegaR2*dispR.value();
    double j0R1=j0(argR1);
    double j1R1=j1(argR1);
    double j0R2=j0(argR2);
    double j1R2=j1(argR2);

    double invKL=1.0/(omega*dispL.value());
    double argL1=omegaR1*dispL.value();
    double argL2=omegaR2*dispL.value();
    double j0L1=j0(argL1);
    double j1L1=j1(argL1);
    double j0L2=j0(argL2);
    double j1L2=j1(argL2);

    _jRR=_rFactor*invKR*(invKR*(j0R2-j0R1)+_r2*j1R2-_r1*j1R1);
    _jLR=_rFactor*invKL*invKL*(j0L1-j0L2);
    _jRT=_rFactor*invKR*invKR*(j0R1-j0R2);
    _jLT=_rFactor*invKL*(invKL*(j0L2-j0L1)+_r2*j1L2-_r1*j1L1);
    return true;
  } else {
    return false;
  }
}

inline RealValue AutocorrEngine::radial(double alphaRayleigh, double alphaLove)
{
  return RealValue(alphaRayleigh*_jRR +alphaLove*_jLR);
}

inline RealValue AutocorrEngine::transverse(double alphaRayleigh, double alphaLove)
{
  return RealValue(alphaRayleigh*_jRT+alphaLove*_jLT);
}

} // namespace QGpCoreWave

#endif // AUTOCORRENGINE_H
