#pragma once

#include <box3d/box3d.h>
#include <enkiTS/TaskScheduler.h>

class crApp;

// enkiTS task adapter for Box3D's enqueueTask/finishTask callbacks.
// Box3D tasks are single-shot (no range split — box2d passed itemCount/minRange, box3d does not).
class crPhysicsTask : public enki::ITaskSet
{
public:
    b3TaskCallback* fn      = nullptr;
    void*           context = nullptr;

    void ExecuteRange( enki::TaskSetPartition range, uint32_t threadIndex ) override
    {
        ( void )range;
        ( void )threadIndex;
        fn( context );
    }
};

class crPhysics
{
public:
    static constexpr b3Vec3  PHYSICS_GRAVITY  = { 0.00f, -9.81f, 0.00f };
    static constexpr int32_t PHYSICS_SUBSTEPS = 4;   // the default a game inherits if it says nothing

    // solver substeps per fixed step — the single biggest knob on physics cost, and how much of it
    // a scene needs is the GAME's call: stacked/jointed setups want the accuracy, a flat crowd of
    // spheres does not. set it in Game::Init; it survives ResetWorld (world creation does not read it)
    int32_t substeps = PHYSICS_SUBSTEPS;

private:
    static constexpr int32_t MAX_PHYSICS_TASKS = 64;

    // scheduler is shared — see crTasks
    inline static crPhysicsTask _tasks[ MAX_PHYSICS_TASKS ];
    inline static int32_t       _taskCount = 0;

    b3WorldId _world;

public:
    void Init( crApp* app );
    void Cleanup();
    void FixedUpdate();

    void ResetWorld( crApp* app );

    b3WorldId World() const { return _world; }

private:
    void CreateWorld( crApp* app );

    static void* TaskEnqueue( b3TaskCallback* task, void* taskContext, void* userContext, const char* taskName );
    static void  TaskFinish( void* userTask, void* userContext );
};
