/*
 * Copyright 2012 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */
#include "SkOpContour.h"
#include "SkPath.h"

#ifdef SK_DEBUG
#include "SkPathOpsPoint.h"
#endif

class SkIntersectionHelper {
public:
    enum SegmentType {
        kHorizontalLine_Segment = -1,
        kVerticalLine_Segment = 0,
        kLine_Segment = SkPath::kLine_Verb,
        kQuad_Segment = SkPath::kQuad_Verb,
        kCubic_Segment = SkPath::kCubic_Verb,
    };

    bool addCoincident(SkIntersectionHelper& other, const SkIntersections& ts, bool swap) {
        return fContour->addCoincident(fIndex, other.fContour, other.fIndex, ts, swap);
    }

    // FIXME: does it make sense to write otherIndex now if we're going to
    // fix it up later?
    void addOtherT(int index, double otherT, int otherIndex) {
        fContour->addOtherT(fIndex, index, otherT, otherIndex);
    }

    bool addPartialCoincident(SkIntersectionHelper& other, const SkIntersections& ts, int index,
            bool swap) {
        return fContour->addPartialCoincident(fIndex, other.fContour, other.fIndex, ts, index,
                swap);
    }

    // Avoid collapsing t values that are close to the same since
    // we walk ts to describe consecutive intersections. Since a pair of ts can
    // be nearly equal, any problems caused by this should be taken care
    // of later.
    // On the edge or out of range values are negative; add 2 to get end
    int addT(const SkIntersectionHelper& other, const SkPoint& pt, double newT) {
        return fContour->addT(fIndex, other.fContour, other.fIndex, pt, newT);
    }

    int addSelfT(const SkIntersectionHelper& other, const SkPoint& pt, double newT) {
        return fContour->addSelfT(fIndex, other.fContour, other.fIndex, pt, newT);
    }

    bool advance() {
        return ++fIndex < fLast;
    }

    SkScalar bottom() const {
        return bounds().fBottom;
    }

    const SkPathOpsBounds& bounds() const {
        return fContour->segments()[fIndex].bounds();
    }

    void init(SkOpContour* contour) {
        fContour = contour;
        fIndex = 0;
        fLast = contour->segments().count();
    }

    bool isAdjacent(const SkIntersectionHelper& next) {
        return fContour == next.fContour && fIndex + 1 == next.fIndex;
    }

    bool isFirstLast(const SkIntersectionHelper& next) {
        return fContour == next.fContour && fIndex == 0
                && next.fIndex == fLast - 1;
    }

    bool isPartial(double t1, double t2, const SkDPoint& pt1, const SkDPoint& pt2) const {
        const SkOpSegment& segment = fContour->segments()[fIndex];
        double mid = (t1 + t2) / 2;
        SkDPoint midPtByT = segment.dPtAtT(mid);
        SkDPoint midPtByAvg = SkDPoint::Mid(pt1, pt2);
        return midPtByT.approximatelyPEqual(midPtByAvg);
    }

    SkScalar left() const {
        return bounds().fLeft;
    }

    const SkPoint* pts() const {
        return fContour->segments()[fIndex].pts();
    }

    SkScalar right() const {
        return bounds().fRight;
    }

    SegmentType segmentType() const {
        const SkOpSegment& segment = fContour->segments()[fIndex];
        SegmentType type = (SegmentType) segment.verb();
        if (type != kLine_Segment) {
            return type;
        }
        if (segment.isHorizontal()) {
            return kHorizontalLine_Segment;
        }
        if (segment.isVertical()) {
            return kVerticalLine_Segment;
        }
        return kLine_Segment;
    }

    bool startAfter(const SkIntersectionHelper& after) {
        fIndex = after.fIndex;
        return advance();
    }

    SkScalar top() const {
        return bounds().fTop;
    }

    SkPath::Verb verb() const {
        return fContour->segments()[fIndex].verb();
    }

    SkScalar x() const {
        return bounds().fLeft;
    }

    bool xFlipped() const {
        return x() != pts()[0].fX;
    }

    SkScalar y() const {
        return bounds().fTop;
    }

    bool yFlipped() const {
        return y() != pts()[0].fY;
    }

#ifdef SK_DEBUG
    void dump() {
        SkDPoint::dump(pts()[0]);
        SkDPoint::dump(pts()[1]);
        if (verb() >= SkPath::kQuad_Verb) {
            SkDPoint::dump(pts()[2]);
        }
        if (verb() >= SkPath::kCubic_Verb) {
            SkDPoint::dump(pts()[3]);
        }
    }
#endif

private:
    SkOpContour* fContour;
    int fIndex;
    int fLast;
};
