/*
 * Copyright 2024 Rive
 */

#pragma once

#include "rive/math/simd.hpp"
#include <vector>

namespace rive::gpu
{
// 255 x 255 tile that manages a set of rectangles and their groupIndex.
// From a given rectangle, finds the max groupIndex in the set of internal
// rectangles it intersects. The size is 255 so we can store bounding box
// coordinates in 8 bits.
class IntersectionTile
{
public:
    void reset(int left, int top, int16_t baselineGroupIndex = 0);

    void addRectangle(int4 ltrb, int16_t groupIndex);

    // Accumulate local maximum intersecting group indices for the given
    // rectangle in each channel of a int16x8. "runningMaxGroupIndices" is a
    // running set of local maximums if the IntersectionBoard also ran this same
    // test on other tile(s) that the rectangle touched. The absolute maximum
    // group index that this rectangle intersects with will be
    // simd::reduce_max(returnValue).
    int16x8 findMaxIntersectingGroupIndex(int4 ltrb,
                                          int16x8 runningMaxGroupIndices) const;

private:
    int4 m_topLeft;
    int16_t m_baselineGroupIndex;
    int16_t m_maxGroupIndex;
    size_t m_rectangleCount = 0;

    // How many rectangles/groupIndices are in each chunk of data?
    constexpr static size_t kChunkSize = 8;

    // Chunk of 8 rectangles encoded as [L, T, 255 - R, 255 - B], relative to
    // m_left and m_top. The data is also transposed: [L0..L7, T0..T7, -R0..R7,
    // -B0..B7].
    std::vector<int8x32> m_edges;
    static_assert(sizeof(m_edges[0]) == kChunkSize * 4);

    // Chunk of 8 groupIndices corresponding to the above edges.
    std::vector<int16x8> m_groupIndices;
    static_assert(sizeof(m_groupIndices[0]) == kChunkSize * 2);
};

// Manages a set of rectangles and their groupIndex across a variable-sized
// viewport. Each time a rectangle is added, assigns and returns a groupIndex
// that is one larger than the max groupIndex in the set of existing rectangles
// it intersects.
class IntersectionBoard
{
public:
    void resizeAndReset(uint32_t viewportWidth, uint32_t viewportHeight);

    // Adds a rectangle to the internal set and assigns it "layerCount"
    // contiguous group indices, beginning one larger than the max groupIndex in
    // the set of existing rectangles it intersects.
    //
    // Returns the first newly assigned groupIndex for the added rectangle.
    // If it does not intersect with any other rectangles, this groupIndex is 1.
    //
    // It is the caller's responsibility to not insert more rectangles than can
    // fit in a signed 16-bit integer. (The result is signed because SSE doesn't
    // have an unsigned max instruction.)
    int16_t addRectangle(int4 ltrb, int16_t layerCount = 1);

private:
    int2 m_viewportSize;
    int32_t m_cols;
    int32_t m_rows;
    std::vector<IntersectionTile> m_tiles;
};
} // namespace rive::gpu
