/***************************************************************************
**
**  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: 2006-09-26
**  Copyright: 2006-2019
**    Marc Wathelet
**    Marc Wathelet (LGIT, Grenoble, France)
**
***************************************************************************/

#include "DispersionFactory.h"
#include "EllipticityFactory.h"
#include "AutocorrFactory.h"
#include "Dispersion.h"
#include "Mode.h"

namespace QGpCoreWave {

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

  Full description of class still missing
*/

DispersionFactory::DispersionFactory()
{
  TRACE;
  _phaseRayleigh=nullptr;
  _phaseLove=nullptr;
  _groupRayleigh=nullptr;
  _groupLove=nullptr;
}

DispersionFactory::~DispersionFactory()
{
  TRACE;
  delete _phaseRayleigh;
  delete _phaseLove;
  delete _groupRayleigh;
  delete _groupLove;
}

/*!
  Validate modes, make sure that the number of modes for group slowness is less of equal to
  phase slowness. Also check autocorr and ellipticity.
*/
void DispersionFactory::validate(EllipticityFactory * ell, AutocorrFactory * autocorr)
{
  TRACE;
  if(ell && ell->signedValues()) {
    upgrade<Dispersion>(_phaseRayleigh, ell->signedValues()->modeCount()-1);
  }
  if(autocorr) {
    if(autocorr->verticalModeCount()>0) {
      upgrade<Dispersion>(_phaseRayleigh, autocorr->verticalModeCount()-1);
    }
    if(autocorr->horizontalModeCount()>0) {
      upgrade<Dispersion>(_phaseRayleigh, autocorr->horizontalModeCount()-1);
      upgrade<Dispersion>(_phaseLove, autocorr->horizontalModeCount()-1);
    }
  }
  if(_groupRayleigh) {
    upgrade<Dispersion>(_phaseRayleigh, _groupRayleigh->modeCount()-1);
  }
  if(_groupLove) {
    upgrade<Dispersion>(_phaseLove, _groupLove->modeCount()-1);
  }
}

void DispersionFactory::setMode (const Mode& m)
{
  TRACE;
  switch (m.slowness()) {
  case Mode::Phase:
    switch (m.polarization()) {
    case Mode::Vertical:
    case Mode::Radial:
    case Mode::Rayleigh:
      upgrade<Dispersion>(_phaseRayleigh, m.index()); break;
    case Mode::Love:
    case Mode::Transverse:
      upgrade<Dispersion>(_phaseLove, m.index()); break;
    }
    break;
  case Mode::Group:
    switch (m.polarization()) {
    case Mode::Vertical:
    case Mode::Radial:
    case Mode::Rayleigh:
      upgrade<Dispersion>(_groupRayleigh, m.index()); break;
    case Mode::Love:
    case Mode::Transverse:
      upgrade<Dispersion>(_groupLove, m.index()); break;
    }
    break;
  }
}

const RealValue * DispersionFactory::mode(const Mode& m) const
{
  switch (m.slowness()) {
  case Mode::Phase:
    switch (m.polarization()) {
    case Mode::Vertical:
    case Mode::Radial:
    case Mode::Rayleigh:
      return _phaseRayleigh ? _phaseRayleigh->mode(m.index()): nullptr;
    case Mode::Love:
    case Mode::Transverse:
      return _phaseLove ? _phaseLove->mode(m.index()): nullptr;
    }
    break;
  case Mode::Group:
    switch (m.polarization()) {
    case Mode::Vertical:
    case Mode::Radial:
    case Mode::Rayleigh:
      return _groupRayleigh ? _groupRayleigh->mode(m.index()): nullptr;
    case Mode::Love:
    case Mode::Transverse:
      return _groupLove ? _groupLove->mode(m.index()): nullptr;
    }
    break;
  }
  return nullptr; // just to avoid warning
}

/*!
  Calculate theoretical curves
*/
bool DispersionFactory::calculate(const Seismic1DModel * m, EllipticityFactory * ellFactory)
{
  TRACE;
  if(_phaseRayleigh) {
    Rayleigh rayleigh(m);
    Ellipticity * ell;
    if(ellFactory) {
      ell=ellFactory->signedValues();
    } else {
      ell=nullptr;
    }
    if(!_phaseRayleigh->calculate(&rayleigh, ell)) {
      return false;
    }
    if(ell) { // Since 2011-01-07, signed ellipticity is computed
              // Since 2019-02-18, deg. instead of rad.
      ell->atanDeg();
      Ellipticity * ellAbs=ellFactory->absoluteValues();
      if(ellAbs) {
        *ellAbs=*ell;
        ellAbs->abs();
      }
    }
    if(_groupRayleigh) {
      *_groupRayleigh=*_phaseRayleigh;
      _groupRayleigh->setGroupSlowness();
    }
  }
  if(_phaseLove) {
    Love love(m);
    if(!_phaseLove->calculate(&love)) {
      return false;
    }
    if(_groupLove) {
      *_groupLove=*_phaseLove;
      _groupLove->setGroupSlowness();
    }
  }
  return true;
}

} // namespace QGpCoreWave
