// Copyright 2021 Google LLC.
#ifndef Interface_DEFINED
#define Interface_DEFINED
#include <string>
#include "experimental/sktext/include/Types.h"
#include "experimental/sktext/src/Line.h"
#include "include/core/SkCanvas.h"
#include "include/core/SkFontMgr.h"
#include "include/core/SkFontStyle.h"
#include "include/core/SkPaint.h"
#include "include/core/SkSize.h"
#include "include/core/SkString.h"
#include "include/core/SkTextBlob.h"
#include "modules/skshaper/include/SkShaper.h"
#include "modules/skunicode/include/SkUnicode.h"

using namespace skia::text;
namespace skia {
namespace API {
/**
 * This class contains all the SKUnicode/ICU information.
 */
class UnicodeText {
public:
    /** Makes calls to SkShaper and collects all the shaped data.
        @param blocks         a range of FontBlock elements that keep information about
                              fonts required to shape the text.
                              It's utf16 range but internally it will have to be converted
                              to utf8 (since all shaping operations use utf8 encoding)
        @param textDirection  a starting text direction value
        @return               an object that contains the result of shaping operations
    */
    std::unique_ptr<ShapedText> shape(SkSpan<FontBlock> blocks, TextDirection textDirection);

    UnicodeText(std::unique_ptr<SkUnicode> unicode, SkSpan<uint16_t> utf16);
    UnicodeText(std::unique_ptr<SkUnicode> unicode, const SkString& utf8);

    bool hasProperty(TextIndex index, CodeUnitFlags flag) const
    bool isHardLineBreak(TextIndex index) const ;
    bool isSoftLineBreak(TextIndex index) const;
    bool isWhitespaces(TextRange range) const;

    SkUnicode* getUnicode() const;
    SkSpan<const char16_t> getText16() const;
};

class WrappedText;
/**
 * This class provides all the information from SkShaper/harfbuzz in a raw format.
 * It does require a single existing font for each codepoint.
 */
 // Question: do we provide a visitor for ShapedText?
class ShapedText {
public:
    /** Break text by lines with a given width (and possible new lines).
        @param unicodeText    a reference to UnicodeText that is used to query Unicode information
        @param width          a line width at which the text gets wrapped
        @param height         a text height, currently not supported
        @return               an object that contains the result of shaping operations (wrapping and formatting).
    */
    std::unique_ptr<WrappedText> wrap(UnicodeText* unicodeText, float width, float height);
    SkSpan<const LogicalRun> getLogicalRuns() const;
};

/**
 * This is a helper visitor class that allows a user to process the wrapped text
 * structures: lines and runs (to draw them, for instance)
 */
class Visitor {
public:
    virtual ~Visitor() = default;
    virtual void onBeginLine(size_t index, TextRange lineText, bool hardBreak, SkRect bounds) { }
    virtual void onEndLine(size_t index, TextRange lineText, GlyphRange trailingSpaces, size_t glyphCount) { }
    virtual void onGlyphRun(const SkFont& font,
                            TextRange textRange,        // Currently we make sure that the run edges are the grapheme cluster edges
                            SkRect bounds,              // bounds contains the physical boundaries of the run
                            int trailingSpaces,         // Depending of TextDirection it goes right to the end (LTR) or left to the start (RTL)
                            int glyphCount,             // Just the number of glyphs
                            const uint16_t glyphs[],
                            const SkPoint positions[],        // Positions relative to the line
                            const TextIndex clusters[])       // Text indices inside the entire text
    { }
    virtual void onPlaceholder(TextRange, const SkRect& bounds) { }
};

class DrawableText;
class SelectableText;
/**
 * This class provides all the information about wrapped/formatted text.
 */
class WrappedText {
public:
    /** Builds a list of SkTextBlobs to draw on a canvas.
        @param positionType   specifies a text adjustment granularity (grapheme cluster, grapheme, glypheme, glyph)
        @param blocks         a range of text indices that cause an additional run breaking to be used for styling
        @return               an object that contains a list of SkTextBlobs to draw on a canvas
    */
    std::unique_ptr<DrawableText> prepareToDraw(UnicodeText* unicodeText, PositionType positionType, SkSpan<TextIndex> blocks) const;
    /** Aggregates all the data to navigate the text (move up, down, left, right),
        select some text near the cursor point, adjust all text position to word,
        grapheme cluster and such.
        @return               an object that contains all the data for navigation
    */
    std::unique_ptr<SelectableText> prepareToEdit(UnicodeText* unicodeText) const;
    /** Formats a text line by line.
        @param textAlign     specifies a text placement on the line:
                             left, right, center and justified (last one currently not supported)
        @param textDirection specifies a text direction that also used in formatting
    */
    void format(TextAlign textAlign, TextDirection textDirection);
    /** Breaks the text runs into smaller runs by given list of chunks to be used for styling.
        @param unicodeText    a reference to UnicodeText object
        @param chunks         a range of text indices that cause an additional run breaking to be used for styling
    */
    void decorate(UnicodeText* unicodeText, SkSpan<TextIndex> chunks);

