// Copyright 2021 Filippo Rusconi
// GPLv3+


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


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


/////////////////////// Local includes
#include "integrationscoperect.h"


namespace pappso
{

IntegrationScopeRect::IntegrationScopeRect() : IntegrationScope()
{
  // qDebug() << "Constructing" << this;
}

IntegrationScopeRect::IntegrationScopeRect(const QPointF &point,
                                           double width,
                                           double height)
  : IntegrationScope(point, width), m_height(height)
{
  // qDebug() << "Constructing" << this << "point:" << point << "width:" << width
           // << "height:" << height;
}


IntegrationScopeRect::IntegrationScopeRect(const QPointF &point,
                                           double width,
                                           DataKind data_kind_x,
                                           double height,
                                           DataKind data_kind_y)
  : IntegrationScope(point, width, data_kind_x),
    m_height(height),
    m_dataKindY(data_kind_y)
{
  // qDebug() << "Constructing" << this << "point:" << point << "width:" << width
  //          << "height:" << height
  //          << "data_kind_x:" << static_cast<int>(data_kind_x)
  //          << "data_kind_y:" << static_cast<int>(data_kind_y);
}

IntegrationScopeRect::IntegrationScopeRect(const IntegrationScopeRect &other)
  : IntegrationScope(other.m_point, other.m_width, other.m_dataKindX),
    m_height(other.m_height),
    m_dataKindY(other.m_dataKindY)
{
 // qDebug() << "Constructing" << this << "point:" << point << "width:" << width
 //          << "height:" << height
 //          << "data_kind_x:" << static_cast<int>(data_kind_x)
 //          << "data_kind_y:" << static_cast<int>(data_kind_y);
 }

IntegrationScopeRect::~IntegrationScopeRect()
{
  // qDebug() << "Destructing" << this;
}

IntegrationScopeRect &
IntegrationScopeRect::operator=(const IntegrationScopeRect &other)
{
  if(this == &other)
    return *this;

  IntegrationScope::operator=(other);

  m_height = other.m_height;

  m_dataKindX = other.m_dataKindX;
  m_dataKindY = other.m_dataKindY;

  return *this;
}

IntegrationScopeFeatures
IntegrationScopeRect::getTopMostPoint(QPointF &point) const
{
  point = QPointF(m_point.x(), m_point.y() + m_height);
  return IntegrationScopeFeatures::SUCCESS;
}

IntegrationScopeFeatures
IntegrationScopeRect::getBottomMostPoint(QPointF &point) const
{
  point = m_point;
  return IntegrationScopeFeatures::SUCCESS;
}

void
IntegrationScopeRect::setHeight(double height)
{
  m_height = height;
}

IntegrationScopeFeatures
IntegrationScopeRect::getHeight(double &height) const
{ 
  height = m_height;
  return IntegrationScopeFeatures::SUCCESS;
}

bool
IntegrationScopeRect::range(Axis axis, double &start, double &end) const
{
  if(axis == Axis::x)
    {
      start = m_point.x();
      end   = start + m_width;

      return true;
    }
  else if(axis == Axis::y)
    {
      start = m_point.y();
      end   = start + m_height;

      return true;
    }

  return false;
}

void
IntegrationScopeRect::setDataKindX([[maybe_unused]] DataKind data_kind)
{
  m_dataKindX = data_kind;
}

void
IntegrationScopeRect::setDataKindY([[maybe_unused]] DataKind data_kind)
{
  m_dataKindY = data_kind;
}

bool
IntegrationScopeRect::getDataKindX([[maybe_unused]] DataKind &data_kind)
{
  data_kind = m_dataKindX;
  return true;
}

bool
IntegrationScopeRect::getDataKindY([[maybe_unused]] DataKind &data_kind)
{
  data_kind = m_dataKindY;
  return true;
}

bool
IntegrationScopeRect::is1D() const
{
  return false;
}

bool
IntegrationScopeRect::is2D() const
{
  return !is1D();
}

bool
IntegrationScopeRect::isRectangle() const
{
  return true;
}

bool
IntegrationScopeRect::isRhomboid() const
{
  return false;
}

bool
IntegrationScopeRect::transpose()
{
  DataKind was_data_kind_y = m_dataKindY;
  m_dataKindY              = m_dataKindX;
  m_dataKindX              = was_data_kind_y;

  // qDebug() << "Point before rectangle transposition:" << m_point;

  // Transpose the point.
  QPointF transposed_point(m_point.y(), m_point.x());
  m_point = transposed_point;

  //qDebug() << "Point after rectangle transposition:" << m_point;

  // qDebug() << "Before transposition: width is" << m_width << "and height is"
  //          << m_height;

  // Tranpose width <--> height
  double was_height = m_height;
  m_height          = m_width;
  m_width           = was_height;

  // qDebug() << "After transposition: width is" << m_width << "and height is"
  //          << m_height;

  return true;
}

void
IntegrationScopeRect::update(const QPointF &point, double width, double height)
{
  IntegrationScope::m_point = point;
  IntegrationScope::m_width = width;
  m_height                  = height;
}

bool
IntegrationScopeRect::contains(const QPointF &point) const
{
  return (point.x() >= m_point.x() && point.x() <= m_point.x() + m_width &&
          point.y() >= m_point.y() && point.y() <= m_point.y() + m_height);
}

QString
IntegrationScopeRect::toString() const
{
  QString text = "[";

  text.append(QString("(%1, %2)")
                .arg(m_point.x(), 0, 'f', 3)
                .arg(m_point.y(), 0, 'f', 3));

  text.append("->");

  text.append(QString("(%1, %2)")
                .arg(m_point.x() + m_width, 0, 'f', 3)
                .arg(m_point.y() + m_height, 0, 'f', 3));

  text.append("]");

  return text;
}

void
IntegrationScopeRect::reset()
{
  IntegrationScope::reset();

  m_height = 0;
}

} // namespace pappso
