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

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

namespace QGpCompatibility {

#define inv2pi 0.159154943092

const QString CompatModalRefinedCurves::xmlModalRefinedCurvesTag="ModalRefinedCurves";

void CompatModalRefinedCurves::operator=(CompatModalRefinedCurves& o)
{
  TRACE;
  CompatModalFrequency::operator=(o);
  _curves=o._curves;
}

void CompatModalRefinedCurves::addRayleighModes(int nNewModes)
{
  TRACE;
  _curves.insertModes(nRayleighModes(), nNewModes);
  setNRayleighModes(nRayleighModes()+nNewModes);
}

int CompatModalRefinedCurves::toPointVector(int imode,Point2D *& pointList)
{
  TRACE;
  int nm=nModes();
  int nf=nOmegas();
  int count=nf;
  ASSERT(imode<nm);
  // init fixed values
  double nextFixedX=omega(0);
  CompatVDataPointVector& point=mode(imode);
  // init refines values
  CompatRefineIterator it=_refines.begin();
  double nextRefineX;
  // Currently refines are for Rayleigh fundamental mode only
  if(imode==0 && it!=_refines.end()) {
    nextRefineX=(*it).omega();
  } else {
    nextRefineX=std::numeric_limits<double>::infinity();
    it=_refines.end();
  }
  // loop variables
  double val, invalidValue=_curves.invalidValue();
  int i=0;
  while(i<nf || it!=_refines.end()) {
    while(nextFixedX<nextRefineX) {
      val=point[i].value();
      if(val!=invalidValue)
        *(pointList++)=Point2D(inv2pi*nextFixedX,val);
      else count--;
      i++;
      if(i<nf) nextFixedX=omega(i);
      else {
        nextFixedX=std::numeric_limits<double>::infinity();
        break;
      }
    }
    while(nextRefineX<nextFixedX) {
      val=(*it).value(imode);
      if(val!=invalidValue) {
        *(pointList++)=Point2D(inv2pi*nextRefineX,val);
        count++;
      }
      ++it;
      if(it!=_refines.end())
        nextRefineX=(*it).omega();
      else {
        nextRefineX=std::numeric_limits<double>::infinity();
        break;
      }
    }
  }
  return count;
}

QVector<Point> * CompatModalRefinedCurves::toPointVector(int imode)
{
  int nm=nModes();
  int nf=nOmegas();
  ASSERT(imode<nm);
  QVector<Point> * points=new QVector<Point>;
  // init fixed values
  double nextFixedX=omega(0);
  CompatVDataPointVector& point=mode(imode);
  // init refines values
  CompatRefineIterator it=_refines.begin();
  double nextRefineX;
  // Currently refines are for Rayleigh fundamental mode only
  if(imode==0 && it!=_refines.end()) {
    nextRefineX=(*it).omega();
  } else {
    nextRefineX=std::numeric_limits<double>::infinity();
    it=_refines.end();
  }
  // loop variables
  double val, invalidValue=_curves.invalidValue();
  int i=0;
  while(i<nf || it!=_refines.end()) {
    while(nextFixedX<nextRefineX) {
      val=point[i].value();
      if(val!=invalidValue) points->append(Point(inv2pi*nextFixedX,val,0));
      i++;
      if(i<nf) nextFixedX=omega(i);
      else {
        nextFixedX=std::numeric_limits<double>::infinity();
        break;
      }
    }
    while(nextRefineX<nextFixedX) {
      val=(*it).value(imode);
      if(val!=invalidValue) points->append(Point(inv2pi*nextRefineX,val,0));
      ++it;
      if(it!=_refines.end())
        nextRefineX=(*it).omega();
      else {
        nextRefineX=std::numeric_limits<double>::infinity();
        break;
      }
    }
  }
  return points;
}

void CompatModalRefinedCurves::toStream(FILE *f)
{
  TRACE;
  int nm=nModes();
  int nf=nOmegas();
  for(int imode=0; imode<nm;imode++) {
    // init fixed values
    double nextFixedX=omega(0);
    CompatVDataPointVector& point=mode(imode);
    // init refines values
    CompatRefineIterator it=_refines.begin();
    double nextRefineX;
    // Currently refines are for Rayleigh fundamental mode only
    if(imode==0 && it!=_refines.end()) {
      nextRefineX=(*it).omega();
    } else {
      nextRefineX=std::numeric_limits<double>::infinity();
      it=_refines.end();
    }
    // loop variables
    double val, invalidValue=_curves.invalidValue();
    int i=0;
    while(i<nf || it!=_refines.end()) {
      while(nextFixedX<nextRefineX) {
        val=point[i].value();
        if(val!=invalidValue) fprintf(f,"%lg %lg\n",inv2pi*nextFixedX,val);
        i++;
        if(i<nf) nextFixedX=omega(i);
        else {
          nextFixedX=std::numeric_limits<double>::infinity();
          break;
        }
      }
      while(nextRefineX<nextFixedX) {
        val=(*it).value(imode);
        if(val!=invalidValue) fprintf(f,"%lg %lg\n",inv2pi*nextRefineX,val);
        ++it;
        if(it!=_refines.end())
          nextRefineX=(*it).omega();
        else {
          nextRefineX=std::numeric_limits<double>::infinity();
          break;
        }
      }
    }
  }
}

/*!
  Save all to report (including frequency), mixing refines and original samples, removing invalid values

  Begin with a table of the number of samples per mode

  4                 nm Modes
  4                 n Rayleigh modes
  nm*4              table of number of samples per mode
  sum(nfr)*2*8      values: sum(nfr)*2*8
*/
void CompatModalRefinedCurves::writeReport(QDataStream& s) const
{
  TRACE;
  int nm=nModes();
  int nf=nOmegas();
  s << nm;
  s << nRayleighModes();
  int nfr=0;
  for(int imode=0; imode<nm;imode++) {
    // room for sample per mode table
    qint64 nSamplesPos=s.device()->pos();
    s << nfr;
    // init fixed values
    double nextFixedX=omega(0);
    const CompatVDataPointVector& point=mode(imode);
    // init refines values
    CompatRefineConstIterator it=_refines.begin();
    double nextRefineX;
    // Currently refines are for Rayleigh fundamental mode only
    if(imode==0 && it!=_refines.end()) {
      nextRefineX=(*it).omega();
    } else {
      nextRefineX=std::numeric_limits<double>::infinity();
      it=_refines.end();
    }
    // loop variables
    double val, invalidValue=_curves.invalidValue();
    int i=0;
    nfr=0; // initialize the real number of frequencies
    while(i<nf || it!=_refines.end()) {
      while(nextFixedX<nextRefineX) {
        val=point[i].value();
        if(val!=invalidValue) {
          s << inv2pi*nextFixedX << val;
          nfr++;
        }
        i++;
        if(i<nf) nextFixedX=omega(i);
        else {
          nextFixedX=std::numeric_limits<double>::infinity();
          break;
        }
      }
      while(nextRefineX<nextFixedX) {
        val=(*it).value(imode);
        if(val!=invalidValue) {
          s << inv2pi*nextRefineX << val;
          nfr++;
        }
        ++it;
        if(it!=_refines.end())
          nextRefineX=(*it).omega();
        else {
          nextRefineX=std::numeric_limits<double>::infinity();
          break;
        }
      }
    }
    // Write the real number of frequencies in the table
    qint64 curPos=s.device()->pos();
    s.device()->seek(nSamplesPos);
    s << nfr;
    s.device()->seek(curPos);
  }
}

void CompatModalRefinedCurves::xml_writeChildren(XML_WRITECHILDREN_ARGS) const
{
  TRACE;
  _curves.xml_save(s, context);
}

XMLMember CompatModalRefinedCurves::xml_member(XML_MEMBER_ARGS)
{
  TRACE;
  if(tag=="ModalCurves") return XMLMember(&_curves);
  else return CompatModalFrequency::xml_member(tag, attributes, context);
}

} // namespace QGpCompatibility
