/***************************************************************************
**
**  This file is part of QGpCoreWave.
**
**  This library is free software; you can redistribute it and/or
**  modify it under the terms of the GNU Lesser General Public
**  License as published by the Free Software Foundation; either
**  version 2.1 of the License, or (at your option) any later version.
**
**  This file 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 Lesser General Public
**  License for more details.
**
**  You should have received a copy of the GNU Lesser General Public
**  License along with this library; if not, write to the Free Software
**  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
**
**  See http://www.geopsy.org for more information.
**
**  Created: 2005-12-21
**  Copyright: 2005-2019
**    Marc Wathelet
**    Marc Wathelet (LGIT, Grenoble, France)
**
***************************************************************************/

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

namespace QGpCoreWave {

/*!
  \class Profile Profile.h
  \brief Storage for a 1D profile.

  A profile is made of layers with a uniform value over the depth axis.
  Internally 2 vectors ar maintained, one for the depths and the other for
  the values of the parameter. The first depth is not null. It is the bottom
  depth of the first layer.
*/


/*!
  Set this profile as the resample of \a p with \a baseD. All depths of \a p must belong to \a baseD.
  \a p must be different from this.
*/
void Profile::resample(const Profile& p, const QVector<double>& baseD)
{
  TRACE;
  _depths.clear();
  _values.clear();
  QVector<double>::const_iterator itBase=baseD.begin();
  int i=0, n=p._depths.count();
  double nextDepth=p._depths.at(i);
  double value=p._values.at(i);
  double depth;
  while(true) {
    depth=*(itBase++);
    _depths.append(depth);
    _values.append(value);
    if(depth==nextDepth) {
      ++i;
      if(i>=n) break;
      nextDepth=p._depths.at(i);
      value=p._values.at(i);
    }
  }
  /*App::log("resample(const Profile& p, const QList<double>& baseD)" << endl;
  int nd=_depths.count();
  for(int i=0;i<nd;i++) {
    App::log("  " << _depths.at(i) << " ; " << _values.at(i) << endl;
  }*/
}

/*!
  Add samples from \a baseD to this profile if they do not exist already. Depths must be ordered
*/
void Profile::resample(const QVector<double>& baseD)
{
  TRACE;
  /*App::log("resample(const QList<double>& baseD): baseD" << endl;
  for(int i=0;i<baseD.count();i++) {
    App::log("  " << baseD.at(i) << endl;
  }*/
  int i=0, n=count();
  for(QVector<double>::const_iterator it=baseD.begin(); it!=baseD.end(); it++ ) {
    double depth=*it;
    while(i<n && _depths.at(i)<depth) i++;
    if(i<n) {
      if(_depths.at(i)>depth) {
        _depths.insert(i, depth);
        _values.insert(i, _values[i] );
        i++; n++;
      }
    } else break;
  }
  /*App::log("resample(const QList<double>& baseD): result" << endl;
  for(int i=0;i<_depths.count();i++) {
    App::log("  " << _depths.at(i) << " ; " << _values.at(i) << endl;
  }*/
}

/*!
  Remove all samples that are not in \a baseD. Depths must be ordered
*/
void Profile::setSamples(const QVector<double>& baseD)
{
  TRACE;
  /*App::log("setSamples(const QList<double>& baseD): baseD" << endl;
  for(int i=0;i<baseD.count();i++) {
    App::log("  " << baseD.at(i) << endl;
  }*/
  int i=0, n=count();
  for(QVector<double>::const_iterator it=baseD.begin(); it!=baseD.end(); it++ ) {
    double depth=*it;
    while(i<n && _depths.at(i)<depth) {
      _depths.remove(i);
      _values.remove(i);
      n--;
    }
    i++;
    if(i>=n) break;
  }
  while(i<n) {
      _depths.remove(i);
      _values.remove(i);
      n--;
  }
  /*App::log("setSamples(const QList<double>& baseD): result" << endl;
  for(int i=0;i<_depths.count();i++) {
    App::log("  " << _depths.at(i) << " ; " << _values.at(i) << endl;
  }*/
}

/*
  Returns the value at \a depth assuming continuous change of the value (interpolation).
*/
double Profile::interpoleAt(double depth)
{
  TRACE;
  int i=0;
  int n=_depths.count();
  if(n<2) return 0.0;
  while(i<n && depth<_depths[i]) i++;
  if(i>=n-1) i=n-2;
  return _values[i]+(_values[i+1]-_values[i])/(_depths[i+1]-_depths[i])*(depth-_depths[i]);
}

/*
  Returns the value at \a depth assuming uniform layers.
*/
double Profile::uniformAt(double depth)
{
  TRACE;
  int i=0;
  int n=_depths.count();
  while(i<n && depth>_depths[i] ) i++;
  if(i==n) i--;
  return _values[i];
}

/*!
  Calculates the average values.
  The average is defined by a mean weighted by the
  thicknesses. If the parameter is a slowness, it is the total
  thickness down to a depth divided by the total time needed
  to cross the layer stack to that depth.
*/
void Profile::average()
{
  TRACE;
  int n=_depths.count();
  if(n==0) return;
  double sum=_values[0]*_depths[0];
  for(int i=1; i<n; i++) {
    sum+=_values[i]*(_depths[i]-_depths[i-1]);
    _values[i]=sum/_depths[i];
  }
}

/*!
  Inverses values
*/
void Profile::inverse()
{
  TRACE;
  int n=_depths.count();
  for(int i=0; i<n; i++ ) {
    _values[i]=1.0/_values[i];
  }
}

void Profile::report2plot(QDataStream& s, Point2D * points)
{
  TRACE;
  int n;
  s >> n;
  double lastDepth=0.0;
  double depth,value;
  for(int i=0;i<n;i++) {
    s >> depth;
    s >> value;
    points[0].setY(lastDepth);
    points[0].setX(value);
    points[1].setY(depth);
    points[1].setX(value);
    points+=2;
    lastDepth=depth;
  }
}

Curve<Point2D> Profile::curve() const
{
  TRACE;
  int n=count();
  double lastDepth=0.0;
  Curve<Point2D> c;
  for(int i=0;i<n;i++) {
    c.append(Point2D(_values[i], lastDepth));
    lastDepth=_depths[i];
    c.append(Point2D(_values[i], lastDepth));
  }
  return c;
}

void Profile::toStream(QTextStream& s, bool plotMode) const
{
  TRACE;
  static const QString fmt("%1 %2\n");
  int n=count();
  if(plotMode) {
    double lastDepth=0.0;
    for(int i=0;i<n;i++) {
      s << fmt.arg(_values[i], 20, 'g', 15).arg(lastDepth, 20, 'g', 15);
      lastDepth=_depths[i];
      s << fmt.arg(_values[i], 20, 'g', 15).arg(lastDepth, 20, 'g', 15);
    }
  } else {
    for(int i=0;i<n;i++) {
      s << fmt.arg(_values[i], 20, 'g', 15).arg(_depths[i], 20, 'g', 15);
    }
  }
  s << flush;
}

void Profile::print() const
{
  TRACE;
  int n=count();
  for(int i=0;i<n;i++) {
    printf("%20.15lg %20.15lg\n", _values[i], _depths[i]);
  }
}

void Profile::addValue(double depth, double value)
{
  _depths.append(depth);
  _values.append(value);
}

} // namespace QGpCoreWave
