/***************************************************************************
**
**  This file is part of ArrayCore.
**
**  ArrayCore 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.
**
**  ArrayCore 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: 2018-10-04
**  Copyright: 2018-2019
**    Marc Wathelet (ISTerre, Grenoble, France)
**
***************************************************************************/

#include "ActiveConvFKRayleigh.h"
#include "ActiveHRFKRayleighEllipticity.h"
#include "FKGridSearch.h"
#include "FKCrossSpectrum.h"

namespace ArrayCore {

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

    Full description of class still missing
  */

  void ActiveConvFKRayleigh::setGrid(FKGridSearch * g, double step, double size)
  {
    // Binary prograde/retrograde inversion
    g->setGrid(0.0, size, step, -0.25*M_PI, 0.25*M_PI, 0.5*M_PI);
  }

  bool ActiveConvFKRayleigh::setSource(const QVector<int>& blocks)
  {
    if(ActiveConvFKVertical::setSource(blocks)) {
      //_steeringGeometry.setRadialRotations();
      return true;
    } else {
      return false;
    }
  }

  void ActiveConvFKRayleigh::addCrossSpectrum()
  {
    // Correction for geometrical attenuation can also be achieved in steering
    // However, the steering correction is not the same for Conventional and High Resolution
    // Hence, simpler to correct the cross-spectrum.
    //_crossSpectrum->addActiveRayleighCrossSpectrum(_steeringGeometry);
  }

  /*!
    Return true if \a k is close to the limits (<1% relative).
    \a frequency is used only for reporting.
  */
  bool ActiveConvFKRayleigh::isOnLimit(const Point& k, double frequency) const
  {
    return AbstractFKFunction::isOnLimit(Point(k.x(), 0.0, 0.0), frequency);
  }

  double ActiveConvFKRayleigh::value(const Point& kell) const
  {
    double k2=kell.x()*kell.x();
    if(k2>minimumK2() && k2<maximumK2()) {
      //ComplexMatrix e, eh;
      //_steeringGeometry.twoComponentConvRayleighRadial(kell.x(), ::tan(kell.y()), e, eh);
      //return conventionalPower(eh, _crossSpectrum->rayleigh(), e);
    } else {
      return -1.0;
    }
  }

  /*!
    Ellipticity and wavenumber are obtained separately.
    According to Wathelet et al. 2018, PR is minimum at e==-e0
  */
  double ActiveConvFKRayleigh::ellipticity(const Point& kell)
  {
    const ComplexMatrix& covmat=_crossSpectrum->rayleigh();
    FKGridSearch grid;
    ActiveConvFKRayleighVerticalEllipticity * fz;
    fz=new ActiveConvFKRayleighVerticalEllipticity(_gridCache);
    grid.setFunction(fz);
    fz->setK(kell.x());
    fz->setCrossSpectrum(&covmat);
    fz->setGrid(&grid, M_PI/20, 0.5*M_PI);
    grid.globalMax(0.0);
    double ellz=-::tan(grid.pos().x());
    ActiveConvFKRayleighRadialEllipticity * fh;
    fh=new ActiveConvFKRayleighRadialEllipticity(_gridCache);
    grid.setFunction(fh);
    fh->setK(kell.x());
    fh->setCrossSpectrum(&covmat);
    fh->setGrid(&grid, M_PI/20, 0.5*M_PI);
    grid.globalMax(0.0);
    double ellh=-::tan(grid.pos().x());
    double ell=ellipticityHelper(ellh, ellz);
    // kell.y() has the correct sign for ellipticity
    if(kell.y()*ell>0.0) {
      return ell;
    } else {
      return 0.0;
    }
  }

  double ActiveConvFKRayleigh::ellipticityHelper(double ellh, double ellz)
  {
    double ellRatio=ellh/ellz;
    if(ellRatio>=1.0) {
      _RoverN=::sqrt(ellRatio)-1.0;
      double f=1.0+_RoverN;
      ellz*=f;
      ellh/=f;
      // Should never happen
      if(fabs(ellz-ellh)>1e-5) {
        APP_LOG(0, tr("Difference between ellh (%1) and ellz (%2)=%3\n").arg(ellh).arg(ellz).arg(ellz-ellh))
        return 0.0;
      } else {
        return ellh;
      }
    } else {
      _RoverN=std::numeric_limits<double>::infinity();
      return 0.0;
    }
  }

  double ActiveConvFKRayleigh::noise(const Point&) const
  {
    return _crossSpectrum->stationCount()*_RoverN;
  }


} // namespace ArrayCore

