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

#include <math.h>

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

namespace QGpCompatibility {

CompatMultiModalCurves::CompatMultiModalCurves() :
    CompatMultiModalFrequency()
{
  TRACE;
  _values=0;
  _rayleighModesCount=0;
}

CompatMultiModalCurves::CompatMultiModalCurves(int modesCount, int omegasCount) :
    CompatMultiModalFrequency(modesCount, omegasCount)
{
  TRACE;
  _values=0;
  _rayleighModesCount=modesCount;
  allocatesValues();
}

CompatMultiModalCurves::CompatMultiModalCurves(const CompatMultiModalFrequency * o) :
    CompatMultiModalFrequency(o) 
{
  TRACE;
  _values=0;
  _rayleighModesCount=_modesCount;
  allocatesValues();
}

CompatMultiModalCurves::CompatMultiModalCurves(const CompatMultiModalFrequency * o,
                                         int modesCount) :
CompatMultiModalFrequency(o,modesCount) 
{
  TRACE;
  _values=0;
  _rayleighModesCount=_modesCount;
  allocatesValues();
}

CompatMultiModalCurves::CompatMultiModalCurves(const CompatMultiModalCurves * o) :
    CompatMultiModalFrequency(o) 
{
  TRACE;
  _values=0;
  _rayleighModesCount=_modesCount;
  allocatesValues();
  int iMode;
  for(iMode=0;iMode<_modesCount;iMode++) {
    double * values=_values[iMode];
    double * o_values=o->_values[iMode];
    for(int i=0;i<_omegasCount;i++) values[i]=o_values[i];
  }
}

CompatMultiModalCurves::CompatMultiModalCurves(const CompatMultiModalCurves * o,
                                         int modesCount, double invalidValue) :
CompatMultiModalFrequency(o, modesCount)
{
  TRACE;
  _values=0;
  _rayleighModesCount=modesCount;
  allocatesValues();
  int minModeCount=o->_modesCount;
  if(minModeCount>_modesCount) minModeCount=_modesCount;
  int iMode;
  for(iMode=0;iMode<minModeCount;iMode++) {
    double * values=_values[iMode];
    double * o_values=o->_values[iMode];
    for(int i=0;i<_omegasCount;i++) values[i]=o_values[i];
  }
  for(iMode=minModeCount;iMode<_modesCount;iMode++) {
    double * values=_values[iMode];
    for(int i=0;i<_omegasCount;i++) values[i]=invalidValue;
  }
}

CompatMultiModalCurves::~CompatMultiModalCurves()
{
  TRACE;
  deleteValues();
}

void CompatMultiModalCurves::clearRefines()
{
  TRACE;
  _refines.clear();
}

void CompatMultiModalCurves::deleteValues()
{
  TRACE;
  if(_values) {
    for(int mode=0; mode<_modesCount;mode++)
      delete [] _values[mode];
    delete [] _values;
    _values=0;
  } 
  clearRefines();
}

void CompatMultiModalCurves::allocatesValues()
{
  TRACE;
  deleteValues();
  _values=new double*[_modesCount];
  for(int mode=0; mode<_modesCount;mode++)
    _values[mode]=new double[_omegasCount];
}

void CompatMultiModalCurves::toStream(FILE *f)
{
  TRACE;
  double omega2freq=1.0/_freq2omega;
  // Export first the fundamental together with the refines
  double * values=_values[0];
  int i=0;
  QList<Point2D>::iterator it=_refines.begin();
  double nextFixedX=_omegas[0],nextRefineX;
  if(it==_refines.end()) {
    nextRefineX=std::numeric_limits<double>::infinity();
  } else {
    nextRefineX=(*it).x();
  }
  while(i<_omegasCount || it!=_refines.end()) {
    while(nextFixedX<nextRefineX) {
      fprintf(f,"%lf %lf\n",nextFixedX*omega2freq,values[i]);
      i++;
      if(i<_omegasCount) nextFixedX=_omegas[i];
      else {
        nextFixedX=std::numeric_limits<double>::infinity();
        break;
      }
    }
    while(nextRefineX<nextFixedX) {
      fprintf(f,"%lf %lf\n",nextRefineX*omega2freq,(*it).y());
      ++it;
      if(it==_refines.end()) {
        nextRefineX=std::numeric_limits<double>::infinity();
         break;
     } else {
        nextRefineX=(*it).x();
      }
    }
  }  
  fprintf(f,">\n");
  for(int mode=1; mode<_modesCount;mode++) {
    double * values=_values[mode];
    for(int i=0;i<_omegasCount;i++)
      fprintf(f,"%lf %lf\n",_omegas[i]*omega2freq,values[i]);
    fprintf(f,">\n");
  }   
}

void CompatMultiModalCurves::valuesToReport(QDataStream& s) const
{
  TRACE;
  //cout << "Values to Report" << _modesCount << " " << _omegasCount << endl;
  for(int mode=0; mode<_modesCount;mode++) {
    //s.writeRawData((char *)_values[mode],_omegasCount*sizeof(double));
    double * values=_values[mode];
    for(int i=0;i<_omegasCount;i++) {
      s << values[i];
      //cout << twopi/_omegas[i] << " " << values[i] << endl;
    }
  }
}

void CompatMultiModalCurves::reportToOmega(QDataStream& s)
{
  TRACE;
  deleteValues();
  CompatMultiModalFrequency::reportToOmega(s);
  allocatesValues();
}

void CompatMultiModalCurves::reportToValues(QDataStream& s)
{
  TRACE;
  ASSERT(_values);
  //cout << "Report to Values" << _modesCount << " " << _omegasCount << endl;
  for(int mode=0; mode<_modesCount;mode++) {
    //s.readRawData((char *)_values[mode],_omegasCount*sizeof(double));
    double *values=_values[mode];
    for(long i=0;i<_omegasCount;i++) {
      s >> values[i];
      //cout << twopi/_omegas[i] << " " << values[i] << endl;
    }
  }
}

void CompatMultiModalCurves::setValues(CompatMultiModalCurves * o)
{
  TRACE;
  int minModesCount=_modesCount;
  if(o->_modesCount<minModesCount) minModesCount=_modesCount;
  int minOmegasCount=_omegasCount;
  if(o->_omegasCount<minOmegasCount) minOmegasCount=_omegasCount;
  for(int iMode=0; iMode<minModesCount;iMode++) {
    double * values=_values[iMode];
    double * o_values=o->_values[iMode];
    for(int i=0;i<minOmegasCount;i++) values[i]=o_values[i];
  }
}

int CompatMultiModalCurves::toPointVector(int imode,Point2D * pointList,
                                       double invalid_value)
{
  TRACE;
  ASSERT(imode<_modesCount);
  //    cout << "Refines count=" << _refines.count() << endl;
  //    if(_refines.count()>0) {
  // QList<Point2D *>::iterator it(_refines);
  // Point2D * p;
  // for(;(p=it.current());++it) {
  //     cout << p->x << " " << p->y << endl;
  // }
  //    }
  double omega2freq=1.0/_freq2omega;
  double * values=_values[imode];
  int count=_omegasCount;
  if(imode>0) {
    for(int i=0;i<_omegasCount;i++) {
      double val=values[i];
      if(val!=invalid_value)
        *(pointList++)=Point2D(_omegas[i]*omega2freq,val);
      else count--;
    }
  }
  else {
    int i=0;
    double nextFixedX=_omegas[0], nextRefineX;
    QList<Point2D>::iterator it=_refines.begin();
    if(it==_refines.end()) {
      nextRefineX=std::numeric_limits<double>::infinity();
    } else {
      nextRefineX=(*it).x();
    }
    double val;
    while(i<_omegasCount || it!=_refines.end()) {
      while(nextFixedX<nextRefineX) {
        val=values[i];
        if(val!=invalid_value)
          *(pointList++)=Point2D(nextFixedX*omega2freq,val);
        else count--;
        i++;
        if(i<_omegasCount) nextFixedX=_omegas[i];
        else {
          nextFixedX=std::numeric_limits<double>::infinity();
          break;
        }
      }
      while(nextRefineX<nextFixedX) {
        val=(*it).y();
        if(val!=invalid_value) {
          *(pointList++)=Point2D(nextRefineX*omega2freq,val);
          count++;
        }
        ++it;
        if(it==_refines.end()) {
          nextRefineX=std::numeric_limits<double>::infinity();
          break;
        } else {
          nextRefineX=(*it).x();
        }
      }
    }
  }
  return count;
}

void CompatMultiModalCurves::refinesToReport(QDataStream& s) const
{
  TRACE;
  //cout << _refines.count() << endl;
  s << _refines.count();
  QList<Point2D>::const_iterator it;
  for(it=_refines.begin();it!=_refines.end();++it) {
    //cout << p->x << " " << p->y << endl;
    const Point2D& p=*it;
    s << p.x() << p.y();
  }
}

void CompatMultiModalCurves::reportToRefines(QDataStream& s)
{
  TRACE;
  clearRefines();
  int n;
  double x,y;
  s >> n;
  //cout << n << endl;
  for(int i=0;i<n;i++) {
    s >> x >> y;
    //cout << x << " " << y << endl;
    _refines.append(Point2D(x,y));
  }
}

void CompatMultiModalCurves::clear(double invalidValue)
{
  TRACE;
  for(int iMode=0;iMode<_modesCount;iMode++) {
    double * values=_values[iMode];
    for(int i=0;i<_omegasCount;i++) values[i]=invalidValue;
  }
}

#if 0
void CompatMultiModalCurves::xml_writeProperties(XML_WRITEPROPERTIES_ARGS) const
{
  TRACE;
  writeProperty(s, "action","clear");
  CompatMultiModalFrequency::xml_writeProperties(s,indent);
  writeProperty(s, "rayleighModesCount",(int)_rayleighModesCount);
  writeProperty(s, "action","allocate");
}

bool CompatMultiModalCurves::xml_setProperty(CompatStringSection& propertyName,
                                          CompatStringSection& content)
{
  TRACE;
  if(CompatMultiModalFrequency::xml_setProperty(propertyName, content));
  else if(propertyName=="action") {
    if(content=="clear") deleteValues();
    else if(content=="allocate") allocatesValues();
  }
  else if(propertyName=="rayleighModesCount")
    _rayleighModesCount=content.toInt();
  else return false;
  return true;  
}
#endif

} // namespace QGpCompatibility
