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

#include "CompatAutocorrData.h"

namespace QGpCompatibility {

  CompatAutocorrData::CompatAutocorrData():
    CompatAutocorrCurves()
{
  TRACE;
  _measurements=0;
  _stddev=0;
  _weight=0;
}


CompatAutocorrData::CompatAutocorrData(int modesCount, int radiusCount, int omegasCount) : 
    CompatAutocorrCurves(modesCount, radiusCount, omegasCount)
{
  TRACE;
  _measurements=0;
  _stddev=0;
  _weight=0;
  allocatesData();
}

CompatAutocorrData::CompatAutocorrData(const CompatAutocorrData * o, int modesCount):
    CompatAutocorrCurves(o,modesCount)
{
  TRACE;
  _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 ** measurementsMode=_measurements[iMode];
    double ** stddevMode=_stddev[iMode];
    double ** weightMode=_weight[iMode];
    double ** o_measurementsMode=o->_measurements[iMode];
    double ** o_stddevMode=o->_stddev[iMode];
    double ** o_weightMode=o->_weight[iMode];
    for(int iRadius=0;iRadius<_radiusCount;iRadius++) {
      double * measurements=measurementsMode[iRadius];
      double * stddev=stddevMode[iRadius];
      double * weight=weightMode[iRadius];
      double * o_measurements=o_measurementsMode[iRadius];
      double * o_stddev=o_stddevMode[iRadius];
      double * o_weight=o_weightMode[iRadius];
      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 ** measurementsMode=_measurements[iMode];
    double ** stddevMode=_stddev[iMode];
    double ** weightMode=_weight[iMode];
    for(int iRadius=0;iRadius<_radiusCount;iRadius++) {
      double * measurements=measurementsMode[iRadius];
      double * stddev=stddevMode[iRadius];
      double * weight=weightMode[iRadius];
      for(int i=0;i<_omegasCount;i++) {
        measurements[i]=CompatAUTOCORR_INVALID_VALUE;
        stddev[i]=0;
        weight[i]=1;
      }
    }
  }
  _log=o->_log;
}

CompatAutocorrData::CompatAutocorrData(const CompatAutocorrCurves * o) :
    CompatAutocorrCurves(o)
{
  TRACE;
  _measurements=0;
  _stddev=0;
  _weight=0;
  allocatesData();
  // Copy autocorr from autocorr into measurements
  int iMode;
  for(iMode=0; iMode<_modesCount;iMode++) {
    double ** measurementsMode=_measurements[iMode];
    double ** stddevMode=_stddev[iMode];
    double ** weightMode=_weight[iMode];
    double ** o_valuesMode=o->_values[iMode];
    for(int iRadius=0;iRadius<_radiusCount;iRadius++) {
      double * measurements=measurementsMode[iRadius];
      double * stddev=stddevMode[iRadius];
      double * weight=weightMode[iRadius];
      double * o_values=o_valuesMode[iRadius];
      for(int i=0;i<_omegasCount;i++) {
        measurements[i]=o_values[i];
        stddev[i]=0;
        weight[i]=1;
      }
    }
  }
  // Copy the radius min and max
  for(int iRadius=0;iRadius<_radiusCount;iRadius++) {
    _radiusMin[iRadius]=o->radiusMin(iRadius);
    _radiusMax[iRadius]=o->radiusMax(iRadius);
  }  
}


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

void CompatAutocorrData::deleteData()
{
  TRACE;
  if(_measurements) {
    for(int iMode=0; iMode<_modesCount;iMode++) {
      for(int iRadius=0; iRadius<_radiusCount;iRadius++) 
        delete [] _measurements[iMode][iRadius];
      delete [] _measurements[iMode];
    }
    delete [] _measurements;
    _measurements=0;
  }
  if(_stddev)  {
    for(int iMode=0; iMode<_modesCount;iMode++) {
      for(int iRadius=0; iRadius<_radiusCount;iRadius++)
        delete [] _stddev[iMode][iRadius];
      delete [] _stddev[iMode];
    }
    delete [] _stddev; 
    _stddev=0;
  }
  if(_weight)  {
    for(int iMode=0; iMode<_modesCount;iMode++) {
      for(int iRadius=0; iRadius<_radiusCount;iRadius++)
        delete [] _weight[iMode][iRadius];
      delete [] _weight[iMode];
    }
    delete [] _weight; 
    _weight=0;
  }
}

void CompatAutocorrData::allocatesData()
{
  TRACE;
  deleteData();
  _measurements=new double**[_modesCount];
  _stddev=new double**[_modesCount];
  _weight=new double**[_modesCount];
  for(int iMode=0; iMode<_modesCount;iMode++) {
    double ** measurementsMode=new double *[_radiusCount];
    _measurements[iMode]=measurementsMode;
    double ** stddevMode=new double *[_radiusCount];
    _stddev[iMode]=stddevMode;
    double ** weightMode=new double *[_radiusCount];
    _weight[iMode]=weightMode;
    for(int iRadius=0; iRadius<_radiusCount;iRadius++) {
      measurementsMode[iRadius]=new double[_omegasCount];
      stddevMode[iRadius]=new double[_omegasCount];
      weightMode[iRadius]=new double[_omegasCount];
    }
  }
}

void CompatAutocorrData::valuesToData(const CompatAutocorrCurves * o)
{
  TRACE;
  for(int iMode=0;iMode<_modesCount;iMode++) {
    double ** measurementsMode=_measurements[iMode];
    double ** o_valuesMode=o->_values[iMode];
    double ** stddevMode=_stddev[iMode];
    double ** weightMode=_weight[iMode];
    for(int iRadius=0;iRadius<_radiusCount;iRadius++) {
      double * measurements=measurementsMode[iRadius];
      double * o_values=o_valuesMode[iRadius];
      double * stddev=stddevMode[iRadius];
      double * weight=weightMode[iRadius];
      for(int i=0;i<_omegasCount;i++) {
        measurements[i]=o_values[i];
        stddev[i]=0;
        weight[i]=1;
      }
    }
  }
}

bool CompatAutocorrData::isSameData(const CompatAutocorrData * o) const
{
  TRACE;
  if(_modesCount!=o->_modesCount) return false;
  if(_radiusCount!=o->_radiusCount) return false;
  if(_omegasCount!=o->_omegasCount) return false;
  for(int iMode=0;iMode<_modesCount;iMode++) {
    double ** measurementsMode1=_measurements[iMode];
    double ** stddevMode1=_stddev[iMode];
    double ** weightMode1=_weight[iMode];
    double ** measurementsMode2=o->_measurements[iMode];
    double ** stddevMode2=o->_stddev[iMode];
    double ** weightMode2=o->_weight[iMode];
    for(int iRadius=0;iRadius<_radiusCount;iRadius++) {
      double * measurements1=measurementsMode1[iRadius];
      double * stddev1=stddevMode1[iRadius];
      double * weight1=weightMode1[iRadius];
      double * measurements2=measurementsMode2[iRadius];
      double * stddev2=stddevMode2[iRadius];
      double * weight2=weightMode2[iRadius];
      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 CompatAutocorrData::dataToReport(QDataStream& s)
{
  TRACE;
  radiusToReport(s);
  int iMode;
  for(iMode=0;iMode<_modesCount;iMode++) {
    double ** measurementsMode=_measurements[iMode];
    double ** stddevMode=_stddev[iMode];
    for(int iRadius=0; iRadius<_radiusCount;iRadius++) {
      double * measurements=measurementsMode[iRadius];
      double * stddev=stddevMode[iRadius];
      for(int i=0;i<_omegasCount;i++) {
        s << measurements[i];
        s << stddev[i];
      }
    }
  }
  s << _log;
  // Write it separately to allow version compatibility (Version 3)
  for(iMode=0;iMode<_modesCount;iMode++) {
    double ** weightMode=_weight[iMode];
    for(int iRadius=0; iRadius<_radiusCount;iRadius++) {
      double * weight=weightMode[iRadius];
      for(int i=0;i<_omegasCount;i++) s << weight[i];
    }
  }
}

void CompatAutocorrData::reportToData(QDataStream& s)
{
  TRACE;
  if(!_omegasCount || !_modesCount || !_radiusCount) return;
  reportToRadius(s);
  for(int iMode=0;iMode<_modesCount;iMode++) {
    double ** measurementsMode=_measurements[iMode];
    double ** stddevMode=_stddev[iMode];
    for(int iRadius=0; iRadius<_radiusCount;iRadius++) {
      double * measurements=measurementsMode[iRadius];
      double * stddev=stddevMode[iRadius];
      for(int i=0;i<_omegasCount;i++) {
        s >> measurements[i];
        s >> stddev[i];
      }
    }
  }
  s >> _log;
}

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

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

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

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


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


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

} // namespace QGpCompatibility
