/*
 * Copyright 2013 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "SkPathOpsDebug.h"
#include "SkPath.h"

#if defined SK_DEBUG || !FORCE_RELEASE

const char* SkPathOpsDebug::kLVerbStr[] = {"", "line", "quad", "cubic"};

#if defined(SK_DEBUG) || !FORCE_RELEASE
int SkPathOpsDebug::gContourID = 0;
int SkPathOpsDebug::gSegmentID = 0;
#endif

#if DEBUG_SORT || DEBUG_SWAP_TOP
int SkPathOpsDebug::gSortCountDefault = SK_MaxS32;
int SkPathOpsDebug::gSortCount;
#endif

#if DEBUG_ACTIVE_OP
const char* SkPathOpsDebug::kPathOpStr[] = {"diff", "sect", "union", "xor"};
#endif

bool SkPathOpsDebug::ChaseContains(const SkTDArray<SkOpSpan *>& chaseArray,
        const SkOpSpan* span) {
    for (int index = 0; index < chaseArray.count(); ++index) {
        const SkOpSpan* entry = chaseArray[index];
        if (entry == span) {
            return true;
        }
    }
    return false;
}

void SkPathOpsDebug::MathematicaIze(char* str, size_t bufferLen) {
    size_t len = strlen(str);
    bool num = false;
    for (size_t idx = 0; idx < len; ++idx) {
        if (num && str[idx] == 'e') {
            if (len + 2 >= bufferLen) {
                return;
            }
            memmove(&str[idx + 2], &str[idx + 1], len - idx);
            str[idx] = '*';
            str[idx + 1] = '^';
            ++len;
        }
        num = str[idx] >= '0' && str[idx] <= '9';
    }
}

bool SkPathOpsDebug::ValidWind(int wind) {
    return wind > SK_MinS32 + 0xFFFF && wind < SK_MaxS32 - 0xFFFF;
}

void SkPathOpsDebug::WindingPrintf(int wind) {
    if (wind == SK_MinS32) {
        SkDebugf("?");
    } else {
        SkDebugf("%d", wind);
    }
}

#if DEBUG_SHOW_TEST_NAME
void* SkPathOpsDebug::CreateNameStr() {
    return SkNEW_ARRAY(char, DEBUG_FILENAME_STRING_LENGTH);
}

void SkPathOpsDebug::DeleteNameStr(void* v) {
    SkDELETE_ARRAY(reinterpret_cast<char* >(v));
}

void SkPathOpsDebug::BumpTestName(char* test) {
    char* num = test + strlen(test);
    while (num[-1] >= '0' && num[-1] <= '9') {
        --num;
    }
    if (num[0] == '\0') {
        return;
    }
    int dec = atoi(num);
    if (dec == 0) {
        return;
    }
    ++dec;
    SK_SNPRINTF(num, DEBUG_FILENAME_STRING_LENGTH - (num - test), "%d", dec);
}
#endif

#if !DEBUG_SHOW_TEST_NAME  // enable when building without extended test
void SkPathOpsDebug::ShowPath(const SkPath& one, const SkPath& two, SkPathOp op, const char* name) {
}
#endif

#endif //  defined SK_DEBUG || !FORCE_RELEASE

#include "SkOpAngle.h"
#include "SkOpSegment.h"

#if DEBUG_SORT
void SkOpAngle::debugLoop() const {
    const SkOpAngle* first = this;
    const SkOpAngle* next = this;
    do {
        next->dumpOne(true);
        next = next->fNext;
    } while (next && next != first);
}
#endif

#if DEBUG_ANGLE
void SkOpAngle::debugSameAs(const SkOpAngle* compare) const {
    SK_ALWAYSBREAK(fSegment == compare->fSegment);
    const SkOpSpan& startSpan = fSegment->span(fStart);
    const SkOpSpan& oStartSpan = fSegment->span(compare->fStart);
    SK_ALWAYSBREAK(startSpan.fToAngle == oStartSpan.fToAngle);
    SK_ALWAYSBREAK(startSpan.fFromAngle == oStartSpan.fFromAngle);
    const SkOpSpan& endSpan = fSegment->span(fEnd);
    const SkOpSpan& oEndSpan = fSegment->span(compare->fEnd);
    SK_ALWAYSBREAK(endSpan.fToAngle == oEndSpan.fToAngle);
    SK_ALWAYSBREAK(endSpan.fFromAngle == oEndSpan.fFromAngle);
}
#endif

#if DEBUG_VALIDATE
void SkOpAngle::debugValidateNext() const {
    const SkOpAngle* first = this;
    const SkOpAngle* next = first;
    SkTDArray<const SkOpAngle*>(angles);
    do {
//        SK_ALWAYSBREAK(next->fSegment->debugContains(next));
        angles.push(next);
        next = next->next();
        if (next == first) {
            break;
        }
        SK_ALWAYSBREAK(!angles.contains(next));
        if (!next) {
            return;
        }
    } while (true);
}

void SkOpAngle::debugValidateLoop() const {
    const SkOpAngle* first = this;
    const SkOpAngle* next = first;
    SK_ALWAYSBREAK(first->next() != first);
    int signSum = 0;
    int oppSum = 0;
    bool firstOperand = fSegment->operand();
    bool unorderable = false;
    do {
        unorderable |= next->fUnorderable;
        const SkOpSegment* segment = next->fSegment;
        bool operandsMatch = firstOperand == segment->operand();
        signSum += operandsMatch ? segment->spanSign(next) : segment->oppSign(next);
        oppSum += operandsMatch ? segment->oppSign(next) : segment->spanSign(next);
        const SkOpSpan& span = segment->span(SkMin32(next->fStart, next->fEnd));
        if (segment->_xor()) {
//            SK_ALWAYSBREAK(span.fWindValue == 1);
//            SK_ALWAYSBREAK(span.fWindSum == SK_MinS32 || span.fWindSum == 1);
        }
        if (segment->oppXor()) {
            SK_ALWAYSBREAK(span.fOppValue == 0 || abs(span.fOppValue) == 1);
//            SK_ALWAYSBREAK(span.fOppSum == SK_MinS32 || span.fOppSum == 0 || abs(span.fOppSum) == 1);
        }
        next = next->next();
        if (!next) {
            return;
        }
    } while (next != first);
    if (unorderable) {
        return;
    }
    SK_ALWAYSBREAK(!signSum || fSegment->_xor());
    SK_ALWAYSBREAK(!oppSum || fSegment->oppXor());
    int lastWinding;
    int lastOppWinding;
    int winding;
    int oppWinding;
    do {
        const SkOpSegment* segment = next->fSegment;
        const SkOpSpan& span = segment->span(SkMin32(next->fStart, next->fEnd));
        winding = span.fWindSum;
        if (winding != SK_MinS32) {
//            SK_ALWAYSBREAK(winding != 0);
            SK_ALWAYSBREAK(SkPathOpsDebug::ValidWind(winding));
            lastWinding = winding;
            int diffWinding = segment->spanSign(next);
            if (!segment->_xor()) {
                SK_ALWAYSBREAK(diffWinding != 0);
                bool sameSign = (winding > 0) == (diffWinding > 0);
                winding -= sameSign ? diffWinding : -diffWinding;
                SK_ALWAYSBREAK(SkPathOpsDebug::ValidWind(winding));
                SK_ALWAYSBREAK(abs(winding) <= abs(lastWinding));
                if (!sameSign) {
                    SkTSwap(winding, lastWinding);
                }
            }
            lastOppWinding = oppWinding = span.fOppSum;
            if (oppWinding != SK_MinS32 && !segment->oppXor()) {
                int oppDiffWinding = segment->oppSign(next);
//                SK_ALWAYSBREAK(abs(oppDiffWinding) <= abs(diffWinding) || segment->_xor());
                if (oppDiffWinding) {
                    bool oppSameSign = (oppWinding > 0) == (oppDiffWinding > 0);
                    oppWinding -= oppSameSign ? oppDiffWinding : -oppDiffWinding;
                    SK_ALWAYSBREAK(SkPathOpsDebug::ValidWind(oppWinding));
                    SK_ALWAYSBREAK(abs(oppWinding) <= abs(lastOppWinding));
                    if (!oppSameSign) {
                        SkTSwap(oppWinding, lastOppWinding);
                    }
                }
            }
            firstOperand = segment->operand();
            break;
        }
        SK_ALWAYSBREAK(span.fOppSum == SK_MinS32);
        next = next->next();
    } while (next != first);
    if (winding == SK_MinS32) {
        return;
    }
    SK_ALWAYSBREAK(oppWinding == SK_MinS32 || SkPathOpsDebug::ValidWind(oppWinding));
    first = next;
    next = next->next();
    do {
        const SkOpSegment* segment = next->fSegment;
        lastWinding = winding;
        lastOppWinding = oppWinding;
        bool operandsMatch = firstOperand == segment->operand();
        if (operandsMatch) {
            if (!segment->_xor()) {
                winding -= segment->spanSign(next);
                SK_ALWAYSBREAK(winding != lastWinding);
                SK_ALWAYSBREAK(SkPathOpsDebug::ValidWind(winding));
            }
            if (!segment->oppXor()) {
                int oppDiffWinding = segment->oppSign(next);
                if (oppWinding != SK_MinS32) {
                    oppWinding -= oppDiffWinding;
                    SK_ALWAYSBREAK(SkPathOpsDebug::ValidWind(oppWinding));
                } else {
                    SK_ALWAYSBREAK(oppDiffWinding == 0);
                }
            }
        } else {
            if (!segment->oppXor()) {
                winding -= segment->oppSign(next);
                SK_ALWAYSBREAK(SkPathOpsDebug::ValidWind(winding));
            }
            if (!segment->_xor()) {
                oppWinding -= segment->spanSign(next);
                SK_ALWAYSBREAK(oppWinding != lastOppWinding);
                SK_ALWAYSBREAK(SkPathOpsDebug::ValidWind(oppWinding));
            }
        }
        bool useInner = SkOpSegment::UseInnerWinding(lastWinding, winding);
        int sumWinding = useInner ? winding : lastWinding;
        bool oppUseInner = SkOpSegment::UseInnerWinding(lastOppWinding, oppWinding);
        int oppSumWinding = oppUseInner ? oppWinding : lastOppWinding;
        if (!operandsMatch) {
            SkTSwap(useInner, oppUseInner);
            SkTSwap(sumWinding, oppSumWinding);
        }
        const SkOpSpan& span = segment->span(SkMin32(next->fStart, next->fEnd));
        if (winding == -lastWinding) {
            if (span.fWindSum != SK_MinS32) {
                SkDebugf("%s useInner=%d spanSign=%d lastWinding=%d winding=%d windSum=%d\n",
                        __FUNCTION__,
                        useInner, segment->spanSign(next), lastWinding, winding, span.fWindSum);
            }
        }
        if (oppWinding != SK_MinS32) {
            if (span.fOppSum != SK_MinS32) {
                SK_ALWAYSBREAK(span.fOppSum == oppSumWinding || segment->oppXor() || segment->_xor());
            }
        } else {
            SK_ALWAYSBREAK(!firstOperand);
            SK_ALWAYSBREAK(!segment->operand());
            SK_ALWAYSBREAK(!span.fOppValue);
        }
        next = next->next();
    } while (next != first);
}
#endif

#if DEBUG_SWAP_TOP
bool SkOpSegment::controlsContainedByEnds(int tStart, int tEnd) const {
    if (fVerb != SkPath::kCubic_Verb) {
        return false;
    }
    SkDCubic dst = SkDCubic::SubDivide(fPts, fTs[tStart].fT, fTs[tEnd].fT);
    return dst.controlsContainedByEnds();
}
#endif

#if DEBUG_CONCIDENT
// SK_ALWAYSBREAK if pair has not already been added
void SkOpSegment::debugAddTPair(double t, const SkOpSegment& other, double otherT) const {
    for (int i = 0; i < fTs.count(); ++i) {
        if (fTs[i].fT == t && fTs[i].fOther == &other && fTs[i].fOtherT == otherT) {
            return;
        }
    }
    SK_ALWAYSBREAK(0);
}
#endif

#if DEBUG_ANGLE
void SkOpSegment::debugCheckPointsEqualish(int tStart, int tEnd) const {
    const SkPoint& basePt = fTs[tStart].fPt;
    while (++tStart < tEnd) {
       const SkPoint& cmpPt = fTs[tStart].fPt;
       SK_ALWAYSBREAK(SkDPoint::ApproximatelyEqual(basePt, cmpPt));
    }
}
#endif

#if DEBUG_SWAP_TOP
int SkOpSegment::debugInflections(int tStart, int tEnd) const {
    if (fVerb != SkPath::kCubic_Verb) {
        return false;
    }
    SkDCubic dst = SkDCubic::SubDivide(fPts, fTs[tStart].fT, fTs[tEnd].fT);
    double inflections[2];
    return dst.findInflections(inflections);
}
#endif

const SkOpAngle* SkOpSegment::debugLastAngle() const {
    const SkOpAngle* result = NULL;
    for (int index = 0; index < count(); ++index) {
        const SkOpSpan& span = this->span(index);
        if (span.fToAngle) {
            SkASSERT(!result);
            result = span.fToAngle;
        }
    }
    SkASSERT(result);
    return result;
}

void SkOpSegment::debugReset() {
    fTs.reset();
    fAngles.reset();
}

#if DEBUG_CONCIDENT
void SkOpSegment::debugShowTs(const char* prefix) const {
    SkDebugf("%s %s id=%d", __FUNCTION__, prefix, fID);
    int lastWind = -1;
    int lastOpp = -1;
    double lastT = -1;
    int i;
    for (i = 0; i < fTs.count(); ++i) {
        bool change = lastT != fTs[i].fT || lastWind != fTs[i].fWindValue
                || lastOpp != fTs[i].fOppValue;
        if (change && lastWind >= 0) {
            SkDebugf(" t=%1.3g %1.9g,%1.9g w=%d o=%d]",
                    lastT, xyAtT(i - 1).fX, xyAtT(i - 1).fY, lastWind, lastOpp);
        }
        if (change) {
            SkDebugf(" [o=%d", fTs[i].fOther->fID);
            lastWind = fTs[i].fWindValue;
            lastOpp = fTs[i].fOppValue;
            lastT = fTs[i].fT;
        } else {
            SkDebugf(",%d", fTs[i].fOther->fID);
        }
    }
    if (i <= 0) {
        return;
    }
    SkDebugf(" t=%1.3g %1.9g,%1.9g w=%d o=%d]",
            lastT, xyAtT(i - 1).fX, xyAtT(i - 1).fY, lastWind, lastOpp);
    if (fOperand) {
        SkDebugf(" operand");
    }
    if (done()) {
        SkDebugf(" done");
    }
    SkDebugf("\n");
}
#endif

#if DEBUG_ACTIVE_SPANS || DEBUG_ACTIVE_SPANS_FIRST_ONLY
void SkOpSegment::debugShowActiveSpans() const {
    debugValidate();
    if (done()) {
        return;
    }
#if DEBUG_ACTIVE_SPANS_SHORT_FORM
    int lastId = -1;
    double lastT = -1;
#endif
    for (int i = 0; i < fTs.count(); ++i) {
        if (fTs[i].fDone) {
            continue;
        }
        SK_ALWAYSBREAK(i < fTs.count() - 1);
#if DEBUG_ACTIVE_SPANS_SHORT_FORM
        if (lastId == fID && lastT == fTs[i].fT) {
            continue;
        }
        lastId = fID;
        lastT = fTs[i].fT;
#endif
        SkDebugf("%s id=%d", __FUNCTION__, fID);
        SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
        for (int vIndex = 1; vIndex <= SkPathOpsVerbToPoints(fVerb); ++vIndex) {
            SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
        }
        const SkOpSpan* span = &fTs[i];
        SkDebugf(") t=%1.9g (%1.9g,%1.9g)", span->fT, xAtT(span), yAtT(span));
        int iEnd = i + 1;
        while (fTs[iEnd].fT < 1 && approximately_equal(fTs[i].fT, fTs[iEnd].fT)) {
            ++iEnd;
        }
        SkDebugf(" tEnd=%1.9g", fTs[iEnd].fT);
        const SkOpSegment* other = fTs[i].fOther;
        SkDebugf(" other=%d otherT=%1.9g otherIndex=%d windSum=",
                other->fID, fTs[i].fOtherT, fTs[i].fOtherIndex);
        if (fTs[i].fWindSum == SK_MinS32) {
            SkDebugf("?");
        } else {
            SkDebugf("%d", fTs[i].fWindSum);
        }
        SkDebugf(" windValue=%d oppValue=%d\n", fTs[i].fWindValue, fTs[i].fOppValue);
    }
}
#endif

#if DEBUG_MARK_DONE || DEBUG_UNSORTABLE
void SkOpSegment::debugShowNewWinding(const char* fun, const SkOpSpan& span, int winding) {
    const SkPoint& pt = xyAtT(&span);
    SkDebugf("%s id=%d", fun, fID);
    SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
    for (int vIndex = 1; vIndex <= SkPathOpsVerbToPoints(fVerb); ++vIndex) {
        SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
    }
    SK_ALWAYSBREAK(&span == &span.fOther->fTs[span.fOtherIndex].fOther->
            fTs[span.fOther->fTs[span.fOtherIndex].fOtherIndex]);
    SkDebugf(") t=%1.9g [%d] (%1.9g,%1.9g) tEnd=%1.9g newWindSum=%d windSum=",
            span.fT, span.fOther->fTs[span.fOtherIndex].fOtherIndex, pt.fX, pt.fY,
            (&span)[1].fT, winding);
    if (span.fWindSum == SK_MinS32) {
        SkDebugf("?");
    } else {
        SkDebugf("%d", span.fWindSum);
    }
    SkDebugf(" windValue=%d\n", span.fWindValue);
}

void SkOpSegment::debugShowNewWinding(const char* fun, const SkOpSpan& span, int winding,
                                      int oppWinding) {
    const SkPoint& pt = xyAtT(&span);
    SkDebugf("%s id=%d", fun, fID);
    SkDebugf(" (%1.9g,%1.9g", fPts[0].fX, fPts[0].fY);
    for (int vIndex = 1; vIndex <= SkPathOpsVerbToPoints(fVerb); ++vIndex) {
        SkDebugf(" %1.9g,%1.9g", fPts[vIndex].fX, fPts[vIndex].fY);
    }
    SK_ALWAYSBREAK(&span == &span.fOther->fTs[span.fOtherIndex].fOther->
            fTs[span.fOther->fTs[span.fOtherIndex].fOtherIndex]);
    SkDebugf(") t=%1.9g [%d] (%1.9g,%1.9g) tEnd=%1.9g newWindSum=%d newOppSum=%d oppSum=",
            span.fT, span.fOther->fTs[span.fOtherIndex].fOtherIndex, pt.fX, pt.fY,
            (&span)[1].fT, winding, oppWinding);
    if (span.fOppSum == SK_MinS32) {
        SkDebugf("?");
    } else {
        SkDebugf("%d", span.fOppSum);
    }
    SkDebugf(" windSum=");
    if (span.fWindSum == SK_MinS32) {
        SkDebugf("?");
    } else {
        SkDebugf("%d", span.fWindSum);
    }
    SkDebugf(" windValue=%d oppValue=%d\n", span.fWindValue, span.fOppValue);
}
#endif

#if DEBUG_SHOW_WINDING
int SkOpSegment::debugShowWindingValues(int slotCount, int ofInterest) const {
    if (!(1 << fID & ofInterest)) {
        return 0;
    }
    int sum = 0;
    SkTArray<char, true> slots(slotCount * 2);
    memset(slots.begin(), ' ', slotCount * 2);
    for (int i = 0; i < fTs.count(); ++i) {
   //     if (!(1 << fTs[i].fOther->fID & ofInterest)) {
   //         continue;
   //     }
        sum += fTs[i].fWindValue;
        slots[fTs[i].fOther->fID - 1] = as_digit(fTs[i].fWindValue);
        sum += fTs[i].fOppValue;
        slots[slotCount + fTs[i].fOther->fID - 1] = as_digit(fTs[i].fOppValue);
    }
    SkDebugf("%s id=%2d %.*s | %.*s\n", __FUNCTION__, fID, slotCount, slots.begin(), slotCount,
            slots.begin() + slotCount);
    return sum;
}
#endif

void SkOpSegment::debugValidate() const {
#if DEBUG_VALIDATE
    int count = fTs.count();
    SK_ALWAYSBREAK(count >= 2);
    SK_ALWAYSBREAK(fTs[0].fT == 0);
    SK_ALWAYSBREAK(fTs[count - 1].fT == 1);
    int done = 0;
    double t = -1;
    const SkOpSpan* last = NULL;
    bool tinyTFound = false;
    bool hasLoop = false;
    for (int i = 0; i < count; ++i) {
        const SkOpSpan& span = fTs[i];
        SK_ALWAYSBREAK(t <= span.fT);
        t = span.fT;
        int otherIndex = span.fOtherIndex;
        const SkOpSegment* other = span.fOther;
        SK_ALWAYSBREAK(other != this || fVerb == SkPath::kCubic_Verb);
        const SkOpSpan& otherSpan = other->fTs[otherIndex];
        SK_ALWAYSBREAK(otherSpan.fPt == span.fPt);
        SK_ALWAYSBREAK(otherSpan.fOtherT == t);
        SK_ALWAYSBREAK(&fTs[i] == &otherSpan.fOther->fTs[otherSpan.fOtherIndex]);
        done += span.fDone;
        if (last) {
            SK_ALWAYSBREAK(last->fT != span.fT || last->fOther != span.fOther);
            bool tsEqual = last->fT == span.fT;
            bool tsPreciselyEqual = precisely_equal(last->fT, span.fT);
            SK_ALWAYSBREAK(!tsEqual || tsPreciselyEqual);
            bool pointsEqual = last->fPt == span.fPt;
            bool pointsNearlyEqual = AlmostEqualUlps(last->fPt, span.fPt);
#if 0  // bufferOverflow test triggers this
            SK_ALWAYSBREAK(!tsPreciselyEqual || pointsNearlyEqual);
#endif
//            SK_ALWAYSBREAK(!last->fTiny || !tsPreciselyEqual || span.fTiny || tinyTFound);
            SK_ALWAYSBREAK(last->fTiny || tsPreciselyEqual || !pointsEqual || hasLoop);
            SK_ALWAYSBREAK(!last->fTiny || pointsEqual);
            SK_ALWAYSBREAK(!last->fTiny || last->fDone);
            SK_ALWAYSBREAK(!last->fSmall || pointsNearlyEqual);
            SK_ALWAYSBREAK(!last->fSmall || last->fDone);
//            SK_ALWAYSBREAK(!last->fSmall || last->fTiny);
//            SK_ALWAYSBREAK(last->fTiny || !pointsEqual || last->fDone == span.fDone);
            if (last->fTiny) {
                tinyTFound |= !tsPreciselyEqual;
            } else {
                tinyTFound = false;
            }
        }
        last = &span;
        hasLoop |= last->fLoop;
    }
    SK_ALWAYSBREAK(done == fDoneSpans);
//    if (fAngles.count() ) {
//        fAngles.begin()->debugValidateLoop();
//    }
#endif
}