#ifndef GAME_TYPES_H
#define GAME_TYPES_H

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

#include "info.h"
#include "recovered_game.h"

/* 0x92f49..0x93131 pass and return this eight-byte value through the i386
 * aggregate ABI; it is not a scalar uint64_t. */
typedef struct qint64_s {
    uint8_t b0;
    uint8_t b1;
    uint8_t b2;
    uint8_t b3;
    uint8_t b4;
    uint8_t b5;
    uint8_t b6;
    uint8_t b7;
} qint64;
typedef char qint64_size[(sizeof(qint64) == 8) ? 1 : -1];

/*
 * Unified cvar structure used throughout the codebase.
 * Previously had multiple names: vmCvar_t, client_frame_cvar_t,
 * script_builtin_cvar_t, script_player_cvar_t, client_lifecycle_cvar_t
 */
#define MAX_CVAR_VALUE_STRING 256

typedef struct vmCvar_s {
    int32_t handle;                      /* +0x00 */
    int32_t modificationCount;           /* +0x04 */
    float value;                         /* +0x08 */
    int32_t integer;                     /* +0x0c */
    char string[MAX_CVAR_VALUE_STRING]; /* +0x10 */
} vmCvar_t;

typedef char vmcvar_string_offset[
    (offsetof(vmCvar_t, string) == 0x10) ? 1 : -1];
typedef char vmcvar_size[(sizeof(vmCvar_t) == 0x110) ? 1 : -1];

/* Original hintStrings indices and the immediate values exchanged through
 * playerState.serverCursorHint.  Zero is the runtime "no active hint" value;
 * indices 1..10 have the exact names published by the stock string table. */
typedef enum game_cursor_hint_e {
    CURSOR_HINT_OFF = 0,
    CURSOR_HINT_NONE = 1,
    CURSOR_HINT_ACTIVATE = 2,
    CURSOR_HINT_NOACTIVATE = 3,
    CURSOR_HINT_DOOR = 4,
    CURSOR_HINT_DOOR_LOCKED = 5,
    CURSOR_HINT_MG42 = 6,
    CURSOR_HINT_LMG = 7,
    CURSOR_HINT_HEALTH = 8,
    CURSOR_HINT_LADDER = 9,
    CURSOR_HINT_FRIENDLY = 10
} game_cursor_hint_t;

typedef struct com_parse_session_s {
    char token[0x400];       /* +0x000 */
    int32_t line;            /* +0x400 */
    int32_t ungetToken;      /* +0x404 */
    int32_t spaceDelimited;  /* +0x408 */
    int32_t csv;             /* +0x40c */
    int32_t parseNegativeNumbers; /* +0x410 */
    int32_t savedLine;       /* +0x414 */
    char *savedParse;        /* +0x418, borrowed parser cursor into caller-owned script text. */
    char name[0x40];         /* +0x41c */
} com_parse_session_t;

typedef struct activate_candidate_s {
    gentity_t *ent;        /* +0x0 */
    float score;           /* +0x4 */
} activate_candidate_t;

#if UINTPTR_MAX == 0xffffffffu
GAME_STATIC_ASSERT(activate_candidate_size,
                 sizeof(activate_candidate_t) == 0x8);
GAME_STATIC_ASSERT(activate_candidate_score_offset,
                 offsetof(activate_candidate_t, score) == 0x4);
#endif

typedef struct client_field_s client_field_t;
typedef void (*client_field_callback_t)(gclient_t *client, gclient_t *self,
                                        const client_field_t *field);

struct client_field_s {
    const char *name;
    size_t offset;
    int32_t type;
    client_field_callback_t setter;
    client_field_callback_t getter;
};

/*
 * Trace result structures
 */

/* Forward declaration for hudelem type */
typedef struct hudelem_s hudelem_t;

/* Mac symbols expose the original alias as trace_t. The Linux game, engine,
 * cgame, and Windows client all use this same 0x30-byte i386 result. In
 * particular, StopFollowing places its next local exactly 0x30 bytes after the
 * trace at 0x4b2bf, and VEH_GroundTrace copies exactly twelve dwords at
 * 0x7fd90; the former 0x0c "tail" was unrelated adjacent stack/global state. */
typedef struct trace_s {
    float fraction;             /* +0x00 */
    vec3_t endpos;              /* +0x04 */
    vec3_t normal;              /* +0x10 */
    int32_t surfaceFlags;       /* +0x1c */
    int32_t contents;           /* +0x20 */
    const char *material;       /* +0x24: collision material name */
    uint16_t entityNum;         /* +0x28 */
    uint16_t partName;          /* +0x2a: script-string hit-part handle */
    uint16_t partGroup;         /* +0x2c: DObj part-state/hit-location index */
    uint8_t allsolid;           /* +0x2e */
    uint8_t startsolid;         /* +0x2f */
} trace_t;

typedef enum vehicle_parse_field_type_e {
    VEH_SCRIPT_FIELD_STRING = 1,
    VEH_SCRIPT_FIELD_FLOAT = 4,
    VEH_SCRIPT_FIELD_INT = 5,
    VEH_PARSE_FIELD_TYPE_VEHICLE = 8
} vehicle_parse_field_type_t;

typedef struct vehicle_node_spawn_field_s {
    const char *name;
    size_t offset;
    int32_t type;
} vehicle_node_spawn_field_t;

typedef void (*vehicle_parse_copy_string_t)(char *dest, const char *src);