    /** Walks though the data structures and makes certain callbacks on visitor so the visitor can collect all the information.
        @param visitor      a reference to Visitor object
    */
    void visit(Visitor* visitor) const;
    /** Walks though the data structures and makes certain callbacks on visitor so the visitor can collect all the information.
        @param unicodeText  a reference to UnicodeText object
        @param visitor      a reference to Visitor object
        @param positionType specifies a text adjustment granularity (grapheme cluster, grapheme, glypheme, glyph)
                            to map text blocks to glyph ranges.
        @param blocks       a range of text indices that cause an additional run breaking to be used for styling
    */
    void visit(UnicodeText* unicodeText, Visitor* visitor, PositionType positionType, SkSpan<TextIndex> blocks) const;
};

/** This class contains all the data that allows easily paint the text on canvas.
    Strictly speaking, it is not an important part of SkText API but
    it presents a good example of SkText usages and simplifies testing.
*/
class DrawableText : public Visitor {
public:
    std::vector<sk_sp<SkTextBlob>>& getTextBlobs();
};

struct Position {
    Position(PositionType positionType, size_t lineIndex, GlyphRange glyphRange, TextRange textRange, SkRect rect);
    Position(PositionType positionType);
    PositionType fPositionType;
    size_t fLineIndex;
    GlyphRange fGlyphRange;
    TextRange fTextRange;
    SkRect fBoundaries;
};
struct BoxLine {
    BoxLine(size_t index, TextRange text, bool hardBreak, SkRect bounds);
    skia_private::TArray<SkRect, true> fBoxGlyphs;
    skia_private::TArray<TextIndex, true> fTextByGlyph; // by glyph cluster
    GlyphIndex fTextEnd;
    GlyphIndex fTrailingSpacesEnd;
    TextRange fTextRange;
    size_t fIndex;
    bool fIsHardBreak;
    SkRect fBounds;
};

/** This class contains all the data that allows all navigation operations on the text:
    move up/down/left/right, select some units of text and such.
*/
class SelectableText : public Visitor {
public:
    SelectableText() = default;

    /** Find the drawable unit (specified by positionType) closest to the screen point
        @param positionType   specifies a text adjustment granularity (grapheme cluster, grapheme, glypheme, glyph)
        @param point          a physical coordinates on a screen to find the closest glyph
        @return               a position object that contains all required information
    */
    Position adjustedPosition(PositionType positionType, SkPoint point) const;

    Position previousPosition(Position current) const;
    Position nextPosition(Position current) const;
    Position firstPosition(PositionType positionType) const;
    Position lastPosition(PositionType positionType) const;
    Position firstInLinePosition(PositionType positionType, LineIndex lineIndex) const;
    Position lastInLinePosition(PositionType positionType, LineIndex lineIndex) const;

    bool isFirstOnTheLine(Position element) const;
    bool isLastOnTheLine(Position element) const;

    size_t countLines() const;
    BoxLine getLine(size_t lineIndex) const;
};
}  // namespace text
}  // namespace skia

#endif  // Processor_DEFINED
