/***************************************************************************
**
**  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-05-19
**  Copyright: 2003-2019
**    Marc Wathelet
**    Marc Wathelet (ULg, Liège, Belgium)
**    Marc Wathelet (LGIT, Grenoble, France)
**
***************************************************************************/

#include <QGpCoreWave.h>
#include "CompatMultiModalData.h"
#include "CompatMultiModalCurves.h"

namespace QGpCompatibility {

CompatMultiModalData::CompatMultiModalData()
{
  TRACE;
  _modesCount=0;
  _omegasCount=0;
  _measurements=0;
  _stddev=0;
  _weight=0;
}

CompatMultiModalData::CompatMultiModalData(int modesCount,int omegasCount)
{
  TRACE;
  _measurements=0;
  _stddev=0;
  _weight=0;
  _modesCount=modesCount;
  _omegasCount=omegasCount;
  allocatesData();
}

CompatMultiModalData::CompatMultiModalData(const CompatMultiModalData* o,int modesCount, 
                                     double invalidValue)
{
  TRACE;
  _modesCount=modesCount;
  _omegasCount=o->_omegasCount;
  _measurements=0;
  _stddev=0;
  _weight=0;
  allocatesData();
  int minModeCount=o->_modesCount;
  if(minModeCount>_modesCount) minModeCount=_modesCount;
  int iMode;
  for(iMode=0;iMode<minModeCount;iMode++) {
    double * measurements=_measurements[iMode];
    double * stddev=_stddev[iMode];
    double * weight=_weight[iMode];
    double * o_measurements=o->_measurements[iMode];
    double * o_stddev=o->_stddev[iMode];
    double * o_weight=o->_weight[iMode];
    for(int i=0;i<_omegasCount;i++) {
      measurements[i]=o_measurements[i];
      stddev[i]=o_stddev[i];
      weight[i]=o_weight[i];
    }
  }
  for(iMode=minModeCount;iMode<_modesCount;iMode++) {
    double * measurements=_measurements[iMode];
    double * stddev=_stddev[iMode];
    double * weight=_weight[iMode];
    for(int i=0;i<_omegasCount;i++) {
      measurements[i]=invalidValue;
      stddev[i]=0;
      weight[i]=1;
    } 
  }
  _log=o->_log;
}

CompatMultiModalData::CompatMultiModalData(const CompatMultiModalCurves* o)
{
  TRACE;
  _measurements=0;
  _stddev=0;
  _weight=0;
  _modesCount=o->modesCount();
  _omegasCount=o->omegasCount();
  allocatesData();
  for(int iMode=0;iMode<_modesCount;iMode++) {
    double * measurements=_measurements[iMode];
    double * stddev=_stddev[iMode];
    double * weight=_weight[iMode];
    double * o_values=o->_values[iMode];
    for(int i=0;i<_omegasCount;i++) {
      measurements[i]=o_values[i];
      stddev[i]=0;
      weight[i]=1;
    }
  }
}

CompatMultiModalData::~CompatMultiModalData()
{
  TRACE;
  deleteData();
}

void CompatMultiModalData::deleteData()
{
  TRACE;
  if(_measurements) {
    for(int mode=0; mode<_modesCount;mode++)
      delete [] _measurements[mode];
    delete [] _measurements;
    _measurements=0;
  }
  if(_stddev) {
    for(int mode=0; mode<_modesCount;mode++)
      delete [] _stddev[mode];
    delete [] _stddev; 
    _stddev=0;
  }
  if(_weight) {
    for(int mode=0; mode<_modesCount;mode++)
      delete [] _weight[mode];
    delete [] _weight;
    _weight=0;
  }
}

void CompatMultiModalData::allocatesData()
{
  TRACE;
  deleteData();
  _measurements=new double*[_modesCount];
  int mode;
  for(mode=0; mode<_modesCount;mode++)
    _measurements[mode]=new double[_omegasCount];
  _stddev=new double*[_modesCount];
  for(mode=0; mode<_modesCount;mode++)
    _stddev[mode]=new double[_omegasCount];
  _weight=new double*[_modesCount];
  for(mode=0; mode<_modesCount;mode++)
    _weight[mode]=new double[_omegasCount];
}

bool CompatMultiModalData::checkStdDev(double ratio)
{
  TRACE;
  for(int mode=0;mode<_modesCount;mode++) {
    double * measurements=_measurements[mode];
    double * stddev=_stddev[mode];
    for(int i=0;i<_omegasCount;i++) {
      if(stddev[i]!=0 && measurements[i]<stddev[i]*ratio) return false;
    }
  }
  return true;
}

void CompatMultiModalData::valuesToData(CompatMultiModalCurves * o)
{
  TRACE;
  for(int mode=0;mode<_modesCount;mode++) {
    double * measurements=_measurements[mode];
    double * values=o->_values[mode];
    double * stddev=_stddev[mode];
    double * weight=_weight[mode];
    for(int i=0;i<_omegasCount;i++) {
      measurements[i]=values[i];
      stddev[i]=0;
      weight[i]=1;
    }
  }
}

bool CompatMultiModalData::isSameData(const CompatMultiModalData * o) const
{
  TRACE;
  if(_modesCount!=o->_modesCount) return false;
  if(_omegasCount!=o->_omegasCount) return false;
  for(int mode=0;mode<_modesCount;mode++) {
    double * measurements1=_measurements[mode];
    double * measurements2=o->_measurements[mode];
    double * stddev1=_stddev[mode];
    double * stddev2=o->_stddev[mode];
    double * weight1=_weight[mode];
    double * weight2=o->_weight[mode];
    for(int i=0;i<_omegasCount;i++) {
      if(measurements1[i]!=measurements2[i]) return false;
      if(stddev1[i]!=stddev2[i]) return false;
      if(weight1[i]!=weight2[i]) return false;
    }
  }
  return true;
}

void CompatMultiModalData::dataToReport(QDataStream& s)
{
  TRACE;
  if(!_omegasCount || !_modesCount) return;
  int mode;
  for(mode=0; mode<_modesCount;mode++) {
    double * measurements=_measurements[mode];
    double * stddev=_stddev[mode];
    for(int i=0;i<_omegasCount;i++) {
      s << measurements[i];
      s << stddev[i];
    }
  }
  s << _log;
  // Write it separately to allow version compatibility
  for(mode=0; mode<_modesCount;mode++) {
    double * weight=_weight[mode];
    for(int i=0;i<_omegasCount;i++) s << weight[i];
  }
}

void CompatMultiModalData::reportToData(QDataStream& s)
{
  TRACE;
  if(!_omegasCount || !_modesCount) return;
  for(int mode=0; mode<_modesCount;mode++) {
    double * measurements=_measurements[mode];
    double * stddev=_stddev[mode];
    for(int i=0;i<_omegasCount;i++) {
      s >> measurements[i];
      s >> stddev[i];
      //printf("%i %lg+-%lg\n",i,measurements[i], stddev[i]);
    }
  }
  s >> _log;
}

void CompatMultiModalData::reportToDataWeight(QDataStream& s, bool loadIt)
{
  TRACE;
  if(!_omegasCount || !_modesCount) return;
  if(loadIt) {
    for(int mode=0; mode<_modesCount;mode++) {
      double * weight=_weight[mode];
      for(int i=0;i<_omegasCount;i++) s >> weight[i];
    }
  }
  else {
    for(int mode=0; mode<_modesCount;mode++) {
      double * weight=_weight[mode];
      for(int i=0;i<_omegasCount;i++) weight[i]=1;
    }
  }
}

QVector<double> * CompatMultiModalData::measurements(int iMode)
{
  ASSERT(iMode<_modesCount);
  QVector<double> * list=new QVector<double>;
  double * values=_measurements[iMode];
  for(int i=0;i<_omegasCount;i++) list->push_back(values[i]);
  return list;
}

QVector<double> * CompatMultiModalData::stddev(int iMode)
{
  ASSERT(iMode<_modesCount);
  QVector<double> * list=new QVector<double>;
  double * values=_stddev[iMode];
  for(int i=0;i<_omegasCount;i++) list->push_back(values[i]);
  return list;
}

QVector<double> * CompatMultiModalData::weight(int iMode)
{
  ASSERT(iMode<_modesCount);
  QVector<double> * list=new QVector<double>;
  double * values=_weight[iMode];
  for(int i=0;i<_omegasCount;i++) list->push_back(values[i]);
  return list;
}

ModalCurve CompatMultiModalData::curve(int iMode)
{
  TRACE;
  ASSERT(iMode<_modesCount);
  ModalCurve c(_omegasCount);
  for(int i=0;i<_omegasCount;i++) {
    FactoryPoint& p=c[i];
    p.setMean(_measurements[iMode][i] );
    p.setStddev(_stddev[iMode][i] );
    p.setWeight(_weight[iMode][i] );
  }
  return c;
}

/** 
  Select curves that lies inside +/-devRatio*stddev
  A tolerance of 5 percent is accepted (5% percent of the samples may exceed
  the implied limits
  */
bool CompatMultiModalData::insideDev(double devRatio, int iMode,
                                  Point2D * pDisp, int countPoints)
{
  TRACE;
  ASSERT(countPoints==_omegasCount);
  double * measurements=_measurements[iMode];
  double * stddev=_stddev[iMode];
  double outCount=0, dCount=1.0/_omegasCount;
  for(int i=0;i<_omegasCount;i++) {
    if(fabs(pDisp[i].y()-measurements[i])>devRatio*stddev[i]) {
      outCount+=dCount;
      if(outCount>0.05) return false;
    }
  }
  return true;
}

} // namespace QGpCompatibility
