#ifndef CODUO_XANIM_PRIVATE_H
#define CODUO_XANIM_PRIVATE_H

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

#include "coduo_engine_structs.h"

#if UINTPTR_MAX > UINT32_MAX
void coduo_engine_script_anim_release_unresolved_ref_sidecars(void);
#endif

typedef void *(*coduo_xanim_alloc_callback_t)(size_t size);
typedef void *(*coduo_xanim_load_alloc_callback_t)(size_t size);

enum {
    XANIM_NOTIFY_QUEUE_CAPACITY = 128
};

/* NOT_FROM_ORIGINAL_SOURCE: byte-addressed access to the packed runtime-tree
 * remap caches. Stock i386 computes each table width as 3 * nodeCount + 1
 * bytes (DObjConstructRecord 0x080c66ee..0x080c6712), so the second table is
 * deliberately odd-aligned when nodeCount is even and the original performs
 * unaligned 16-bit loads/stores. Keep the exact packed bytes on every target;
 * memcpy supplies the same word value without requiring native unaligned
 * access. */
static inline uint8_t *
coduo_engine_xanim_part_remap_table_bytes(XAnimTree *runtimeTree,
                                          int32_t selector)
{
    size_t nodeCount = runtimeTree->sourceTree->nodeCount;
    uint8_t *tailBytes =
        (uint8_t *)&runtimeTree->poolNodeHandles[nodeCount];

    return &tailBytes[(size_t)selector * (nodeCount * 3U + 1U)];
}

/* NOT_FROM_ORIGINAL_SOURCE: unaligned-load adapter described above. */
static inline uint16_t
coduo_engine_xanim_part_remap_handle_load(const uint8_t *tableBytes,
                                          size_t nodeIndex)
{
    uint16_t handle;

    memcpy(&handle, &tableBytes[nodeIndex * sizeof(handle)], sizeof(handle));
    return handle;
}

/* NOT_FROM_ORIGINAL_SOURCE: unaligned-store adapter described above. */
static inline void
coduo_engine_xanim_part_remap_handle_store(uint8_t *tableBytes,
                                           size_t nodeIndex,
                                           uint16_t handle)
{
    memcpy(&tableBytes[nodeIndex * sizeof(handle)], &handle, sizeof(handle));
}

/* NOT_FROM_ORIGINAL_SOURCE: packed-tail accessor described above. */
static inline uint8_t *
coduo_engine_xanim_part_remap_generation_bytes(uint8_t *tableBytes,
                                               size_t nodeCount)
{
    return &tableBytes[nodeCount * sizeof(uint16_t)];
}

extern uint16_t xanimDefaultPartRemapHandle;
extern int32_t xanim_activePoolPayloadSlot;
extern DObj *xanim_currentEvalState;
extern XAnimTree *xanim_currentTree;
extern int32_t xanim_deferredNotifyCount;
extern xanim_deferred_notify_t
    xanim_deferredNotifies[XANIM_NOTIFY_QUEUE_CAPACITY];
extern uint16_t xanim_endNotifyHandle;
extern int32_t xanim_evalChildCount;
extern coduo_xanim_eval_child_ref_t *xanim_evalChildRefs;
extern uint16_t xanim_evalCurrentFrame;
extern float xanim_evalCurrentTime;
extern uint8_t xanim_evalLeafOutputMode;
extern coduo_xanim_part_bitset_t xanim_evalPartBits;
extern int32_t xanim_evalPartBytes;
extern int32_t xanim_evalPartCount;
extern int32_t xanim_evalPoolWeightSelector;
extern uint16_t xanim_evalRootHandle;
extern coduo_xanim_part_bitset_t xanim_evalSkipBits;
extern uint16_t xanim_evalStartFrame;
extern float xanim_evalStartTime;
extern float xanim_evalTime;
extern float xanim_evalTimeStep;
extern uint16_t xanim_evalWindowFrame;
extern float xanim_evalWindowTime;
extern coduo_xanim_pool_t xanim_pool;
extern int32_t xanim_poolHighWaterCount;
extern int32_t xanim_poolUsedCount;
extern uint16_t xanim_rootTreeHandle;
extern qboolean xmodel_enforceExist;

void ReadQuat(const int16_t *packed,
              xanim_int16_vec4_t *out);
void XAnimLoadFile(const char *animName,
                   coduo_xanim_load_alloc_callback_t alloc);
int32_t XAnimFindByteKey(float frameFrac, const uint8_t *keys,
                         int32_t keyCount, int32_t targetKey);
