#include "engine/entity.h"
#include "engine/camera.h"
#include <string.h>
#include <stdio.h>

/* ── Distance culling ──────────────────────────────── */

/* Margin around the viewport for update culling (in pixels).
 * Entities within this margin still update even if off-screen,
 * so they are active before scrolling into view. */
#define UPDATE_MARGIN  (SCREEN_WIDTH * 2.0f)

/* Margin for render culling — tighter than update since only
 * entities actually visible (plus a small buffer) need drawing. */
#define RENDER_MARGIN  64.0f

/* Check whether a body is within a rectangle expanded by margin. */
static bool in_camera_range(const Body *body, const Camera *cam, float margin) {
    float left   = cam->pos.x - margin;
    float top    = cam->pos.y - margin;
    float right  = cam->pos.x + cam->viewport.x + margin;
    float bottom = cam->pos.y + cam->viewport.y + margin;

    float ex = body->pos.x + body->size.x;
    float ey = body->pos.y + body->size.y;

    return body->pos.x < right && ex > left &&
           body->pos.y < bottom && ey > top;
}

void entity_manager_init(EntityManager *em) {
    memset(em, 0, sizeof(EntityManager));
}

Entity *entity_spawn(EntityManager *em, EntityType type, Vec2 pos) {
    /* Find a free slot */
    for (int i = 0; i < MAX_ENTITIES; i++) {
        if (!em->entities[i].active) {
            Entity *e = &em->entities[i];
            memset(e, 0, sizeof(Entity));
            e->type = type;
            e->active = true;
            e->body.pos = pos;
            e->body.gravity_scale = 1.0f;
            e->health = 1;
            e->max_health = 1;
            if (i >= em->count) em->count = i + 1;
            return e;
        }
    }
    fprintf(stderr, "Warning: entity limit reached (%d)\n", MAX_ENTITIES);
    return NULL;
}

void entity_destroy(EntityManager *em, Entity *e) {
    if (!e || !e->active) return;

    /* Call type-specific destructor */
    if (em->destroy_fn[e->type]) {
        em->destroy_fn[e->type](e);
    }

    e->active = false;
    e->type = ENT_NONE;

    /* Shrink count if possible */
    while (em->count > 0 && !em->entities[em->count - 1].active) {
        em->count--;
    }
}

void entity_update_all(EntityManager *em, float dt, const Tilemap *map,
                       const Camera *cam) {
    for (int i = 0; i < em->count; i++) {
        Entity *e = &em->entities[i];
        if (!e->active) continue;

        /* Check if dead */
        if (e->health <= 0 && !(e->flags & ENTITY_INVINCIBLE)) {
            e->flags |= ENTITY_DEAD;
        }

        /* Distance culling: skip far-away entities unless they opt out.
         * Dead entities always get their update callback so type-specific
         * cleanup (destroy, particles, etc.) can run. */
        if (cam && !(e->flags & (ENTITY_ALWAYS_UPDATE | ENTITY_DEAD))) {
            if (!in_camera_range(&e->body, cam, UPDATE_MARGIN))
                continue;
        }

        /* Call type-specific update */
        if (em->update_fn[e->type]) {
            em->update_fn[e->type](e, dt, map);
        }
    }
}

void entity_render_all(EntityManager *em, const Camera *cam) {
    for (int i = 0; i < em->count; i++) {
        Entity *e = &em->entities[i];
        if (!e->active) continue;

        /* Render culling: skip entities outside the visible area. */
        if (cam && !in_camera_range(&e->body, cam, RENDER_MARGIN))
            continue;

        if (em->render_fn[e->type]) {
            em->render_fn[e->type](e, cam);
        }
    }
}

void entity_manager_clear(EntityManager *em) {
    for (int i = 0; i < em->count; i++) {
        if (em->entities[i].active) {
            entity_destroy(em, &em->entities[i]);
        }
    }
    em->count = 0;
}

void entity_register(EntityManager *em, EntityType type,
                     EntityUpdateFn update, EntityRenderFn render,
                     EntityDestroyFn destroy) {
    em->update_fn[type] = update;
    em->render_fn[type] = render;
    em->destroy_fn[type] = destroy;
}
