#ifndef ENTITY_H
#define ENTITY_H

#include "sprite.h"
#include "mixer.h"

enum entity_prop {
    ENTITY_PROP_NONE,

    ENTITY_PROP_ANIMATING,
    ENTITY_PROP_PLAYER_CONTROLLED,
    ENTITY_PROP_CAMERA,
    ENTITY_PROP_CAMERA_ACTIVE,

    /* Test props */

    ENTITY_PROP_TEST,
    ENTITY_PROP_TEST_SOUND_EMITTER,

    ENTITY_PROP_COUNT
};

struct entity_handle {
    u64 idx;
    u64 gen;
};

struct entity_store {
    struct arena arena;
    u64 count;
    struct entity_handle first_free;
};

struct entity {
    b32 valid;
    struct entity_handle handle;
    u64 continuity_gen;
    u64 props[(ENTITY_PROP_COUNT + 63) / 64];

    struct entity_handle next_free;

    /* Tree */
    struct entity_handle parent;
    struct entity_handle next;
    struct entity_handle prev;
    struct entity_handle first;
    struct entity_handle last;

    /* ====================================================================== */

    struct xform rel_xform;  /* Transform in relation to parent entity (or the world if entity has no parent) */
    struct xform world_xform;  /* Calculated post-physics */

    /* ====================================================================== */
    /* Physics */

    struct v2 acceleration;
    struct v2 velocity;

    /* ENTITY_PROP_PLAYER_CONTROLLED */
    f32 player_max_speed;
    f32 player_acceleration;
    struct v2 player_aim;

    /* ====================================================================== */
    /* Sprite */

    struct sprite_tag sprite;
    struct string sprite_span_name;
    struct xform sprite_quad_xform;
    u32 sprite_tint;

    /* ====================================================================== */
    /* Animation */

    /* ENTITY_PROP_ANIMATING */
    f64 animation_time_in_frame;
    u64 animation_frame;

    /* ====================================================================== */
    /* Testing */

    /* ENTITY_PROP_TEST */
    b32 test_initialized;
    struct xform test_start_rel_xform;
    struct xform test_start_sprite_xform;

    /* ENTITY_PROP_TEST_SOUND_EMITTER */
    struct string sound_name;
    struct mixer_desc sound_desc;
    struct mixer_track_handle sound_handle;

    /* ====================================================================== */

    /* ENTITY_PROP_CAMERA */
    struct entity_handle camera_follow;
    struct xform camera_quad_xform;
    f32 camera_lerp;  /* Rate at which camera xform approaches target xform */

    u32 camera_lerp_continuity_gen;
    struct xform camera_rel_xform_target;  /* Calculated from camera_follow */
    u32 camera_applied_lerp_continuity_gen_plus_one;  /* Calculated */
};

struct entity_array {
    struct entity *entities;
    u64 count;
};

struct entity_prop_array {
    enum entity_prop *props;
    u64 count;
};

/* ========================== *
 * Nil
 * ========================== */

INLINE struct entity *entity_nil(void)
{
    extern READONLY struct entity _g_entity_nil;
    return &_g_entity_nil;
}

/* ========================== *
 * Property helpers
 * ========================== */

INLINE void entity_enable_prop(struct entity *entity, enum entity_prop prop)
{
    u64 index = prop / 64;
    u64 bit = prop % 64;
    entity->props[index] |= ((u64)1 << bit);
}

INLINE void entity_disable_prop(struct entity *entity, enum entity_prop prop)
{
    u64 index = prop / 64;
    u64 bit = prop % 64;
    entity->props[index] &= ~((u64)1 << bit);
}

INLINE b32 entity_has_prop(struct entity *entity, enum entity_prop prop)
{
    u64 index = prop / 64;
    u64 bit = prop % 64;
    return !!(entity->props[index] & ((u64)1 << bit));
}

/* ========================== *
 * Entity functions
 * ========================== */

/* Entity store */
struct entity_store entity_store_alloc(void);
void entity_store_release(struct entity_store *store);
void entity_store_copy_replace(struct entity_store *dest, struct entity_store *src);

/* Entity */
struct entity *entity_alloc(struct entity_store *store);
void entity_release(struct entity_store *store, struct entity *entity);

struct entity_array entity_store_as_array(struct entity_store *store);
struct entity *entity_from_handle(struct entity_store *store, struct entity_handle handle);
struct entity *entity_find_first_match_one(struct entity_store *store, enum entity_prop prop);
struct entity *entity_find_first_match_all(struct entity_store *store, struct entity_prop_array props);

void entity_link(struct entity_store *store, struct entity *parent, struct entity *child);

#endif
