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

#ifndef Contour_DEFINED
#define Contour_DEFINED

#include "include/core/SkRect.h"
#include "include/core/SkSpan.h"
#include "include/private/base/SkAssert.h"

#include <limits.h>
#include <cstddef>
#include <cstdint>
#include <iterator>
#include <vector>

class SkPath;
namespace myers { class Segment; }
struct SkPoint;

namespace contour {
struct Point {
    int32_t x;
    int32_t y;
};

class Contour {
public:
    SkSpan<const Point> points;
    SkIRect bounds;
};

class Contours {
    class Iterator {
    public:
        using value_type = Contour;
        using difference_type = ptrdiff_t;
        using pointer = value_type*;
        using reference = value_type;
        using iterator_category = std::input_iterator_tag;
        Iterator(const Contours& contours, size_t index)
                : fContours{contours}
                , fIndex{index} { }
        Iterator(const Iterator& that) : Iterator{ that.fContours, that.fIndex } { }
        Iterator& operator++() { ++fIndex; return *this; }
        Iterator operator++(int) { Iterator tmp(*this); operator++(); return tmp; }
        bool operator==(const Iterator& rhs) const { return fIndex == rhs.fIndex; }
        bool operator!=(const Iterator& rhs) const { return fIndex != rhs.fIndex; }
        value_type operator*() { return fContours[fIndex]; }
        friend difference_type operator-(Iterator lhs, Iterator rhs) {
            return lhs.fIndex - rhs.fIndex;
        }

    private:
        const Contours& fContours;
        size_t fIndex = 0;
    };
public:
    static constexpr double kScaleFactor = 1024;
    static Contours Make(SkPath path);

    Contour operator[](size_t i) const {
        SkASSERT(i < fContours.size());
        auto& [bounds, end] = fContours[i];
        int32_t start = i == 0 ? 0 : fContours[i-1].end;
        SkSpan<const Point> points{&fPoints[start], SkToSizeT(end - start)};
        return {points, bounds};
    }

    Iterator begin() const {
        return Iterator{*this, 0};
    }

    Iterator end() const {
        return Iterator{*this, fContours.size()};
    }

    size_t size() const {
        return fContours.size();
    }

    bool empty() const {
        return fContours.empty();
    }

    std::vector<myers::Segment> segments() const;

private:
    static constexpr SkIRect kEmptyRect = SkIRect::MakeLTRB(INT_MAX, INT_MAX, INT_MIN, INT_MIN);
    struct CompactContour {
        SkIRect bounds;
        int32_t end;
    };

    static Point RoundSkPoint(SkPoint p);
    bool currentContourIsEmpty() const;
    void addPointToCurrentContour(SkPoint p);
    void moveToStartOfContour(SkPoint p);
    void closeContourIfNeeded();

    Point fContourStart;
    SkIRect fContourBounds = kEmptyRect;

    std::vector<Point> fPoints;
    std::vector<CompactContour> fContours;
};
}  // namespace contour

#endif  // Contour_DEFINED
