// Copyright 2021 Google LLC.
#ifndef LogicalRun_DEFINED
#define LogicalRun_DEFINED

#include "experimental/sktext/include/Types.h"
#include "experimental/sktext/src/Line.h"
#include "include/private/base/SkTArray.h"
#include "modules/skshaper/include/SkShaper.h"

namespace skia {
namespace text {

class LogicalRun {
    public:
    LogicalRun(const SkShaper::RunHandler::RunInfo& info, TextIndex textStart, SkScalar glyphOffset);
    SkShaper::RunHandler::Buffer newRunBuffer() {
        return {fGlyphs.data(), fPositions.data(), fOffsets.data(), fClusters.data(), {0.0f, 0.0f} };
    }
    void commit() {
        fFont.getBounds(fGlyphs.data(), fGlyphs.size(), fBounds.data(), nullptr);
        fPositions[fGlyphs.size()] = fAdvance;
        fClusters[fGlyphs.size()] = this->leftToRight() ? fUtf8Range.end() : fUtf8Range.begin();
    }

    TextRange getTextRange() const { return fUtf16Range; }

    SkScalar calculateWidth(GlyphRange glyphRange) const {
        SkASSERT(glyphRange.fStart <= glyphRange.fEnd &&
                 glyphRange.fEnd < SkToSizeT(fPositions.size()));
        return fPositions[glyphRange.fEnd].fX - fPositions[glyphRange.fStart].fX;
    }
    SkScalar calculateWidth(GlyphIndex start, GlyphIndex end) const {
      return calculateWidth(GlyphRange(start, end));
    }
    SkScalar width() const { return fAdvance.fX; }
    SkScalar firstGlyphPosition() const { return fPositions[0].fX; }

    bool leftToRight() const { return fBidiLevel % 2 == 0; }
    uint8_t bidiLevel() const { return fBidiLevel; }
    size_t size() const { return fGlyphs.size(); }

    LogicalRunType getRunType() const { return fRunType; }
    void setRunType(LogicalRunType runType) { fRunType = runType; }

    template <typename Callback>
    void forEachCluster(Callback&& callback) {
        for(int glyph = 0; glyph < fClusters.size(); ++glyph) {
            callback(glyph, fRunStart + fClusters[glyph]);
        }
    }

    template <typename Callback>
    void convertUtf16Range(Callback&& callback) {
        this->fUtf16Range.fStart = callback(this->fUtf8Range.begin());
        this->fUtf16Range.fEnd = callback(this->fUtf8Range.end());
    }

    // Convert indexes into utf16 and also shift them to be on the entire text scale
    template <typename Callback>
    void convertClusterIndexes(Callback&& callback) {
        for (int glyph = 0; glyph < fClusters.size(); ++glyph) {
            fClusters[glyph] = callback(fClusters[glyph]);
        }
    }

    private:
    friend class ShapedText;
    friend class WrappedText;
    SkFont fFont;
    TextMetrics fTextMetrics;

    LogicalRunType fRunType;
    SkVector fAdvance;
    SkShaper::RunHandler::Range fUtf8Range;
    TextRange fUtf16Range;
    TextIndex fRunStart;
    SkScalar  fRunOffset;
    SkSTArray<128, SkGlyphID, true> fGlyphs;
    SkSTArray<128, SkPoint, true> fPositions;
    SkSTArray<128, SkPoint, true> fOffsets;
    SkSTArray<128, uint32_t, true> fClusters;
    SkSTArray<128, SkRect, true> fBounds;

    uint8_t fBidiLevel;
};

} // namespace text
} // namespace skia
#endif
