#pragma once

#if defined( __EMSCRIPTEN__ ) || defined( __ANDROID__ )
#include <GLES3/gl3.h>
#else
#include <glad/glad.h>
#endif

#include "crArray.h"
#include "crStruct.h"

// camera-facing ribbon trail — a path of points extruded into a triangle strip, drawn additive in
// the transparent phase (depth read, no write). RIBBON_TRAIL program (pos + color + uv, VP from UBO)
// samples a dedicated GL_REPEAT texture — u tiles along the trail, v spans its width
class crBatchedRibbonTrails
{
private:
    // interleaved vertex stream — must match ribbon_trail.vert locations 0..2
    struct RibbonVertex
    {
        float3 pos;
        color4 color;
        float2 uv;
    };

    static constexpr int32_t  INITIAL_RESERVE = 2048;
    static constexpr uint32_t RESTART_INDEX   = 0xFFFFFFFFu;   // GL_UNSIGNED_INT fixed restart — separates trails into independent strips

    crArray<RibbonVertex> _verts;
    crArray<uint32_t>     _indices;

    GLuint _vao = 0;
    GLuint _vbo = 0;
    GLuint _ibo = 0;

public:
    // one trail's build input; the caller owns the point storage (a ring buffer) for the frame
    struct TrailInput
    {
        const float3* points;
        int32_t       start;        // ring origin — logical point i is points[ ( start + i ) % ringLength ]
        int32_t       ringLength;
        int32_t       count;
        color4        headColor;    // pre-attenuated by the gatherer (intensity, detach fade)
        color4        tailColor;    // lerped to headColor along the trail
        float         headHalfWidth;
        float         tailHalfWidth;
        float         tileLength;
        uint32_t      textureId;    // base texture — trails are sorted by this so each texture draws as one run
    };

    // one draw's worth of the shared index buffer — trails sorted by texture form contiguous runs
    struct Run
    {
        GLuint  texture;
        int32_t indexOffset;
        int32_t indexCount;
    };

    void Init();
    void Cleanup();

    void Prepare( int32_t totalVerts, int32_t totalIndices );   // size the output buffers so BuildRange can fill disjoint regions (in parallel)
    // build trails [first, lastExclusive) into the pre-sized buffers at their prefix-summed offsets — thread-safe across trails
    void BuildRange( const TrailInput* trails, const int32_t* vertBase, const int32_t* indexBase, int32_t first, int32_t lastExclusive, float3 eye, float fogStart, float fogEnd );
    void Flush( GLuint program, GLuint noiseTexture, const Run* runs, int32_t runCount );   // one upload, one indexed draw per texture run; additive, depth read-only
};
