#include "crDevProfiler.h"

#include <imgui.h>

#include "crApp.h"
#include "crBatchedPrimitives.h"
#include "crDevUtils.h"
#include "crBatchedSprites.h"
#include "crGraphics.h"
#include "crRenderSystem.h"
#include "crGraphicsStats.h"
#include "crParticleSystem.h"
#include "crPhysics.h"
#include "crPostProcess.h"
#include "crUi.h"

void crDevProfiler::Init()
{

}
void crDevProfiler::Cleanup()
{

}

void crDevProfiler::TryRenderPanel( const crApp* const app, float unscaledDelta )
{
    if( enabled == false )
        return;

    _accumTime += unscaledDelta;
    ++_accumFrames;

    if( _accumTime >= REFRESH_INTERVAL )
    {
        _displayFps = ( float )_accumFrames / _accumTime;
        _displayMs  = ( _accumTime / ( float )_accumFrames ) * 1000.0f;

        _accumTime   = 0.0f;
        _accumFrames = 0;
    }

    _history[ _historyIndex ] = unscaledDelta * 1000.0f;
    _historyIndex = ( _historyIndex + 1 ) % HISTORY_SIZE;

    const ImGuiViewport* viewport = ImGui::GetMainViewport();
    const float          pad      = 10.0f;

    ImVec2 pos;
    pos.x = viewport->WorkPos.x + viewport->WorkSize.x - pad;
    pos.y = viewport->WorkPos.y + pad;
    ImGui::SetNextWindowPos( pos, ImGuiCond_Always, ImVec2( 1.0f, 0.0f ) );   // pivot top-right

    ImGuiWindowFlags windowFlags = ImGuiWindowFlags_NoTitleBar
                                 | ImGuiWindowFlags_NoMove
                                 | ImGuiWindowFlags_NoResize
                                 | ImGuiWindowFlags_AlwaysAutoResize
                                 | ImGuiWindowFlags_NoSavedSettings;

    ImGui::PushStyleVar( ImGuiStyleVar_WindowBorderSize, 0.0f );
    ImGui::PushStyleVar( ImGuiStyleVar_ItemSpacing, ImVec2( 8.0f, -1.0f ) );
    ImGui::Begin( "Dev Profiler", nullptr, windowFlags );

    char text[ 100 ] = { 0, };
    SDL_snprintf( text, sizeof( text ), "fps %.0f (%.2fms)", _displayFps, _displayMs );
    crDevUtils::TextRight( text );

    ImGui::PlotLines( "##frametime",
                      _history,
                      HISTORY_SIZE,
                      _historyIndex,
                      nullptr,
                      0.0f,
                      FLT_MAX,
                      ImVec2( 0.0f, 30.0f ) );

    const b3Profile  profile  = b3World_GetProfile( app->physics->World() );
    const b3Counters counters = b3World_GetCounters( app->physics->World() );

    SDL_snprintf( text, sizeof( text ), "step %.2f ms", profile.step );
    crDevUtils::TextRight( text );
    SDL_snprintf( text, sizeof( text ), "body %d | task %d | island %d", counters.bodyCount, counters.taskCount, counters.islandCount );
    crDevUtils::TextRight( text );
    SDL_snprintf( text, sizeof( text ), "draw %d (%d verts)", crGraphicsStats::drawCalls, crGraphicsStats::vertices );
    crDevUtils::TextRight( text );
    SDL_snprintf( text, sizeof( text ), "atlas runs ui %d | world %d | fx %d",
                  app->ui->SpriteRunsLastFlush(), app->graphics->Sprites()->RunsLastFlush(), app->particles->DrawsLastRender() );
    crDevUtils::TextRight( text );

    {// primitive instances per shape (columns = materials)
        static const char* SHAPE_NAMES[] = { "box", "sph", "shell", "oct", "ring", "ringt", "ico", "beam", "tet", "arrow", "hex", "star", "spike", "diam" };
        static_assert( SDL_arraysize( SHAPE_NAMES ) == static_cast<size_t>( EPrimitive::_SIZE ), "SHAPE_NAMES out of sync with EPrimitive" );

        const crBatchedPrimitives* primitives = app->renderSys->Primitives();

        for( int32_t s = 0; s < static_cast<int32_t>( EPrimitive::_SIZE ); ++s )
        {
            const int32_t alpha = primitives->AlphaInstanceCount( s );

            char    counts[ 48 ] = "";
            int32_t total        = alpha;
            for( int32_t m = 0; m < primitives->MaterialCount(); ++m )
            {
                const int32_t n = primitives->CameraInstanceCount( m, s );
                total += n;

                char one[ 16 ];
                SDL_snprintf( one, sizeof( one ), ( m == 0 ) ? "%d" : " | %d", n );
                SDL_strlcat( counts, one, sizeof( counts ) );
            }

            if( total == 0 )
                continue;

            if( alpha > 0 )
                SDL_snprintf( text, sizeof( text ), "%s %s a%d", SHAPE_NAMES[ s ], counts, alpha );
            else
                SDL_snprintf( text, sizeof( text ), "%s %s", SHAPE_NAMES[ s ], counts );
            crDevUtils::TextRight( text );
        }

        char    shadow[ 48 ] = "";
        int32_t shadowTotal  = 0;
        for( int32_t c = 0; c < crShadowMap::CASCADE_COUNT; ++c )
        {
            int32_t n = 0;
            for( int32_t s = 0; s < static_cast<int32_t>( EPrimitive::_SIZE ); ++s )
                n += primitives->ShadowInstanceCount( c, s );
            shadowTotal += n;

            char one[ 16 ];
            SDL_snprintf( one, sizeof( one ), ( c == 0 ) ? "%d" : " | %d", n );
            SDL_strlcat( shadow, one, sizeof( shadow ) );
        }
        if( shadowTotal > 0 )
        {
            SDL_snprintf( text, sizeof( text ), "shadow %s", shadow );
            crDevUtils::TextRight( text );
        }
    }

    {// resolution + GL context diagnostics (wasm shows its context line in the shell instead — crWasmBridge)
#ifndef __EMSCRIPTEN__
        if( _ctxQueried == false )
        {
            _ctxQueried = true;

            int32_t msaaBuffers = 0;
            int32_t msaaSamples = 0;
            int32_t alphaBits   = 0;
            if( SDL_GL_GetAttribute( SDL_GL_MULTISAMPLEBUFFERS, &msaaBuffers ) && SDL_GL_GetAttribute( SDL_GL_MULTISAMPLESAMPLES, &msaaSamples ) )
                _ctxAntialias = ( msaaBuffers > 0 ) ? msaaSamples : 0;
            if( SDL_GL_GetAttribute( SDL_GL_ALPHA_SIZE, &alphaBits ) )
                _ctxAlpha = alphaBits;
        }
#endif

        int2 windowPx = {};
        int2 windowPt = {};
        SDL_GetWindowSizeInPixels( app->window, &windowPx.x, &windowPx.y );
        SDL_GetWindowSize( app->window, &windowPt.x, &windowPt.y );

        SDL_snprintf( text, sizeof( text ), "window %dx%d px (%dx%d @%.2f)", windowPx.x, windowPx.y, windowPt.x, windowPt.y, SDL_GetWindowPixelDensity( app->window ) );
        crDevUtils::TextRight( text );

        const crPostProcess* post      = app->graphics->PostProcess();
        const int2           sceneSize = post->SceneSize();
        SDL_snprintf( text, sizeof( text ), "scene %dx%d (scale %.2f)", sceneSize.x, sceneSize.y, post->RenderScale() );
        crDevUtils::TextRight( text );

#ifndef __EMSCRIPTEN__
        SDL_snprintf( text, sizeof( text ), "backbuffer msaa %d | alpha bits %d", _ctxAntialias, _ctxAlpha );
        crDevUtils::TextRight( text );
#endif
    }

    ImGui::End();
    ImGui::PopStyleVar( 2 );
}