int32_t XAnimFindShortKey(float frameFrac, const uint16_t *keys,
                          int32_t keyCount, int32_t targetKey);
int32_t XAnimSignExtendShort(int16_t value);
XAnim *XAnimAllocTree(const char *name,
                                   uint32_t nodeCount,
                                   coduo_xanim_alloc_callback_t allocCallback);
void XAnimSetLeafNode(XAnim *tree,
                      uint16_t nodeIndex,
                      const char *animName);
void XAnimSetParentNode(XAnim *tree,
                        uint16_t nodeIndex,
                        const char *unusedName,
                        uint16_t firstChildIndex,
                        uint16_t childCount,
                        uint16_t flags);
XAnimTree *
XAnimAllocRuntimeTree(XAnim *sourceTree,
                      void *(*allocCallback)(size_t size));
void XAnimCopyRuntimeTree(XAnimTree *runtimeTree,
                          void (*copyCallback)(void *data, size_t size));
void XAnimInit(void);
void XAnimInitSmallTreePool(void);
void XAnimShutdownSmallTreePool(void);
void XAnimShutdown(void);
void XModelEnforceExist(qboolean enforce);
void XAnimSetUser(int32_t activeSlot);
qboolean XAnimHasTime(XAnim *tree, int32_t animIndex);
float XAnimGetAverageRateFrequency(uint32_t nodeIndex);
qboolean XAnimUpdateInfoNoWeightClient(uint32_t nodeIndex);
void XAnimUpdateClientInfoSyncInternal(uint32_t nodeIndex, qboolean notify);
void XAnimUpdateServerInfoSyncInternal(uint32_t nodeIndex);
void XAnimUpdateClientInfoInternal(uint32_t nodeIndex, float delta,
                                   qboolean notify);
float XAnimFindServerNoteTrack(uint32_t nodeIndex, float delta);
void XAnimUpdateServerInfoInternal(uint32_t nodeIndex, float delta,
                                   qboolean notify);
void XAnimUpdateOldServerTime(uint32_t nodeIndex);
qboolean XAnimUpdateOldServerTimeNoWeight(uint32_t nodeIndex);
void DObjInitServerTime(DObj *state,
                        float serverTime);
qboolean DObjUpdateServerInfo(DObj *state,
                              float serverTime,
                              qboolean notify);
void DObjCalcAnim(DObj *state,
                  const uint32_t *partBits);
void XAnimCalc(uint32_t nodeIndex, float weight,
               DObjAnimMat *part, qboolean clear,
               qboolean normalize);
void XAnimClearTree(XAnimTree *runtimeTree);
void XAnimClearServerNotify(XAnimInfo *node);
void XAnimClearServerInfo(XAnimInfo *node);
void XAnimFreeInfo(XAnimTree *runtimeTree,
                       uint16_t handle);
uint16_t XAnimGetNextNotifyTime(XAnimEntry *entry,
                                  XAnimInfo *node,
                                  float time);
void XAnimClearData(DObjAnimMat *part);
void XAnimCalcRelDeltaParts(
    XAnimParts *loadedRecord, float weight,
    float *accumulatedDelta, float startTime, float endTime);
void XAnimCalcAbsDeltaParts(
    XAnimParts *loadedRecord, float weight,
    float *delta, float time);
void XAnimCalcPartsSmallIndices(XAnimParts *loadedRecord,
                                const uint8_t *targetToSourcePart,
                                float time, float weight,
                                DObjAnimMat *part);
void XAnimCalcPartsLargeIndices(
    XAnimParts *loadedRecord,
    const uint8_t *targetToSourcePart, float time,
    float weight, DObjAnimMat *part);
void XAnimCalcNonLoopEnd(
    XAnimParts *loadedRecord,
    const uint8_t *targetToSourcePart, float weight,
    DObjAnimMat *part);
void XAnimSetupSyncNodes(XAnim *tree);
void SetAnimCheck(int32_t animCheckEnabled);
XAnim *Scr_GetAnims(uint32_t treeIndex);
XAnim *
XAnimRuntimeTreeSourceTree(XAnimTree *runtimeTree);
const char *XAnimGetAnimName(XAnim *tree,
                             int32_t animIndex);
const char *XAnimGetAnimTreeDebugName(XAnim *tree);
/* NOT_FROM_ORIGINAL_SOURCE: typed accessor for the recovered DObj layout. */
XAnimTree *coduo_engine_xanim_get_runtime_tree(DObj *state);

#endif
