#include "crPhysics.h"

#include <SDL3/SDL_log.h>

#include "crApp.h"
#include "crDevPhysics.h"
#include "crStruct.h"
#include "crTasks.h"

void crPhysics::Init( crApp* app )
{
    CreateWorld( app );
}

void crPhysics::Cleanup()
{
    b3DestroyWorld( _world );
}

void crPhysics::FixedUpdate()
{
    _taskCount = 0;   // reset per-step task pool

    b3World_Step( _world, crApp::FIXED_TIMESTEP, substeps );
}

void crPhysics::ResetWorld( crApp* app )
{
    b3DestroyWorld( _world );
    CreateWorld( app );
}

void crPhysics::CreateWorld( crApp* app )
{
    b3WorldDef def = b3DefaultWorldDef();
    def.gravity = PHYSICS_GRAVITY;

    def.workerCount = crTasks::WORKER_COUNT;
    def.enqueueTask = TaskEnqueue;
    def.finishTask  = TaskFinish;

    //NOTE( Claude ): debug-shape handles - box3d hands each shape's geometry to crDevPhysics once, DrawShape uses it every frame
    def.createDebugShape      = crDevPhysics::CreateDebugShape;
    def.destroyDebugShape     = crDevPhysics::DestroyDebugShape;
    def.userDebugShapeContext = app->devPhysics;

    _world = b3CreateWorld( &def );

    SDL_LogTrace( CR_LOG_CATEGORY_PHYSICS, "crPhysics: box3d world created" );
}

/*static*/ void* crPhysics::TaskEnqueue( b3TaskCallback* task, void* taskContext, void* userContext, const char* taskName )
{
    ( void )userContext;
    ( void )taskName;

    if( _taskCount < MAX_PHYSICS_TASKS )
    {
        crPhysicsTask& t = _tasks[ _taskCount ];
        t.m_SetSize = 1;
        t.fn        = task;
        t.context   = taskContext;
        crTasks::scheduler.AddTaskSetToPipe( &t );
        ++_taskCount;
        return &t;
    }

    task( taskContext );   // pool full → run inline; null return tells box3d it already finished
    return nullptr;
}

/*static*/ void crPhysics::TaskFinish( void* userTask, void* userContext )
{
    ( void )userContext;

    if( userTask != nullptr )
    {
        crTasks::scheduler.WaitforTask( static_cast<crPhysicsTask*>( userTask ) );
    }
}