typedef enum vehicle_turret_activity_slot_e {
    VEH_TURRET_ACTIVITY_PRIMARY,
    VEH_TURRET_ACTIVITY_GUNNER
} vehicle_turret_activity_slot_t;

typedef enum binary_mover_state_e {
    MOVER_STATE_DOOR_POS1 = 0,
    MOVER_STATE_DOOR_POS2 = 1,
    MOVER_STATE_DOOR_POS3 = 2,
    MOVER_STATE_DOOR_MOVING_TO_POS2 = 3,
    MOVER_STATE_DOOR_MOVING_TO_POS1 = 4,
    MOVER_STATE_DOOR_MOVING_TO_POS3 = 5,
    MOVER_STATE_DOOR_MOVING_TO_POS2_FROM_POS3 = 6,
    MOVER_STATE_ROTATE_POS1 = 7,
    MOVER_STATE_ROTATE_POS2 = 8,
    MOVER_STATE_ROTATE_MOVING_TO_POS2 = 9,
    MOVER_STATE_ROTATE_MOVING_TO_POS1 = 10
} binary_mover_state_t;

typedef struct mover_push_record_s {
    gentity_t *ent;
    vec3_t origin;
    vec3_t angles; /* idTech pushed_t slot; UO binary does not read/write it. */
    float yawDelta;
} mover_push_record_t;

typedef struct pml_s {
    vec3_t forward;           /* +0x00  DAT_002023a0 */
    vec3_t right;             /* +0x0c  DAT_002023ac */
    vec3_t up;                /* +0x18  DAT_002023b8 */
    float frametime;          /* +0x24  DAT_002023c4 */
    int32_t msec;             /* +0x28  DAT_002023c8, clamped PmoveSingle frame msec */
    int32_t groundPlane;      /* +0x2c  DAT_002023cc */
    int32_t waterlevel;       /* +0x30  DAT_002023d0 */
    int32_t waterlevel2;      /* +0x34  DAT_002023d4 */
    trace_t groundTrace; /* +0x38 */
    float maxClipImpact;      /* +0x68  DAT_00202408 */
    vec3_t previousOrigin;    /* +0x6c  DAT_0020240c */
    vec3_t previousVelocity;  /* +0x78  DAT_00202418 */
    int32_t previousOverheadLevel; /* +0x84  DAT_00202424 */
    const weaponInfo_t *weaponInfo; /* +0x88  DAT_00202428 */
} pml_t;

typedef struct viewheight_lerp_key_s {
    int32_t percent;
    float height;
    int32_t originAdjust;
} viewheight_lerp_key_t;

typedef int (*weapon_parse_custom_t)(void *base, const char *value,
                                     int fieldType);
typedef void (*weapon_parse_copy_string_t)(char *dest, const char *src);

typedef struct bg_stock_item_seed_s {
    const char *classname;
    const char *pickupSound;
    const char *worldModel;
    const char *iconModel;
    const char *hudIcon;
    const char *ammoIcon;
    const char *pickupName;
    int32_t quantity;
    itemType_t type;
    int32_t weapon;
    int32_t ammoIndex;
    int32_t clipIndex;
} bg_stock_item_seed_t;

typedef struct bg_stock_item_seed_entry_s {
    int itemIndex;
    bg_stock_item_seed_t item;
} bg_stock_item_seed_entry_t;

typedef struct bg_weapon_sway_params_s {
    float maxAngle;
    float smoothRate;
    float pitchAngleScale;
    float yawAngleScale;
    float yawOffsetScale;
    float pitchOffsetScale;
} bg_weapon_sway_params_t;

#define GAME_TYPES_STATIC_ASSERT(name, condition) \
    typedef char game_types_static_assert_##name[(condition) ? 1 : -1]

#if UINTPTR_MAX == 0xffffffffu
GAME_TYPES_STATIC_ASSERT(trace_fraction_offset, offsetof(trace_t, fraction) == 0x00);
GAME_TYPES_STATIC_ASSERT(trace_endpos_offset, offsetof(trace_t, endpos) == 0x04);
GAME_TYPES_STATIC_ASSERT(trace_normal_offset, offsetof(trace_t, normal) == 0x10);
GAME_TYPES_STATIC_ASSERT(trace_surface_flags_offset,
                            offsetof(trace_t, surfaceFlags) == 0x1c);
GAME_TYPES_STATIC_ASSERT(trace_contents_offset, offsetof(trace_t, contents) == 0x20);
GAME_TYPES_STATIC_ASSERT(trace_material_offset, offsetof(trace_t, material) == 0x24);
GAME_TYPES_STATIC_ASSERT(trace_entity_num_offset, offsetof(trace_t, entityNum) == 0x28);
GAME_TYPES_STATIC_ASSERT(trace_part_name_offset, offsetof(trace_t, partName) == 0x2a);
GAME_TYPES_STATIC_ASSERT(trace_part_group_offset, offsetof(trace_t, partGroup) == 0x2c);
GAME_TYPES_STATIC_ASSERT(trace_allsolid_offset, offsetof(trace_t, allsolid) == 0x2e);
GAME_TYPES_STATIC_ASSERT(trace_startsolid_offset, offsetof(trace_t, startsolid) == 0x2f);
GAME_TYPES_STATIC_ASSERT(trace_size, sizeof(trace_t) == 0x30);
#endif

#endif /* GAME_TYPES_H */
