/***************************************************************************
**
**  This file is part of QGpCompatibility.
**
**  QGpCompatibility 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.
**
**  QGpCompatibility 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: 2003-06-10
**  Copyright: 2003-2019
**    Marc Wathelet
**    Marc Wathelet (ULg, Liège, Belgium)
**    Marc Wathelet (LGIT, Grenoble, France)
**
***************************************************************************/

#include <math.h>

#include <QGpCoreTools.h>
#include "CompatFunctionList.h"

namespace QGpCompatibility {

/*!
  \class CompatFunctionList
  \brief A list of CompatFunction

  A list of function specifically dedicated to the computation of the average multi modal curves.
  
  To get one averaged curve per mode, you must call (in this order):
 
  \li int modesCount(bool multiMode, QString baseName, QString * curveNames,
                     uint startAt)
  \li void addXSamples(CompatFunction& xModel)
  \li void sameSamplingForCurves(CompatFunction& xModel,double invalidValue)
  \li void average(int iMode,int i, double invalidValue, double& average, double& averageStddev,
                   double invalidValue)
  \li int cleanupAverage()
*/

CompatFunctionList::CompatFunctionList()
{
  TRACE;
  _selected=0;
}

CompatFunctionList::CompatFunctionList (const CompatFunctionList& o) :
    QList<CompatFunction *>(o)
{
  TRACE;
  _selected=0;
}

/*!
  In usual situations, when the functions have been called without error, this
  destructor does nothing
*/
CompatFunctionList::~CompatFunctionList()
{
  TRACE;
  if(_selected) {
    qDeleteAll(*_selected);
    _selected->clear();
    delete _selected;
  }
}

void CompatFunctionList::clear()
{
  TRACE;
  qDeleteAll(*this);
  QList<CompatFunction *>::clear();
}

void CompatFunctionList::deleteVectors()
{
  TRACE;
  CompatFunction * it;
  foreach(it, *this) it->deleteVectors();
}

CompatFunctionList& CompatFunctionList::operator= (const CompatFunctionList& o)
{
  TRACE;
  QList<CompatFunction *>::operator=(o);
  _selected=0;
  return *this;
}

int CompatFunctionList::modesCount(bool multiMode, QString baseName,
                                const QStringList& curveNames, int startAt)
{
  TRACE;
  if(!_selected) {
    _selected=new QList< QList<CompatFunction *> *>;
  }
  for(int i=count()-1;i>=0;i--) {
    const QString& str=curveNames[i];
    if(str.contains(baseName)) {
      if(multiMode) {
        int num=str.section(" ",-1,-1).toUInt()+startAt;
        while(_selected->count()<num)
          _selected->append(new QList<CompatFunction *>);
        _selected->at(num-1)->append(at(i));
      } else {
        if(_selected->count()==startAt)
          _selected->append(new QList<CompatFunction *>);
        _selected->at(startAt)->append(at(i));
      }
    }
  }
  if(_selected->count()>startAt)
    return _selected->count()-startAt;
  else return 0;
}

void CompatFunctionList::addXSamples(CompatFunction& xModel, bool addInvalidY,
                                  double invalidValue)
{
  TRACE;
  ASSERT(_selected);
  QVector<double>& xList=*xModel.x();
  QVector<double>& yList=*xModel.y();
  foreach(QList<CompatFunction *> * modePtr, *_selected) {
    foreach(CompatFunction * fPtr, *modePtr) {
      QVector<double>& x=*fPtr->x();
      QVector<double>& y=*fPtr->y();
      for(int ix=x.count()-1;ix>=0;ix--) {
        if(addInvalidY || y[ix]!=invalidValue) {
          xList.append(x[ix]);
          yList.append(0);
        }
      }
    }
  }
}

void CompatFunctionList::sameSamplingForCurves(CompatFunction& xModel,bool interpole,
    double invalidValue)
{
  TRACE;
  ASSERT(_selected);
  foreach(QList<CompatFunction *> * modePtr, *_selected) {
    foreach(CompatFunction * fPtr, *modePtr) {
      fPtr->resample(xModel.x(),invalidValue,interpole);
    }
  }
}

void CompatFunctionList::inverseX()
{
  TRACE;
  ASSERT(_selected);
  foreach(QList<CompatFunction *> * modePtr, *_selected) {
    foreach(CompatFunction * fPtr, *modePtr) {
      fPtr->inverseX();
    }
  }
}

void CompatFunctionList::average(int iMode,int i, double& average,
                              double& averageStddev, int& hitsCount,
                              double invalidValue, bool logStat)
{
  TRACE;
  ASSERT(_selected);
  if(iMode>=(int)_selected->count()) {
    average=invalidValue;
    averageStddev=0;
    hitsCount=1;
    return;
  }
  average=0;
  averageStddev=0;
  hitsCount=0;
  double mui, sigma2i, w;
  foreach(CompatFunction * fPtr, *_selected->at(iMode)) {
    CompatFunction& curve=*fPtr;
    mui=curve.y()->at(i);
    if(mui!=invalidValue) {
      if(logStat) mui=log10(mui);
      if(curve.yErr())
        sigma2i=curve.yErr()->at(i);
      else
        sigma2i=0;
      if(logStat && sigma2i>0) sigma2i=log10(sigma2i);
      sigma2i=sigma2i*sigma2i;
      if(curve.weight()) {
        w=floor(curve.weight()->at(i)+0.5);
        average+=w*mui;
        if(w>1) {
          averageStddev+=(w-1)*sigma2i+w*mui*mui;
          hitsCount+=(int)w;
        } else {
          averageStddev+=sigma2i+mui*mui;
          hitsCount++;
        }
      } else {
        average+=mui;
        averageStddev+=mui*mui;
        hitsCount++;
      }
    }
  }
  if(hitsCount>0) {
    average/=hitsCount;
    averageStddev-=average*average*hitsCount;
    if(hitsCount>1)
      averageStddev/=hitsCount-1;
    else
      averageStddev/=hitsCount;

    if(averageStddev<1e-10) {
      averageStddev=0;
      hitsCount=1;
    } else averageStddev=sqrt(averageStddev);
    if(logStat) {
      average=pow(10.0,average);
      averageStddev=pow(10.0,averageStddev);
    }
  } else {
    average=invalidValue;
    if(logStat) averageStddev=1; else averageStddev=0;
    hitsCount=1;
  }
}

/*!
  Delete the _selected vectors and return the total number of curves
*/
int CompatFunctionList::cleanupAverage()
{
  TRACE;
  ASSERT(_selected);
  int curveCount=0;
  foreach(QList<CompatFunction *> * modePtr, *_selected)
    curveCount+=modePtr->count();
  qDeleteAll(*_selected);
  _selected->clear();
  delete _selected;
  _selected=0;
  return curveCount;
}


} // namespace QGpCompatibility
