#ifndef BG_STATE_H
#define BG_STATE_H

#include <stdint.h>
#include <stddef.h>
#include <string.h>

#include "recovered_game.h"

#define BG_ANIM_MAX_ANIMATIONS 512

/* bg_static_animation_t::flags is a fixed-width word, but the individual bits
 * form one shared client/server domain. Names describe the parser producer or
 * the exact consumer behavior, rather than unrelated call-site guesses. */
typedef enum bg_anim_entry_flag_e {
    BG_ANIM_ENTRY_NON_PRIMITIVE = 0x00000001,
    BG_ANIM_ENTRY_VERTICAL_MOTION = 0x00000002,
    BG_ANIM_ENTRY_TURRET = 0x00000004,
    BG_ANIM_ENTRY_FIRE_WEAPON = 0x00000008,
    BG_ANIM_ENTRY_STRAFE_LEFT = 0x00000010,
    BG_ANIM_ENTRY_STRAFE_RIGHT = 0x00000020,
    BG_ANIM_ENTRY_DEATH = 0x00000040,
    BG_ANIM_ENTRY_LOOPED = 0x00000080,
    BG_ANIM_ENTRY_UNUSED = 0x00000100
} bg_anim_entry_flag_t;

typedef struct bg_static_animation_s {
    char name[64];                                 /* +0x00 */
    int32_t blendTime;                             /* +0x40 */
    int32_t moveSpeed;                             /* +0x44 */
    int32_t duration;                              /* +0x48 */
    int32_t hash;                                  /* +0x4c */
    uint32_t flags;                                /* +0x50, bg_anim_entry_flag_t bits */
    uint32_t stateFlags;                           /* +0x54, one bit per bg_anim_move_type_t */
    int32_t usedByScript;                          /* +0x58 */
} bg_static_animation_t;

typedef char bg_static_animation_blend_time_offset_check[
    (offsetof(bg_static_animation_t, blendTime) == 0x40) ? 1 : -1];
typedef char bg_static_animation_move_speed_offset_check[
    (offsetof(bg_static_animation_t, moveSpeed) == 0x44) ? 1 : -1];
typedef char bg_static_animation_duration_offset_check[
    (offsetof(bg_static_animation_t, duration) == 0x48) ? 1 : -1];
typedef char bg_static_animation_flags_offset_check[
    (offsetof(bg_static_animation_t, flags) == 0x50) ? 1 : -1];
typedef char bg_static_animation_state_flags_offset_check[
    (offsetof(bg_static_animation_t, stateFlags) == 0x54) ? 1 : -1];
typedef char bg_static_animation_size_header_check[
    (sizeof(bg_static_animation_t) == 0x5c) ? 1 : -1];

typedef const char *(*bg_anim_sound_alias_fn)(const char *name);
typedef void (*bg_anim_sound_event_fn)(int clientNum,
                                       const char *soundAliasName);

typedef struct bg_static_animation_table_s {
    bg_static_animation_t entries[BG_ANIM_MAX_ANIMATIONS];         /* +0x00000 */
    int32_t count;                                                 /* +0xb800 */
    bg_anim_script_list_t animationLists[ANIM_STATE_COUNT *
                                         ANIM_MT_COUNT];            /* +0xb804 */
    bg_anim_script_list_t cannedAnimationLists[ANIM_STATE_COUNT *
                                               ANIM_MT_COUNT];      /* +0x14924 */
    bg_anim_script_list_t statechangeLists[ANIM_STATE_COUNT *
                                           ANIM_STATE_COUNT];       /* +0x1da44 */
    bg_anim_script_list_t eventLists[ANIM_EVENT_RELOAD];           /* +0x1fa84 */
    bg_anim_script_list_t scriptLists[ANIM_EVENT_COUNT -
                                      ANIM_EVENT_RELOAD];           /* +0x20eac */
    bg_anim_script_t scriptPool[BG_ANIM_MAX_GLOBAL_SCRIPTS];       /* +0x21ac4 */
    int32_t scriptPoolCount;                                       /* +0xa7ac4 */
    XAnim *anims;                                                   /* +0xa7ac8 */
} bg_static_animation_table_t;

typedef bg_static_animation_table_t bg_static_animation_table_view_t;

