// Copyright 2019, 2020 Filippo Rusconi
// License: GPLv3+

/////////////////////// StdLib includes
#include <vector>
#include <cmath>


/////////////////////// Qt includes
#include <QDebug>
#include <QDataStream>
#include <QRegularExpressionMatch>


/////////////////////// Local includes
#include "datapoint.h"
#include "pappsomspp/core/types.h"
#include "pappsomspp/core/utils.h"
#include "pappsomspp/core/pappsoexception.h"
#include "pappsomspp/core/exception/exceptionoutofrange.h"
#include "pappsomspp/core/exception/exceptionnotpossible.h"


int dataPointMetaTypeId = qRegisterMetaType<pappso::DataPoint>("pappso::DataPoint");


int dataPointCstSPtrMetaTypeId =
  qRegisterMetaType<pappso::DataPointCstSPtr>("pappso::DataPointCstSPtr");

namespace pappso
{


DataPoint::DataPoint(pappso_double x, pappso_double y) : x(x), y(y)
{
}


DataPoint::DataPoint(std::pair<pappso_double, pappso_double> pair) : x(pair.first), y(pair.second)
{
}


DataPoint::DataPoint(const QString &text)
{
  if(!initialize(text))
    throw ExceptionNotPossible(
      "Failed to initialize the DataPoint object using the provided string.");
}

DataPoint::DataPoint(const DataPoint &other) : x(other.x), y(other.y)
{
}


// For debugging purposes.
// DataPoint::~DataPoint()
//{
////qDebug() << __FILE__ << "@" << __LINE__ << __FUNCTION__ << "()"
////<< "Calling destructor for DataPoint.";
//}


DataPointCstSPtr
DataPoint::makeDataPointCstSPtr() const
{
  return std::make_shared<const DataPoint>(*this);
}


void
DataPoint::initialize(pappso_double x, pappso_double y)
{
  this->x = x;
  this->y = y;
}


void
DataPoint::initialize(const DataPoint &other)
{
  x = other.x;
  y = other.y;
}


bool
DataPoint::initialize(const QString &text)
{

  QRegularExpressionMatch regExpMatch;

  regExpMatch = Utils::xyMassDataFormatRegExp.match(text);

  if(!regExpMatch.hasMatch())
    return false;

  bool ok = false;

  double key = regExpMatch.captured(1).toDouble(&ok);

  if(!ok)
    return false;

  // Note that group 2 is the separator group.

  double val = regExpMatch.captured(3).toDouble(&ok);

  if(!ok)
    return false;

  x = key;
  y = val;

  return true;
}


void
DataPoint::reset()
{
  x = -1;
  y = 0;
}


bool
DataPoint::isValid() const
{
  return (x >= 0);
}


QString
DataPoint::toString() const
{
  return QString("%1 %2").arg(x, 0, 'f', 15).arg(y, 0, 'f', 15);
}


QString
DataPoint::toString(int decimals) const
{
  return QString("%1 %2").arg(x, 0, 'f', decimals).arg(y, 0, 'f', decimals);
}


QDataStream &
operator<<(QDataStream &out, const DataPoint &dataPoint)
{
  out << dataPoint.x;
  out << dataPoint.y;

  return out;
}


QDataStream &
operator>>(QDataStream &in, DataPoint &dataPoint)
{

  if(in.atEnd())
    {
      throw PappsoException(QString("error in QDataStream unserialize operator>> of massSpectrum "
                                    "dataPoint:\nread datastream failed status=%1")
                              .arg(in.status()));
    }
  in >> dataPoint.x;
  in >> dataPoint.y;

  return in;
}


void
DataPoint::incrementX(pappso_double value)
{
  x += value;
}


void
DataPoint::incrementY(pappso_double value)
{
  y += value;
}

bool
DataPoint::operator==(const DataPoint &other) const
{
  return ((x == other.x) && (y == other.y));
}


DataPoint &
DataPoint::operator=(const DataPoint &other)
{
  x = other.x;
  y = other.y;

  return *this;
}


} // namespace pappso
