#ifndef _RIVE_TEXT_CORE_HPP_
#define _RIVE_TEXT_CORE_HPP_
#include "rive/generated/text/text_base.hpp"
#include "rive/math/aabb.hpp"
#include "rive/text/text_value_run.hpp"
#include "rive/text_engine.hpp"
#include "rive/shapes/shape_paint_path.hpp"
#include "rive/simple_array.hpp"
#include "rive/text/glyph_lookup.hpp"
#include "rive/text/text_interface.hpp"

#include <unordered_map>
#include <vector>

namespace rive
{

class TextModifierGroup;

class StyledText
{
private:
    /// Represents the unicode characters making up the entire text string
    /// displayed. Only valid after update.
    std::vector<Unichar> m_value;
    std::vector<TextRun> m_runs;

public:
    bool empty() const;
    void clear();
    void append(rcp<Font> font,
                float size,
                float lineHeight,
                float letterSpacing,
                const std::string& text,
                uint16_t styleId);
    const std::vector<Unichar>& unichars() const { return m_value; }
    const std::vector<TextRun>& runs() const { return m_runs; }

    void swapRuns(std::vector<TextRun>& otherRuns) { m_runs.swap(otherRuns); }
};

struct TextBoundsInfo
{
    float minY;
    float maxWidth;
    float totalHeight;
    int ellipsisLine;
    bool isEllipsisLineLast;
};

enum class LineIter : uint8_t
{
    drawLine,
    skipThisLine,
    yOutOfBounds
};

class TextStylePaint;
class Text : public TextBase, public TextInterface
{
public:
    // Implements TextInterface
    void markShapeDirty() override;
    void markPaintDirty() override;

    void draw(Renderer* renderer) override;
    Core* hitTest(HitInfo*, const Mat2D&) override;
    void addRun(TextValueRun* run);
    void addModifierGroup(TextModifierGroup* group);
    void markShapeDirty(bool sendToLayout);
    void modifierShapeDirty();

    void update(ComponentDirt value) override;
    void onDirty(ComponentDirt value) override;
    Mat2D m_transform;
    Mat2D m_shapeWorldTransform;

    const Mat2D& shapeWorldTransform() const { return m_shapeWorldTransform; }
    TextSizing sizing() const { return (TextSizing)sizingValue(); }
    TextSizing effectiveSizing() const;
    TextOverflow overflow() const { return (TextOverflow)overflowValue(); }
    TextOrigin textOrigin() const { return (TextOrigin)originValue(); }
    TextWrap wrap() const { return (TextWrap)wrapValue(); }
    VerticalTextAlign verticalAlign() const
    {
        return (VerticalTextAlign)verticalAlignValue();
    }
    TextAlign align() const;
    void overflow(TextOverflow value) { return overflowValue((uint32_t)value); }
    void buildRenderStyles();
    const TextStylePaint* styleFromShaperId(uint16_t id) const;
    bool modifierRangesNeedShape() const;
    AABB localBounds() const override;
    AABB constraintBounds() const override { return localBounds(); }
    void originXChanged() override;
    void originYChanged() override;

    Vec2D measureLayout(float width,
                        LayoutMeasureMode widthMode,
                        float height,
                        LayoutMeasureMode heightMode) override;
    void controlSize(Vec2D size,
                     LayoutScaleType widthScaleType,
                     LayoutScaleType heightScaleType,
                     LayoutDirection direction) override;
    float effectiveWidth()
    {
        return std::isnan(m_layoutWidth) ? width() : m_layoutWidth;
    }
    float effectiveHeight()
    {
        return std::isnan(m_layoutHeight) ? height() : m_layoutHeight;
    }
    float computedWidth() override { return localBounds().width(); };
    float computedHeight() override { return localBounds().height(); };
#ifdef WITH_RIVE_TEXT
    const std::vector<TextValueRun*>& runs() const { return m_runs; }
    static SimpleArray<SimpleArray<GlyphLine>> BreakLines(
        const SimpleArray<Paragraph>& paragraphs,
        float width,
        TextAlign align,
        TextWrap wrap);
#endif

    bool haveModifiers() const
    {
#ifdef WITH_RIVE_TEXT
        return !m_modifierGroups.empty();
#else
        return false;
#endif
    }
#ifdef TESTING
    const std::vector<OrderedLine>& orderedLines() const
    {
        return m_orderedLines;
    }
    const std::vector<TextModifierGroup*>& modifierGroups() const
    {
        return m_modifierGroups;
    }
    const SimpleArray<Paragraph>& shape() const { return m_shape; }
    const std::vector<Unichar>& unichars() const
    {
        return m_styledText.unichars();
    }
#endif

protected:
    void alignValueChanged() override;
    void sizingValueChanged() override;
    void overflowValueChanged() override;
    void widthChanged() override;
    void heightChanged() override;
    void paragraphSpacingChanged() override;
    bool makeStyled(StyledText& styledText, bool withModifiers = true) const;
    void originValueChanged() override;

private:
#ifdef WITH_RIVE_TEXT
    void updateOriginWorldTransform();
    std::vector<TextValueRun*> m_runs;
    std::vector<TextStylePaint*> m_renderStyles;
    SimpleArray<Paragraph> m_shape;
    SimpleArray<Paragraph> m_modifierShape;
    SimpleArray<SimpleArray<GlyphLine>> m_lines;
    SimpleArray<SimpleArray<GlyphLine>> m_modifierLines;
    // Runs ordered by paragraph line.
    std::vector<OrderedLine> m_orderedLines;
    GlyphRun m_ellipsisRun;
    RawPath m_clipRect;
    ShapePaintPath m_clipPath;
    AABB m_bounds;
    std::vector<TextModifierGroup*> m_modifierGroups;

    StyledText m_styledText;
    StyledText m_modifierStyledText;
    GlyphLookup m_glyphLookup;

    void clearRenderStyles();
    TextBoundsInfo computeBoundsInfo();
    LineIter shouldDrawLine(float y, float totalHeight, const GlyphLine& line);

#endif
    float m_layoutWidth = NAN;
    float m_layoutHeight = NAN;
    uint8_t m_layoutWidthScaleType = std::numeric_limits<uint8_t>::max();
    uint8_t m_layoutHeightScaleType = std::numeric_limits<uint8_t>::max();
    LayoutDirection m_layoutDirection = LayoutDirection::inherit;
    Vec2D measure(Vec2D maxSize);
};
} // namespace rive

#endif
