/***************************************************************************
**
**  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: 2007-05-22
**  Copyright: 2007-2019
**    Marc Wathelet
**    Marc Wathelet (LGIT, Grenoble, France)
**
***************************************************************************/

#include <QGpCoreTools.h>
#include "AutocorrCurves.h"
#include "AutocorrDispersion.h"

namespace QGpCoreWave {

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

  Full description of class still missing
*/

const QString AutocorrCurves::xmlAutocorrCurvesTag="AutocorrCurves";

/*!
  Add one ring (for convenience)
*/
void AutocorrCurves::addRing(const AutocorrRing& r)
{
  TRACE;
  _rings.append(r);
}

/*!
  Remove ring with index \a index
*/
void AutocorrCurves::removeRing(int index)
{
  TRACE;
  _rings.remove(index);
}

QList<ModalCurve> AutocorrCurves::ringCurves(int index) const
{
  QList<ModalCurve> l;
  for(QList<ModalCurve>::ConstIterator it=_curves.begin(); it!=_curves.end(); it++ ) {
    const ModalCurve& c=*it;
    if(c.modes().first().ringIndex()==index) {
      l.append(c);
    }
  }
  return l;
}

/*!
  Add a new curve. Make sure ring index fits with the number of rings.
  At least one ring must be available.
*/
void AutocorrCurves::addCurve(const ModalCurve& c)
{
  TRACE;
  ASSERT(_rings.count()>0);
  _curves.append(c);
  ModalCurve& nc=_curves.last();
  nc.sort(); // Make sure it is sorted.
  QList<Mode>& modes=nc.modes();
  int iRing;
  if(modes.isEmpty()) {
    nc.addMode(Mode( Mode::Vertical, 0, 0) );
    iRing=0;
  } else {
    iRing=modes.first().ringIndex();
    if(iRing < 0 || iRing >= _rings.count()) {
      App::log(tr("Bad ring index for curve %1: %2 while max is %3\n").arg(c.name()).arg(iRing).arg(_rings.count()-1) );
    }
    for(QList<Mode>::Iterator it=modes.begin(); it!=modes.end(); it++ ) {
      it->setRingIndex(iRing);
    }
  }
}

/*!
  Adds all curves and rings from \a c into this object. Common rings are smartly merged
*/
void AutocorrCurves::add(const AutocorrCurves& curves)
{
  TRACE;
  for(QList<ModalCurve>::ConstIterator it=curves._curves.begin(); it!=curves._curves.end(); it++ ) {
    const ModalCurve& c=*it;
    int iCurvesRing=c.modes().first().ringIndex();
    if(iCurvesRing >= curves._rings.count()) {
      App::log(tr("Bad ring index for curve %1: %2 while max is %3\n").arg(c.name()).arg(iCurvesRing).arg(curves._rings.count()-1) );
    } else {
      const AutocorrRing& r=curves._rings.at(iCurvesRing);
      int iRing=indexOf(r);
      if(iRing==-1) {
        iRing=ringCount();
        addRing(r);
      }
      _curves.append(c);
      QList<Mode>& modes=_curves.last().modes();
      for(QList<Mode>::Iterator it=modes.begin(); it!=modes.end(); it++ ) {
        it->setRingIndex(iRing);
      }
    }
  }
}

void AutocorrCurves::sort()
{
  // No longer usefull, remove in 3.4
}

void AutocorrCurves::xml_writeChildren(XML_WRITECHILDREN_ARGS) const
{
  TRACE;
  int nRings=_rings.count();
  static const QString key("type");
  static const QString value("autocorr");
  XMLSaveAttributes att;
  att.add(key, value);
  for(int iRing=0;iRing<nRings;iRing++ ) {
    _rings.at(iRing).xml_save(s, context);
    QList<ModalCurve> curves=ringCurves(iRing);
    for(QList<ModalCurve>::Iterator it=curves.begin(); it!=curves.end(); it++ ) {
      it->xml_save(s, context, att);
    }
  }
}

XMLMember AutocorrCurves::xml_member(XML_MEMBER_ARGS)
{
  TRACE;
  Q_UNUSED(context)
  if(tag=="ModalCurve" ) {
    static const QString typeAtt("type");
    XMLRestoreAttributeIterator it=attributes.find(typeAtt);
    if(it!=attributes.end()) {
      if(it.value()=="autocorr") {
        _curves.append(ModalCurve());
        return XMLMember(&_curves.last());
      } else {
        App::log(tr("Wrong type of modal curve %1\n").arg(it.value().toString()) );
        return XMLMember(XMLMember::Unknown);
      }
    } else {
      App::log(tr("No type defined for modal curve\n") );
      return XMLMember(XMLMember::Unknown);
    }
  } else if(tag=="AutocorrRing") {
    addRing();
    return XMLMember(&_rings.last());
  } else return XMLMember(XMLMember::Unknown);
}

/*!
  Calculate the dispersion curve corresponding to curve \a index.
  The solutions are searched between \a kmin and \a kmax. The returned curve
  may contain various values for the same frequency.
*/
ModalCurve AutocorrCurves::dispersionCurve(int index, double kmin, double kmax, int maxSolutionCount) const
{
  const ModalCurve& autocorr=_curves.at(index);
  int nx=autocorr.count();
  ModalCurve dispersion;
  AutocorrDispersion eng;
  eng.setRing(_rings.at(autocorr.modes().first().ringIndex()) );
  for(int ix=0; ix<nx; ix++ ) {
    const FactoryPoint& p=autocorr.at(ix);
    if(p.isValid()) {
      QVector<double> solutions=eng.dispersion(p, kmin, kmax);
      for(int i=0; i<solutions.count() && i<maxSolutionCount; i++) {
        dispersion.append(FactoryPoint(p.x(), solutions.at(i)));
      }
    }
  }
  dispersion.addMode(Mode(Mode::Phase, Mode::Rayleigh, 0));
  return dispersion;
}

} // namespace QGpCoreWave
