#ifndef NDEBUG

#include "assertions.h"
#include "perimeterrange.h"
#include "labelcandidates.h"
#include "labelmodel.h"

namespace {

constexpr qreal epsilon = 0.00001;

} // unnamed namespace

void assertEqual(qreal x1, qreal x2)
{
    assert(std::abs(x2-x1) <= epsilon);
}
void assertEqual(QPointF const& p1, QPointF const& p2)
{
    assertEqual(p1.x(), p2.x());
    assertEqual(p1.y(), p2.y());
}
void assertEqual(PerimeterRange const& range1, PerimeterRange const& range2)
{
    assertEqual(range1.first,  range2.first);
    assertEqual(range1.second, range2.second);
}

void assertEqual(LabelCandidateSegment const& segment1, LabelCandidateSegment const& segment2)
{
    assert(segment1.isEmpty() == segment2.isEmpty());
    assert(segment1.isDegenerate() == segment2.isDegenerate());
    if (!segment1.isEmpty()) {
        assertEqual(segment1.leftmostPoint(),  segment2.leftmostPoint());
        assertEqual(segment1.rightmostPoint(), segment2.rightmostPoint());
    }
}
void assertEqual(LabelCandidates const& candidates1, LabelCandidates const& candidates2)
{
    assertEqual(candidates1.leftmostCandidates(), candidates2.leftmostCandidates());
    assertEqual(candidates1.rightmostCandidates(), candidates2.rightmostCandidates());
    assertEqual(candidates1.topmostCandidates(), candidates2.topmostCandidates());
    assertEqual(candidates1.bottommostCandidates(), candidates2.bottommostCandidates());
}

void assertSubset(PerimeterRange const& subrange, PerimeterRange const& range)
{
    assert(range.first-epsilon <= subrange.first && subrange.second <= range.second+epsilon);
}
void assertSubsetAfterAlignment(PerimeterRange const& subrange, PerimeterRange range, qreal labelPerimeter)
{
    alignPerimeterRanges(subrange, range, labelPerimeter);
    assertSubset(subrange, range);
}
void assertSubsetAfterAlignment(PerimeterRange const& subrange, PerimeterRange range, LabelModel const& labelModel)
{
    assertSubsetAfterAlignment(subrange, range, labelModel.labelPerimeter());
}

namespace {

void assertProperSubsegment(LabelCandidateSegment const& subsegment, LabelCandidateSegment const& segment)
{
    assert(subsegment.isHorizontal() == segment.isHorizontal());
    assert(subsegment.isVertical()   == segment.isVertical());
    if (segment.isHorizontal()) {
        assertEqual(subsegment.leftmostPoint().y(), segment.leftmostPoint().y());
        assertSubset({subsegment.leftmostPoint().x(), subsegment.rightmostPoint().x()},
                       {segment.leftmostPoint().x(), segment.rightmostPoint().x()});
    } else {
        assertEqual(subsegment.topmostPoint().x(), segment.topmostPoint().x());
        assertSubset({subsegment.topmostPoint().y(), subsegment.bottommostPoint().y()},
                       {segment.topmostPoint().y(), segment.bottommostPoint().y()});
    }
}
void assertDegenerateSubsegment(LabelCandidateSegment const& subsegment, LabelCandidateSegment const& segment)
{
    QPointF a1 = subsegment.leftmostPoint(), a2 = subsegment.rightmostPoint();
    QPointF b1 = segment.leftmostPoint(),    b2 = segment.rightmostPoint();
    assert(a1 == b1 || a1 == b2);
    assert(a2 == b1 || a2 == b2);
}

} // unnamed namespace

void assertSubset(LabelCandidateSegment const& subsegment, LabelCandidateSegment const& segment)
{
    if (segment.isEmpty()) {
        assert(subsegment.isEmpty());
    } else if (!subsegment.isEmpty()) {
        if (segment.isDegenerate()) {
            assertDegenerateSubsegment(subsegment, segment);
        } else {
            assertProperSubsegment(subsegment, segment);
        }
    }
}

void assertSubset(LabelCandidates const& subcandidates, LabelCandidates const& candidates)
{
    assertSubset(subcandidates.leftmostCandidates(),   candidates.leftmostCandidates());
    assertSubset(subcandidates.rightmostCandidates(),  candidates.rightmostCandidates());
    assertSubset(subcandidates.topmostCandidates(),    candidates.topmostCandidates());
    assertSubset(subcandidates.bottommostCandidates(), candidates.bottommostCandidates());
}

void assertMonotonicInX(std::vector<QPointF> const& vertices)
{
    if (vertices.size() <= 2) { return; }

    auto i1 = vertices.begin();
    auto i2 = std::next(i1);
    auto i3 = std::next(i2);
    auto iend = vertices.end();
    for (; i3 != iend; ++i1, ++i2, ++i3) {
        qreal diff1 = i2->x() - i1->x();
        qreal diff2 = i3->x() - i2->x();
        bool sameSign = diff1 * diff2 > 0;
        assert(sameSign);
    }
}

#endif // NDEBUG
