#include "crDevText.h"

#include "crApp.h"
#include "crBatchedLines3D.h"
#include "crCamera.h"
#include "crGraphics.h"
#include "crMath.h"

static const color4 GLYPH_COLOR  = color4( 0.2f, 1.0f, 0.2f, 0.5f );   // per-glyph wire box
static const color4 GROUP_COLOR  = color4( 1.0f, 1.0f, 0.3f, 0.9f );   // per-group wire box
static const color4 ANCHOR_COLOR = color4( 1.0f, 0.2f, 0.2f, 1.0f );   // anchor / pivot circle

static constexpr float   ANCHOR_RADIUS   = 3.0f;
static constexpr int32_t ANCHOR_SEGMENTS = 8;

void crDevText::Init( crApp* app )
{
    _app       = app;
    _lineBatch = new crBatchedLines3D;
    _lineBatch->Init();
}
void crDevText::Cleanup()
{
    if( _lineBatch != nullptr )
    {
        _lineBatch->Cleanup();
        delete _lineBatch;
        _lineBatch = nullptr;
    }

    _app = nullptr;
}

void crDevText::BeginGroup( float2 anchorPx )
{
    if( _debugDrawEnabled == false )
        return;

    if( _frameStarted == false )   // first text of the frame — start the batch
    {
        _lineBatch->Begin();
        _frameStarted = true;
    }

    _anchor   = anchorPx;
    _hasGlyph = false;
}
void crDevText::AddGlyphRect( float2 centerPx, float2 sizePx )
{
    if( _debugDrawEnabled == false )
        return;

    const float hx   = ( sizePx.x * 0.5f );
    const float hy   = ( sizePx.y * 0.5f );
    const float minx = ( centerPx.x - hx );
    const float maxx = ( centerPx.x + hx );
    const float miny = ( centerPx.y - hy );
    const float maxy = ( centerPx.y + hy );

    AddRect( float2( minx, miny ), float2( maxx, maxy ), GLYPH_COLOR );

    if( _hasGlyph == false )
    {
        _groupMin = float2( minx, miny );
        _groupMax = float2( maxx, maxy );
        _hasGlyph = true;
    }
    else
    {
        if( minx < _groupMin.x )
            _groupMin.x = minx;
        if( miny < _groupMin.y )
            _groupMin.y = miny;
        if( maxx > _groupMax.x )
            _groupMax.x = maxx;
        if( maxy > _groupMax.y )
            _groupMax.y = maxy;
    }
}
void crDevText::EndGroup()
{
    if( _debugDrawEnabled == false )
        return;

    if( _hasGlyph )
        AddRect( _groupMin, _groupMax, GROUP_COLOR );

    // anchor as a wire circle (a line batch has no solid fill)
    const float step = ( 2.0f * crMath::PI ) / static_cast<float>( ANCHOR_SEGMENTS );

    float2 prev = float2( _anchor.x + ANCHOR_RADIUS, _anchor.y );
    for( int32_t i = 1; i <= ANCHOR_SEGMENTS; ++i )
    {
        const float2 cs  = crMath::CosSin( step * static_cast<float>( i ) );
        const float2 cur = float2( _anchor.x + ( cs.x * ANCHOR_RADIUS ), _anchor.y + ( cs.y * ANCHOR_RADIUS ) );

        _lineBatch->Line( float3( prev.x, prev.y, 0.0f ), float3( cur.x, cur.y, 0.0f ), ANCHOR_COLOR );
        prev = cur;
    }
}

void crDevText::TryRenderDebugDraw()
{
    if( _frameStarted == false )
        return;

    _frameStarted = false;   // reset even when disabled mid-frame — no stale lines on re-enable

    if( _debugDrawEnabled == false )
        return;

    // pixel-space overlay: u_projection is a plain uniform, so set it here
    // (the GraphicsUniforms UBO carries the 3D camera only)
    const GLuint program = _app->graphics->Program( EShader::VERTEX_COLOR );
    glUseProgram( program );

    const float4x4 proj = _app->camera->PixelProjection();
    glUniformMatrix4fv( glGetUniformLocation( program, "u_projection" ), 1, GL_FALSE, proj.m );

    _lineBatch->Flush( program, false );
}

bool crDevText::GetDebugDraw() const
{
    return _debugDrawEnabled;
}
void crDevText::SetDebugDraw( bool enable )
{
    _debugDrawEnabled = enable;

    if( enable == false )
        _lineBatch->ClearUnusedMemory();
}

void crDevText::AddRect( float2 rectMin, float2 rectMax, color4 color )
{
    const float3 a = float3( rectMin.x, rectMin.y, 0.0f );
    const float3 b = float3( rectMax.x, rectMin.y, 0.0f );
    const float3 c = float3( rectMax.x, rectMax.y, 0.0f );
    const float3 d = float3( rectMin.x, rectMax.y, 0.0f );

    _lineBatch->Line( a, b, color );
    _lineBatch->Line( b, c, color );
    _lineBatch->Line( c, d, color );
    _lineBatch->Line( d, a, color );
}
