/***************************************************************************
**
**  This file is part of QGpCoreMath.
**
**  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: 2006-09-25
**  Copyright: 2006-2019
**    Marc Wathelet
**    Marc Wathelet (LGIT, Grenoble, France)
**
***************************************************************************/

#ifndef STATISTICALPOINT_H
#define STATISTICALPOINT_H

#include <math.h>

#include "StatisticalValue.h"
#include "ComplexPointOptions.h"
#include "QGpCoreMathDLLExport.h"

namespace QGpCoreMath {

template <class numberType>
class QGPCOREMATH_EXPORT StatisticalPoint : public StatisticalValue<numberType>
{
public:
  StatisticalPoint() {_x=0.0;}
  StatisticalPoint(double x, const numberType& y)
    : StatisticalValue<numberType>(y), _x(x) {}

  inline void operator=(const StatisticalPoint<numberType>& o);
  inline void operator=(const StatisticalValue<numberType>& o);

  inline bool operator<(const StatisticalPoint<numberType>& p) const;
  inline bool operator==(const StatisticalPoint<numberType>& p) const;

  void setX(double v) {_x=v;}
  double x() const {return _x;}

  inline void interpole(double valX,
                        const StatisticalPoint<numberType>& p1,
                        const StatisticalPoint<numberType>& p2);

  bool fromString(const StringSection& str);
  QString toString(int precision=6, char format='g') const;
protected:
  void xml_writeProperties(XML_WRITEPROPERTIES_ARGS) const;
  XMLMember xml_member(XML_MEMBER_ARGS);
  bool xml_setProperty(XML_SETPROPERTY_ARGS);
private:
  double _x;
};

class QGPCOREMATH_EXPORT RealStatisticalPoint : public StatisticalPoint<double>
{
public:
  RealStatisticalPoint() {}
  RealStatisticalPoint(double x, const double& y) : StatisticalPoint<double>(x, y) {}

  virtual const QString& xml_tagName() const {return xmlRealStatisticalPointTag;}
  static const QString xmlRealStatisticalPointTag;

  inline void operator=(const RealStatisticalPoint& o);
  inline void operator=(const RealStatisticalValue& o);

  inline void setY(double v, const CurvePointOptions * options=0);
  inline double y(const CurvePointOptions * options=0) const;
};

inline void RealStatisticalPoint::operator=(const RealStatisticalPoint& o)
{
  StatisticalPoint<double>::operator=(o);
}

inline void RealStatisticalPoint::operator=(const RealStatisticalValue& o)
{
  StatisticalPoint<double>::operator=(o);
}

inline void RealStatisticalPoint::setY(double v, const CurvePointOptions * options)
{
  Q_UNUSED(options)
  setMean(v);
}

inline double RealStatisticalPoint::y(const CurvePointOptions * options) const
{
  Q_UNUSED(options)
  return StatisticalPoint<double>::mean();
}

class QGPCOREMATH_EXPORT ComplexStatisticalPoint : public StatisticalPoint<Complex>
{
public:
  ComplexStatisticalPoint() {}
  ComplexStatisticalPoint(double x, const Complex& y) : StatisticalPoint<Complex>(x, y) {}

  virtual const QString& xml_tagName() const {return xmlComplexStatisticalPointTag;}
  static const QString xmlComplexStatisticalPointTag;

  inline void operator=(const ComplexStatisticalPoint& o);
  inline void operator=(const ComplexStatisticalValue& o);

