/***************************************************************************
**
**  This file is part of spac2disp.
**
**  spac2disp 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.
**
**  spac2disp 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: 2007-06-26
**  Copyright: 2007-2019
**    Marc Wathelet
**    Marc Wathelet (LGIT, Grenoble, France)
**
***************************************************************************/

#include "Spac3CForward.h"

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

  Full description of class still missing
*/

Spac3CForward::Spac3CForward()
{
  TRACE;
  _dispFactory=nullptr;
  _autocorrFactory=nullptr;
  _iOmega=0;
}

Spac3CForward::Spac3CForward(const Spac3CForward& o)
{
  TRACE;
  _dispFactory=nullptr;
  _autocorrFactory=nullptr;
  setCurves(o._curves);
  setFrequency(o._iOmega);
}

Spac3CForward::~Spac3CForward()
{
  delete _dispFactory;
  delete _autocorrFactory;
}

AbstractForward * Spac3CForward::clone() const
{
  TRACE;
  Spac3CForward * f=new Spac3CForward(*this);

  f->parameterSpace().addParameter("Sr", "m/s", 0.0002, 0.007, ParameterGrid::Linear, 0.000025);
  f->parameterSpace().addParameter("Sl", "m/s", 0.0002, 0.007, ParameterGrid::Linear, 0.000025);
  f->parameterSpace().addParameter("Alpha", "", 0.0, 1.0, ParameterGrid::Linear, 0.01);
  return f;
}

void Spac3CForward::setCurves(const AutocorrCurves& autocorr)
{
  TRACE;
  _curves=autocorr;

  _curveList=_curves.curves();

  delete _dispFactory;
  delete _autocorrFactory;

  _dispFactory=new DispersionFactory;
  _autocorrFactory=new AutocorrFactory(_curves.rings());

  _autocorrFactory->setX(_curveList);
  _autocorrFactory->linkX(_curveList);
  _autocorrFactory->setModes(_curveList);

  _dispFactory->setX(*_autocorrFactory);
  _dispFactory->validate(nullptr, _autocorrFactory);

  _dispFactory->setAngularFrequency();
  _autocorrFactory->setAngularFrequency();
  _autocorrFactory->init();
}

void Spac3CForward::setFrequency(int i)
{
  TRACE;
  _iOmega=i;
  _curveIndexes.clear();
  for(QList<ModalCurve>::iterator it=_curveList.begin(); it!= _curveList.end(); it++) {
    _curveIndexes.append(it->indexOf(_iOmega));
  }
}

/*
  A model is a vector with three values: rayleigh slowness, love slowness and alpha
*/
void Spac3CForward::setModel(const double * model)
{
  _dispFactory->phaseRayleigh()->mode(0)[_iOmega].setValue(model[0]);
  _dispFactory->phaseLove()->mode(0)[_iOmega].setValue(model[1]);
  _autocorrFactory->calculateVertical(_dispFactory);
  _autocorrFactory->calculateHorizontal(_iOmega, model[2], _dispFactory);
}

double Spac3CForward::misfit(double * model, bool& ok)
{
  TRACE;
  setModel(model);
  double rms_val=0.0;
  int nValues=0;
  int nData=0;
  for(int i=_curveList.count()-1; i>=0; i--) {
    int index=_curveIndexes.at(i);
    if(index>-1) {
      const ModalCurve& c=_curveList.at(i);
      const FactoryPoint& p=c.at(index);
      //printf("%i %lg\n",i,_autocorrFactory.mode(c.modes().first())[ _iOmega ].value());
      rms_val+=p.misfit(nValues, nData, _autocorrFactory->mode(c.modes().first())[_iOmega], L2, 0.0);
    }
  }
  if(nValues>0)
    rms_val=sqrt(rms_val/nValues)*(1+nData-nValues);
  else if(nData==0) rms_val=0.0; // Case where data curve contains only invalid values
  else {
    rms_val=-1;
    ok=false;
  }
  return rms_val;
}

double Spac3CForward::vertical(int ringIndex) const
{
  return _autocorrFactory->vertical(ringIndex).mode(0)[_iOmega].value();
}

double Spac3CForward::radial(int ringIndex) const
{
  return _autocorrFactory->radial(ringIndex).mode(0)[_iOmega].value();
}

double Spac3CForward::transverse(int ringIndex) const
{
  return _autocorrFactory->transverse(ringIndex).mode(0)[_iOmega].value();
}
