#include "crEcs.h"

#include <box3d/box3d.h>
#include <SDL3/SDL_log.h>

#include "crStruct.h"
#include "crEcsComponents.h"
#include "crMath.h"
#include "crTasks.h"

void crEcsSnapshotTask::ExecuteRange( enki::TaskSetPartition range, uint32_t threadIndex )
{
    ( void )threadIndex;

    auto& storage = registry->storage<CTransform>();
    const entt::entity* entities = storage.data();   // packed array — contiguous, index-splittable

    for( uint32_t i = range.start; i < range.end; ++i )
    {
        CTransform& t = storage.get( entities[ i ] );
        t.previous = t.current;
    }
}

void crEcs::Init()
{
    RecreateRegistry();
}
void crEcs::Cleanup()
{
    registry.clear();
}
void crEcs::Reset()
{
    RecreateRegistry();
}

void crEcs::SnapshotPrevTransforms()
{
    const uint32_t count = static_cast<uint32_t>( registry.storage<CTransform>().size() );
    if( count == 0 )
        return;

    _snapshotTask.registry   = &registry;
    _snapshotTask.m_SetSize  = count;
    _snapshotTask.m_MinRange = SNAPSHOT_MIN_RANGE;  //NOTE( visai, 2026.7.2 ): enki 추천- 파티션당 10,000 사이클?

    crTasks::scheduler.AddTaskSetToPipe( &_snapshotTask );
    crTasks::scheduler.WaitforTask( &_snapshotTask );
}
void crEcs::SyncDynamicTransforms( float fdt )
{
    auto withBody = registry.view<const CPhysicsBody, CVelocity, CTransform>();
    for( entt::entity e : withBody )
    {
        const CPhysicsBody& b = withBody.get<CPhysicsBody>( e );
        CVelocity&          v = withBody.get<CVelocity>( e );
        CTransform&         t = withBody.get<CTransform>( e );

        v.linear  = b3Body_GetLinearVelocity( b.bodyId );
        v.angular = b3Body_GetAngularVelocity( b.bodyId );

        t.current = b3Body_GetTransform( b.bodyId );
    }

    auto withoutBody = registry.view<CVelocity, CTransform>( entt::exclude<CPhysicsBody> );
    for( entt::entity e : withoutBody )
    {
        const CVelocity& v = withoutBody.get<CVelocity>( e );
        CTransform&      t = withoutBody.get<CTransform>( e );

        t.current.p.x += ( v.linear.x * fdt );
        t.current.p.y += ( v.linear.y * fdt );
        t.current.p.z += ( v.linear.z * fdt );
        t.current.q    = crMath::IntegrateRotation( t.current.q, v.angular, fdt );
    }
}

uint64_t crEcs::HashTransforms() const
{
    uint64_t hash = crMath::HASH_FNV1A_SEED;

    // scenery is excluded by tag, never inferred — see CTag_NonHashTarget. iteration follows the
    // CTransform pool's packed order, itself a function of the create/destroy sequence
    const auto view = registry.view<const CTransform>( entt::exclude<CTag_NonHashTarget> );

    uint64_t count = 0;   // fixed width — size_t differs between win64/wasm32
    for( const entt::entity entity : view )
    {
        const CTransform& t = view.get<const CTransform>( entity );
        hash = crMath::HashFnv1a( &t.current, sizeof( t.current ), hash );
        ++count;
    }

    // folded in last, so a divergence in how MANY things exist is caught as well as where they are
    hash = crMath::HashFnv1a( &count, sizeof( count ), hash );

    return hash;
}

void crEcs::RecreateRegistry()
{
    registry = entt::registry{};

    entt::entity dummyForValidZero = registry.create();
    SDL_assert( entt::to_integral( dummyForValidZero ) == 0 );

    SDL_LogTrace( CR_LOG_CATEGORY_ECS, "crEcs: registry recreated( dummy id:%u )", entt::to_integral( dummyForValidZero ) );

    registry.storage<CTransform>().reserve( INIT_CAPA_TRANSFORM );
    registry.storage<CPhysicsBody>().reserve( INIT_CAPA_PHYSICS_BODY );
    registry.storage<CVelocity>().reserve( INIT_CAPA_VELOCITY );
    registry.storage<CPrimitive>().reserve( INIT_CAPA_PRIMITIVE );
}
