/***************************************************************************
**
**  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-07-12
**  Copyright: 2018-2019
**    Marc Wathelet (ISTerre, Grenoble, France)
**
***************************************************************************/

#include "HRFKDirectRayleighRefined.h"
#include "FKCrossSpectrum.h"
#include "FKGridSearch.h"
#include "HRFKRayleighEllipticity.h"
#include "HRFKDirectRayleighFixedEll.h"
#include "FKResults.h"
#include "WaveNumberConverter.h"

namespace ArrayCore {

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

    Full description of class still missing
  */

  /*!
    Adjust k with a better estimation of ellipticity that the raw maximization of power
  */
  bool HRFKDirectRayleighRefined::refine(const Point2D & k, double ell,
                                        FKPeaks * results,
                                        const WaveNumberConverter& conv)
  {
    FKGridSearch grid;
    HRFKDirectRayleighFixedEll * f=new HRFKDirectRayleighFixedEll(_gridCache);
    grid.setFunction(f);
    f->setEllipticity(ell);
    //double refineRange=_crossSpectrum->parameters()->refineRange();
    double gridStep=_crossSpectrum->parameters()->gridStep();
    double refineRange=0.2;
    double dk=k.length()*refineRange;
    if(dk<2.0*gridStep) {
      dk=2.0*gridStep;
    }
    Rect limits(k.x()-dk, k.y()-dk, k.x()+dk, k.y()+dk);
    grid.setGrid(limits.x1(), limits.x2(), gridStep, limits.y1(), limits.y2(), gridStep);
    // Avoid peaks that are outside refineRange
    double minDk=0.05*dk;
    limits.enlarge(-minDk, -minDk, LinearScale, LinearScale);
    if(fabs(conv.frequency()-3.65704)<0.01) {
      printf("Freq to debug\n");
    }
    grid.localMax(INT_MAX);
    double slow=conv.slowness(k);
    double ellDeg=Angle::radiansToDegrees(::atan(ell));
    APP_LOG(5, tr("%1 Hz: refining at %2 s/m with ellipticity %3 deg, found %4 maxima\n")
            .arg(conv.frequency())
            .arg(slow)
            .arg(ellDeg)
            .arg(grid.maximaCount()))
    FunctionMaximaIterator it;
    for(it=grid.begin(); it!=grid.end(); ++it) {
      const FunctionSearchMaximum& m=**it;
      Point2D newk=m.pos();
      if(limits.includes(newk)) {
        double newSlow=conv.slowness(newk);
        APP_LOG(6, tr("%1 Hz: refined %2 s/m with ellipticity %3 deg to %4 m/s (%5 %)\n")
                .arg(conv.frequency())
                .arg(slow)
                .arg(ellDeg)
                .arg(newSlow)
                .arg((newSlow-slow)/slow*100.0, 0, 'f', 2))
        results->add(newk, ell, noise(newk), m.value(), conv, minimumSlowness());
      }
    }
    return true;
  }

} // namespace ArrayCore