  void setY(double v, const CurvePointOptions * options);
  double y(const CurvePointOptions * options) const;
};

inline void ComplexStatisticalPoint::operator=(const ComplexStatisticalPoint& o)
{
  StatisticalPoint<Complex>::operator=(o);
}

inline void ComplexStatisticalPoint::operator=(const ComplexStatisticalValue& o)
{
  StatisticalPoint<Complex>::operator=(o);
}

template <class numberType>
inline void StatisticalPoint<numberType>::operator=(const StatisticalPoint<numberType>& o)
{
  TRACE;
  StatisticalValue<numberType>::operator=(o);
  _x=o._x;
}

template <class numberType>
inline void StatisticalPoint<numberType>::operator=(const StatisticalValue<numberType>& o)
{
  TRACE;
  StatisticalValue<numberType>::operator=(o);
}

template <class numberType>
inline bool StatisticalPoint<numberType>::operator<(const StatisticalPoint<numberType>& p) const
{
  return _x<p._x || (_x==p._x && StatisticalValue<numberType>::mean()<p.mean());
}

template <class numberType>
inline bool StatisticalPoint<numberType>::operator==(const StatisticalPoint<numberType>& p) const
{
  return _x==p._x && StatisticalValue<numberType>::operator==(p);
}

template <class numberType>
inline void StatisticalPoint<numberType>::interpole(double valX,
                                                    const StatisticalPoint<numberType>& p1,
                                                    const StatisticalPoint<numberType>& p2)
{
  TRACE;
  double factor=(valX-p1.x())/(p2.x()-p1.x());
  setX(valX);
  this->setMean(p1.mean()+(p2.mean()-p1.mean())*factor);
  this->setStddev(p1.stddev()+(p2.stddev()-p1.stddev())*factor);
  this->setWeight(p1.weight()+(p2.weight()-p1.weight())*factor);
}

template <class numberType>
void StatisticalPoint<numberType>::xml_writeProperties(XML_WRITEPROPERTIES_ARGS) const
{
  TRACE;
  Q_UNUSED(context)
  StatisticalPoint<numberType>::writeProperty(s,"x",_x);
  StatisticalValue<numberType>::xml_writeProperties(s, context);
}

template <class numberType>
XMLMember StatisticalPoint<numberType>::xml_member(XML_MEMBER_ARGS)
{
  TRACE;
  Q_UNUSED(context)
  if(tag=="x") return XMLMember(0);
  else return StatisticalValue<numberType>::xml_member(tag, attributes, context)+1;
}

template <class numberType>
bool StatisticalPoint<numberType>::xml_setProperty(XML_SETPROPERTY_ARGS)
{
  TRACE;
  Q_UNUSED(context)
  switch (memberID) {
  case 0: _x=content.toDouble(); return true;
  default: return StatisticalValue<numberType>::xml_setProperty(memberID-1, tag, attributes, content, context);
  }
}

template <class numberType>
QString StatisticalPoint<numberType>::toString(int precision, char format) const
{
  TRACE;
  QString tmp;
  tmp+=QString::number(_x, format, precision);
  tmp+=" ";
  tmp+=QString::number(StatisticalValue<numberType>::mean(), format, precision);
  tmp+=" ";
  tmp+=QString::number(StatisticalValue<numberType>::stddev(), format, precision);
  if(StatisticalValue<numberType>::isValid()) {
    tmp+=" ";
    tmp+=QString::number(StatisticalValue<numberType>::weight(), format, precision);
  } else {
    tmp+=" 0";
  }
  return tmp;
}

template <class numberType>
bool StatisticalPoint<numberType>::fromString(const StringSection& str)
{
  TRACE;
  const QChar * ptr=0;
  StringSection f;
  f=str.nextField(ptr);
  if(f.isValid()) _x=f.toDouble(); else return false;
  numberType tmp;
  if(str.nextNumber(tmp, ptr)) {
    StatisticalValue<numberType>::setMean(tmp);
  } else return false;
  f=str.nextField(ptr);
  if(f.isValid()) StatisticalValue<numberType>::setStddev(f.toDouble()); else return false;
  f=str.nextField(ptr);
  if(f.isValid()) {
    double w=f.toDouble();
    if(w==0.0) {
      StatisticalValue<numberType>::setValid(false);
      StatisticalValue<numberType>::setWeight(1.0);
    } else {
      StatisticalValue<numberType>::setValid(true);
      StatisticalValue<numberType>::setWeight(w);
    }
  } else return false;
  return true;
}

} // namespace QGpCoreMath

#endif // STATISTICALPOINT_H
