/***************************************************************************
**
**  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: 2017-02-14
**  Copyright: 2017-2019
**    Marc Wathelet (ISTerre, Grenoble, France)
**
***************************************************************************/

#include "FKTool.h"
#include "KmaxSolver.h"
#include "KminSolver.h"
#include "FKResults.h"

namespace ArrayCore {

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

    Full description of class still missing
  */

  /*!
    Description of constructor still missing
  */
  FKTool::FKTool()
    : AbstractArrayTool()
  {
    TRACE;
    _kmaxSolver=nullptr;
    setResults(new FKResults);
    // Construct a default parameter structure.
    // For instance setWaveNumberGrid() can be used even if setParameters()
    // is not used before.
    AbstractArrayTool::setParameters(FKParameters());
  }

  /*!
    Description of destructor still missing
  */
  FKTool::~FKTool()
  {
    TRACE;
    delete _kmaxSolver;
  }

  bool FKTool::setSubPool(SubSignalPool *subPool)
  {
    TRACE;
    if(AbstractArrayTool::setSubPool(subPool)) {
      return true;
    } else {
      return false;
    }
  }

  bool FKTool::setParameters(const AbstractParameters &param)
  {
    TRACE;
    const FKParameters * fkParam=dynamic_cast<const FKParameters *>(&param);
    if(!fkParam) {
      qDebug() << "Parameters are not FKParameters in FKTool::setParameters.";
      return false;
    }

    if(!AbstractArrayTool::setParameters(param)) {
      return false;
    }

    FKParameters * myParam=parameters();
    if(myParam->kmin()==0.0 ||
       myParam->kmax()==0.0) {
      setWaveNumberGrid();
    }

    if(results()) {
      results()->setMinimumSlowness(myParam->minimumSlowness());
    }
    // At this step parameters are fine and they will be no longer modified
    return setLoop();
  }

  /*!
    Called automatically by setParameters if minimum and maximum
    waveNumber are not set.
  */
  bool FKTool::setWaveNumberGrid()
  {
    TRACE;
    if(_kmaxSolver) {
      _kmaxSolver->terminate();
      _kmaxSolver->deleteLater();
      _kmaxSolver=nullptr;
    }

    QVector<Point2D> stations;
    int n=array()->count();
    for(int i=0; i<n; i++) {
      stations.append(Point2D(array()->relativePos(i)));
    }
    // Estimate kmin and kmax from station coordinates
    KminSolver kminSolver(stations);
    bool ok=true;
    double kmin=kminSolver.calculate(ok);
    if (ok && kmin>0.0) {
      App::log(tr("Computed kmin=%1 rad/m\n").arg(kmin));
      parameters()->setKmin(kmin);
      _kmaxSolver=new KmaxSolver(stations, kmin, 0.25);
      connect(_kmaxSolver, SIGNAL(finished()), this, SLOT(setComputedKmax()));
      if(!_kmaxSolver->calculate()) {
        setComputedKmax(_kmaxSolver); // 1D array
      }
    } else {
      App::log(tr("Error computing kmin for station coordinates\n"));
      return false;
    }
    return true;
  }

  void FKTool::setComputedKmax(KmaxSolver * solver)
  {
    TRACE;
    // Upon termination, _kmaxSolver might be replaced by another solver
    // and this function called by an old event still in the stack
    // Make sure that it still refers to the current solver.
    if(!solver) {
      solver=qobject_cast<KmaxSolver *>(sender());
    }
    if(solver && _kmaxSolver==solver) {
      bool ok;
      double kmax=_kmaxSolver->kmax(ok);
      if(ok) {
        App::log(tr("Computed kmax=%1 rad/m\n").arg(kmax) );
        parameters()->setKmax(kmax);
        emit waveNumberGridReady();
      } else {
        App::log(tr("Error computing kmax for station coordinates\n") );
      }
      _kmaxSolver->deleteLater();
      _kmaxSolver=nullptr;
    }
  }  

  void FKTool::start()
  {
    TRACE;
    waitFinished();
    AbstractArrayTool::start();
  }

  void FKTool::waitFinished()
  {
    TRACE;
    if(_kmaxSolver) {
      App::log(tr("Waiting for Kmax computation...\n") );
      _kmaxSolver->wait();
      CoreApplication::processEvents();
    }
    AbstractArrayTool::waitFinished();
  }


} // namespace ArrayCore

