#pragma once

#include <QLineF>
#include <QPointF>
#include <QPolygonF>
#include <QtMath>

#include "GenericCircle.h"
#include "PolygonCalculus.h"

using Circle = GenericCircle<qreal, QPointF>;

namespace PlanimetryCalculus {

enum IntersectType {
  InsideNoIntersection,
  InsideTouching,
  InsideIntersection,
  OutsideIntersection,
  OutsideTouching,
  OutsideNoIntersection,
  CirclesEqual, // Circle Circle intersection

  Tangent,
  Secant, // Circle Line Intersetion

  EdgeCornerIntersection,
  EdgeEdgeIntersection,
  CornerCornerIntersection,
  LinesParallel,
  LinesEqual, // Line Line intersection

  Interior, // Polygon contains

  NoIntersection,
  Error // general
};

typedef QVector<QPair<int, int>> NeighbourVector;
typedef QVector<QPointF> QPointFVector;
typedef QVector<IntersectType> IntersectVector;

void rotateReference(QPointF &point, double alpha);
void rotateReference(QPointFVector &points, double alpha);
void rotateReference(QLineF &line, double alpha);
// void rotateReference(QPolygonF &polygon, double alpha);

QPointF rotateReturn(QPointF point, double alpha);
QPointFVector rotateReturn(QPointFVector points, double alpha);
QLineF rotateReturn(QLineF line, double alpha);
// QPolygonF       rotateReturn(QPolygonF &polygon, double alpha);

bool intersects(const Circle &circle1, const Circle &circle2);
bool intersects(const Circle &circle1, const Circle &circle2,
                IntersectType &type);
bool intersects(const Circle &circle1, const Circle &circle2,
                QPointFVector &intersectionPoints);
bool intersects(const Circle &circle1, const Circle &circle2,
                QPointFVector &intersectionPoints, IntersectType &type);

bool intersects(const Circle &circle, const QLineF &line);
bool intersects(const Circle &circle, const QLineF &line, IntersectType &type);
bool intersects(const Circle &circle, const QLineF &line,
                QPointFVector &intersectionPoints);
bool intersects(const Circle &circle, const QLineF &line,
                QPointFVector &intersectionPoints, IntersectType &type);

bool intersects(const Circle &circle, const QPolygonF &polygon);
bool intersects(const Circle &circle, const QPolygonF &polygon,
                QVector<QPointFVector> &intersectionPoints);
bool intersects(const Circle &circle, const QPolygonF &polygon,
                QVector<QPointFVector> &intersectionPoints,
                IntersectVector &typeList);
bool intersects(const Circle &circle, const QPolygonF &polygon,
                QVector<QPointFVector> &intersectionPoints,
                NeighbourVector &neighbourList);
bool intersects(const Circle &circle, const QPolygonF &polygon,
                QVector<QPointFVector> &intersectionPoints,
                NeighbourVector &neighbourList, IntersectVector &typeList);

bool intersects(const QLineF &line1, const QLineF &line2);
bool intersects(const QLineF &line1, const QLineF &line2,
                QPointF &intersectionPt);
bool intersects(const QLineF &line1, const QLineF &line2,
                QPointF &intersectionPt, IntersectType &type);
//    bool intersectsFast(const QLineF &line1, const QLineF &line2, QPointF
//    &intersectionPt, IntersectType &type);
bool intersects(const QPolygonF &polygon, const QLineF &line,
                QPointFVector &intersectionList);
bool intersects(const QPolygonF &polygon, const QLineF &line,
                QPointFVector &intersectionList, IntersectVector &typeList);
bool intersects(const QPolygonF &polygon, const QLineF &line,
                QPointFVector &intersectionList,
                NeighbourVector &neighbourList);
bool intersects(const QPolygonF &polygon, const QLineF &line,
                QPointFVector &intersectionList, NeighbourVector &neighbourList,
                IntersectVector &typeList);

bool intersectsFast(const QPolygonF &polygon, const QLineF &line);

bool contains(const QLineF &line, const QPointF &point);
bool contains(const QLineF &line, const QPointF &point, IntersectType &type);
bool contains(const QPolygonF &polygon, const QPointF &point);
bool contains(const QPolygonF &polygon, const QPointF &point,
              IntersectType &type);

double distance(const QPointF &p1, const QPointF p2);
double norm(double x, double y);
double norm(const QPointF &p);
double angle(const QPointF &p1, const QPointF p2);
double angle(const QPointF &p1);
double angle(const QLineF &line);
double angleDegree(const QPointF &p1, const QPointF p2);
double truncateAngle(double angle);
double truncateAngleDegree(double angle);

/*!
 * \fntemplate <typename T> int signum(T val)
 * Returns the signum of a value \a val.
 *
 * \sa QPair, QVector
 */
template <typename T> int signum(T val) { return (T(0) < val) - (val < T(0)); }
} // namespace PlanimetryCalculus
