#ifndef LABELCANDIDATES_H
#define LABELCANDIDATES_H

#include <QPointF>

class LabelCandidates;
class LabelCandidateSegment {
public:
    LabelCandidateSegment();
    LabelCandidateSegment(QPointF const& endpoint1, QPointF const& endpoint2);

    LabelCandidateSegment(LabelCandidateSegment const&) = default;
    LabelCandidateSegment& operator=(LabelCandidateSegment const&) = default;

    // named constructors
    static LabelCandidateSegment empty();
    static LabelCandidateSegment singleton(QPointF const& point);
    static LabelCandidateSegment doubleton(QPointF const& point1, QPointF const& point2);
    static LabelCandidateSegment segment(QPointF const& endpoint1, QPointF const& endpoint2);

    void makeEmpty();
    void reset(QPointF const& endpoint1, QPointF const& endpoint2);

    bool isEmpty() const;
    bool isDegenerate() const;
    bool isHorizontal() const;
    bool isVertical() const;

    void turnClockwise();
    void turnCounterClockwise();
    void mirror();

    QPointF const& leftmostPoint() const;
    QPointF const& rightmostPoint() const;
    QPointF const& topmostPoint() const;
    QPointF const& bottommostPoint() const;

    QPointF& leftmostPoint();
    QPointF& rightmostPoint();
    QPointF& topmostPoint();
    QPointF& bottommostPoint();

    // Remove all label candidates intersecting the label placed at `labelCenter`.  Return whether this removed anything.
    bool removeIntersecting(QPointF const& labelCenter, qreal labelWidth, qreal labelHeight);
    bool removeAlmostIntersecting(QPointF const& labelCenter, qreal labelWidth, qreal labelHeight, qreal epsilon = 0.1)
    { return removeIntersecting(labelCenter, labelWidth+0.5*epsilon, labelHeight+0.5*epsilon); }
    bool removeDefinitelyIntersecting(QPointF const& labelCenter, qreal labelWidth, qreal labelHeight, qreal epsilon = 0.1)
    { return removeIntersecting(labelCenter, labelWidth-0.5*epsilon, labelHeight-0.5*epsilon); }

    // Remove all label candidates that intersect all of `candidates`.  Return whether this removed anything.
    bool removeBlocking(LabelCandidates const& candidates, qreal labelWidth, qreal labelHeight);
    bool removeAlmostBlocking(LabelCandidates const& candidates, qreal labelWidth, qreal labelHeight, qreal epsilon = 0.1)
    { return removeBlocking(candidates, labelWidth+0.5*epsilon, labelHeight+0.5*epsilon); }
    bool removeDefinitelyBlocking(LabelCandidates const& candidates, qreal labelWidth, qreal labelHeight, qreal epsilon = 0.1)
    { return removeBlocking(candidates, labelWidth-0.5*epsilon, labelHeight-0.5*epsilon); }

    void printForIpe() const;
private:
    bool removeBlockingWhenHorizontal(LabelCandidateSegment const& blocker, qreal labelWidth, qreal labelHeight);
    bool removeBlockingWhenVertical(LabelCandidateSegment const& blocker, qreal labelWidth, qreal labelHeight);

private:
    QPointF _leftPoint, _rightPoint;
    bool _isDoubleton;
};

class LabelCandidates {
private:
    enum { L,R,T,B };
    LabelCandidates(LabelCandidateSegment l, LabelCandidateSegment r,
                    LabelCandidateSegment t, LabelCandidateSegment b,
                    int w, int h)
        : _labelWidth(w), _labelHeight(h)
    {
        _segments[L] = l;
        _segments[R] = r;
        _segments[T] = t;
        _segments[B] = b;
    }
public:
    LabelCandidates(LabelCandidates const&) = default;
    LabelCandidates& operator=(LabelCandidates const&) = default;

    // named constructors
    static LabelCandidates leftSlider(QPointF const& p, int w, int h);
    static LabelCandidates rightSlider(QPointF const& p, int w, int h);
    static LabelCandidates topSlider(QPointF const& p, int w, int h);
    static LabelCandidates bottomSlider(QPointF const& p, int w, int h);
    static LabelCandidates fourSliders(QPointF const& p, int w, int h);

    bool isEmpty() const;

    void turnClockwise();
    void turnCounterClockwise();
    void mirror();

    QPointF const& leftmostPoint() const;
    QPointF& leftmostPoint();

    LabelCandidateSegment& leftmostCandidates()   { return _segments[L]; }
    LabelCandidateSegment& rightmostCandidates()  { return _segments[R]; }
    LabelCandidateSegment& topmostCandidates()    { return _segments[T]; }
    LabelCandidateSegment& bottommostCandidates() { return _segments[B]; }

    LabelCandidateSegment const& leftmostCandidates() const   { return _segments[L]; }
    LabelCandidateSegment const& rightmostCandidates() const  { return _segments[R]; }
    LabelCandidateSegment const& topmostCandidates() const    { return _segments[T]; }
    LabelCandidateSegment const& bottommostCandidates() const { return _segments[B]; }

    // Remove all label candidates intersecting the label placed at `labelCenter`.  Return whether this removed anything.
    bool removeIntersecting(QPointF const& labelCenter)
    { return removeAlmostIntersecting(labelCenter, 0.0); }
    bool removeAlmostIntersecting(QPointF const& labelCenter, qreal epsilon = 0.1);
    bool removeDefinitelyIntersecting(QPointF const& labelCenter, qreal epsilon = 0.1)
    { return removeAlmostIntersecting(labelCenter, -epsilon); }

    // Remove all label candidates that intersect all of `candidates`.  Return whether this removed anything.
    bool removeBlocking(LabelCandidates const& candidates)
    { return removeAlmostBlocking(candidates, 0.0); }
    bool removeAlmostBlocking(LabelCandidates const& candidates, qreal epsilon = 0.1);
    bool removeDefinitelyBlocking(LabelCandidates const& candidates, qreal epsilon = 0.1)
    { return removeAlmostBlocking(candidates, -epsilon); }

    void printForIpe() const;
private:
    LabelCandidateSegment _segments[4];
    int _labelWidth, _labelHeight;
};

#endif // LABELCANDIDATES_H