typedef enum bg_anim_parse_section_e {
    ANIM_SECTION_DEFINES = 0,
    ANIM_SECTION_ANIMATIONS = 1,
    ANIM_SECTION_CANNED_ANIMATIONS = 2,
    ANIM_SECTION_STATECHANGES = 3,
    ANIM_SECTION_EVENTS = 4
} bg_anim_parse_section_t;

/* NOT_FROM_ORIGINAL_SOURCE: maintained accessor for host-safe recovered table reads. */
static inline uint32_t game_compat_bg_read_u32_at_offset(const void *base, size_t offset)
{
    uint32_t value;

    memcpy(&value, (const uint8_t *)(const void *)base + offset,
           sizeof(value));
    return value;
}

/* NOT_FROM_ORIGINAL_SOURCE: maintained accessor for the typed bgs animation table view. */
static inline XAnim *game_compat_bg_static_animation_anims(
    const bg_static_animation_table_view_t *table)
{
    return table->anims;
}

/* NOT_FROM_ORIGINAL_SOURCE: maintained accessor for generated animation-slot word reads. */
static inline uint32_t game_compat_bg_anim_slot_animation_word(const void *clientInfo,
                                                size_t slotOffset)
{
    return game_compat_bg_read_u32_at_offset(clientInfo,
                              slotOffset +
                                  offsetof(bg_anim_slot_t, animationWord));
}

/* NOT_FROM_ORIGINAL_SOURCE: maintained accessor for generated animation-slot word reads. */
static inline uint32_t game_compat_bg_anim_slot_animation_word_from_slot(const void *slot)
{
    return game_compat_bg_read_u32_at_offset(
        slot, offsetof(bg_anim_slot_t, animationWord));
}

/* NOT_FROM_ORIGINAL_SOURCE: maintained accessor for generated animation table offset reads. */
static inline uint32_t game_compat_bg_anim_slot_table_offset(const void *clientInfo,
                                              size_t slotOffset)
{
    return game_compat_bg_read_u32_at_offset(clientInfo,
                              slotOffset +
                                  offsetof(bg_anim_slot_t, animationOffset));
}

/* NOT_FROM_ORIGINAL_SOURCE: maintained accessor for generated animation table offset reads. */
static inline uint32_t game_compat_bg_anim_slot_table_offset_from_slot(const void *slot)
{
    return game_compat_bg_read_u32_at_offset(
        slot, offsetof(bg_anim_slot_t, animationOffset));
}

/* NOT_FROM_ORIGINAL_SOURCE: maintained accessor for generated static-animation flag reads. */
static inline uint32_t game_compat_bg_static_animation_flags_at_offset(
    const bg_static_animation_table_view_t *table, uint32_t animationOffset)
{
    return table->entries[(size_t)animationOffset /
                          sizeof(table->entries[0])].flags;
}

/*
 * bg_t -- small 12-byte global timing state.
 *
 * Binary symbol: `bg` at ELF RVA 0x136a60, size 12.
 * Ghidra address range: 0x146a60 .. 0x146a6b (imagebase 0x10000).
 * G_InitGame memsets it to zero; G_RunFrame writes all three fields.
 *
 * RECOVERED(UO-GAME-UNK-0142): field names are recovered.
 */
typedef struct bg_s {
    int32_t time;                        /* +0x00, set to levelTime */
    int32_t levelFrameTime;              /* +0x04, DAT_00146a64, set to levelTime */
    int32_t frameTime;                   /* +0x08, DAT_00146a68, frame delta msec */
} bg_t;

/*
 * Partial bgs animation-prefix map.
 *
 * Binary symbol: `bgs` at ELF RVA 0x136a80, size 0xBAEF4 (765684 bytes).
 * Ghidra address range: 0x146a80 .. 0x201973 (imagebase 0x10000).
 * The complete global is declared as bgs_t in game_globals.h: this file maps
 * the animation prefix that starts at bgs+0. The client-info tail begins at
 * the original i386 offset +0xa7af4 and is typed as bgs_t::clientinfo.
 *
 * NOTE: The DAT_0024xxxx addresses from the decompiler are NOT bgs offsets.
 * They are level fields (level is at Ghidra 0x24b520). Only the large-offset
 * accesses like bgs._686808_ are genuine bgs fields.
 *
 * RECOVERED(UO-GAME-UNK-0143): offsets confirmed from decompiler output.
 */

#endif /* BG_STATE_H */
