#ifndef CODUO_CM_WORLD_SECTOR_PRIVATE_H
#define CODUO_CM_WORLD_SECTOR_PRIVATE_H

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

#include "coduo_engine_structs.h"

extern vec3_t cm_worldMins;
extern vec3_t cm_worldMaxs;
extern worldSector_t cm_worldSectorRoot;
extern worldSector_t *cm_freeWorldSectors;
extern worldSector_t cm_nullWorldSector;
extern coduo_world_sector_pool_t cm_worldSectorPool;
extern coduo_host_entity_link_table_t sv_entityLinks;
extern cplane_t *cm_planes;
extern coduo_host_sv_prefix_t sv;
extern coduo_vm_t *sv_gameVM;
extern vec3_t sv_defaultEntityClipMins;
extern vec3_t sv_defaultEntityClipMaxs;

typedef struct worldSectorAreaLink_s {
    XModel *model;
    vec3_t origin;
    vec3_t inverseAxis[3];
    struct worldSectorAreaLink_s *nextInWorldSector;
    vec3_t linkMins;
    vec3_t linkMaxs;
    qboolean sightTraceEligible;
} worldSectorAreaLink_t;

#if UINTPTR_MAX == UINT32_MAX
_Static_assert(offsetof(worldSectorAreaLink_t, model) == 0x00,
               "worldSectorAreaLink_t.model offset");
_Static_assert(offsetof(worldSectorAreaLink_t, origin) == 0x04,
               "worldSectorAreaLink_t.origin offset");
_Static_assert(offsetof(worldSectorAreaLink_t, inverseAxis) == 0x10,
               "worldSectorAreaLink_t.inverseAxis offset");
_Static_assert(offsetof(worldSectorAreaLink_t, nextInWorldSector) ==
                   0x34,
               "worldSectorAreaLink_t.nextInWorldSector offset");
_Static_assert(offsetof(worldSectorAreaLink_t, linkMins) == 0x38,
               "worldSectorAreaLink_t.linkMins offset");
_Static_assert(offsetof(worldSectorAreaLink_t, linkMaxs) == 0x44,
               "worldSectorAreaLink_t.linkMaxs offset");
_Static_assert(
    offsetof(worldSectorAreaLink_t, sightTraceEligible) == 0x50,
    "worldSectorAreaLink_t.sightTraceEligible offset");
#endif

typedef struct coduo_area_entities_work_s {
    int32_t unk00;
    const float *mins;
    const float *maxs;
    int32_t *entityList;
    int32_t count;
    int32_t maxcount;
    int32_t contentMask;
} coduo_area_entities_work_t;

#if UINTPTR_MAX == UINT32_MAX
_Static_assert(offsetof(coduo_area_entities_work_t, mins) == 0x04,
               "coduo_area_entities_work_t.mins offset");
_Static_assert(offsetof(coduo_area_entities_work_t, maxs) == 0x08,
               "coduo_area_entities_work_t.maxs offset");
_Static_assert(offsetof(coduo_area_entities_work_t, entityList) == 0x0c,
               "coduo_area_entities_work_t.entityList offset");
_Static_assert(offsetof(coduo_area_entities_work_t, count) == 0x10,
               "coduo_area_entities_work_t.count offset");
_Static_assert(offsetof(coduo_area_entities_work_t, maxcount) == 0x14,
               "coduo_area_entities_work_t.maxcount offset");
_Static_assert(offsetof(coduo_area_entities_work_t, contentMask) == 0x18,
               "coduo_area_entities_work_t.contentMask offset");
_Static_assert(sizeof(coduo_area_entities_work_t) == 0x1c,
               "coduo_area_entities_work_t size");
#endif

typedef struct cmPointTraceStaticModelsWork_s {
    trace_t trace;
    int32_t contentsMask;
    vec3_t start;
    vec3_t end;
} cmPointTraceStaticModelsWork_t;

#if UINTPTR_MAX == UINT32_MAX
_Static_assert(offsetof(cmPointTraceStaticModelsWork_t, trace) == 0x00,
               "cmPointTraceStaticModelsWork_t.trace offset");
_Static_assert(offsetof(cmPointTraceStaticModelsWork_t, contentsMask) == 0x30,
               "cmPointTraceStaticModelsWork_t.contentsMask offset");
_Static_assert(offsetof(cmPointTraceStaticModelsWork_t, start) == 0x34,
               "cmPointTraceStaticModelsWork_t.start offset");
_Static_assert(offsetof(cmPointTraceStaticModelsWork_t, end) == 0x40,
               "cmPointTraceStaticModelsWork_t.end offset");
_Static_assert(sizeof(cmPointTraceStaticModelsWork_t) == 0x4c,
               "cmPointTraceStaticModelsWork_t size");
#endif

typedef struct cmSightTraceStaticModelsWork_s {
    int32_t contentsMask;
    vec3_t start;
    vec3_t end;
} cmSightTraceStaticModelsWork_t;

#if UINTPTR_MAX == UINT32_MAX
_Static_assert(offsetof(cmSightTraceStaticModelsWork_t, contentsMask) ==
                   0x00,
               "cmSightTraceStaticModelsWork_t.contentsMask offset");
_Static_assert(offsetof(cmSightTraceStaticModelsWork_t, start) == 0x04,
               "cmSightTraceStaticModelsWork_t.start offset");
_Static_assert(offsetof(cmSightTraceStaticModelsWork_t, end) == 0x10,
               "cmSightTraceStaticModelsWork_t.end offset");
_Static_assert(sizeof(cmSightTraceStaticModelsWork_t) == 0x1c,
               "cmSightTraceStaticModelsWork_t size");
#endif

void Com_DPrintf(const char *format, ...);
intptr_t VM_Call(coduo_vm_t *vm,
                               int32_t command, ...);
int32_t Q_rint(float value);
float RadiusFromBounds(const vec3_t mins, const vec3_t maxs);
int32_t CM_InlineModel(int32_t modelNum);
void CM_ModelBounds(int32_t model, vec3_t mins, vec3_t maxs);
int32_t CM_TempBoxModel(const vec3_t mins, const vec3_t maxs,
                                    int32_t contents,
                                    qboolean capsule);
int32_t
CM_BoxLeafnums(const vec3_t mins, const vec3_t maxs,
               int32_t *list,
               int32_t listSize,
               int32_t *lastLeaf);
int32_t CM_LeafArea(int32_t leafnum);
int32_t CM_LeafCluster(
    int32_t leafnum);
int32_t CM_PointLeafnum(const vec3_t point);
const uint8_t *CM_ClusterPVS(int32_t cluster);
qboolean CM_AreasConnected(int32_t area1,
                           int32_t area2);
void CM_AdjustAreaPortalState(int32_t area1,
                              int32_t area2,
                              qboolean open);
int32_t CM_PointContents(const vec3_t point,
                                       int32_t model);
int32_t
CM_TransformedPointContents(const vec3_t point, int32_t model,
                            const vec3_t origin, const vec3_t angles);
char *CM_EntityString(void);
void CM_TransformedBoxTrace(trace_t *trace,
                            const vec3_t start,
                            const vec3_t end,
                            const vec3_t mins,
                            const vec3_t maxs,
                            int32_t model,
                            int32_t brushMask,
                            const vec3_t origin,
                            const vec3_t angles,
                            qboolean capsule);
void CM_TransformedBoxTraceExternal(trace_t *trace,
                                    const vec3_t start,
                                    const vec3_t end,
                                    const vec3_t mins,
                                    const vec3_t maxs,
                                    int32_t model,
                                    int32_t brushMask,
                                    const vec3_t origin,
                                    const vec3_t angles,
                                    qboolean capsule);
void CM_BoxTrace(trace_t *trace,
                 const vec3_t start,
                 const vec3_t end,
                 const vec3_t mins,
                 const vec3_t maxs,
                 int32_t model,
                 int32_t brushMask,
                 qboolean capsule);
int32_t
CM_BoxSightTrace(int32_t result,
                 const vec3_t start,
                 const vec3_t end,
                 const vec3_t mins,
                 const vec3_t maxs,
                 int32_t model,
                 int32_t brushMask,
                 qboolean capsule);
int32_t
CM_TransformedBoxSightTrace(int32_t result,
                            const vec3_t start,
                            const vec3_t end,
                            const vec3_t mins,
                            const vec3_t maxs,
                            int32_t model,
                            int32_t brushMask,
                            const vec3_t origin,
                            const vec3_t angles,
                            qboolean capsule);
svEntity_t *
SV_SvEntityForGentity(const sharedEntity_t *gentity);
sharedEntity_t *SV_GentityNum(int32_t entityNum);
sharedEntity_t *
SV_GEntityForSvEntity(const svEntity_t *svEntity);
int32_t XModelGetContents(XModel *model);
void CM_TraceStaticModel(worldSectorAreaLink_t *areaLink,
                         trace_t *trace,
                         const vec3_t start,
                         const vec3_t end,
                         int32_t contentsMask);
void SV_ClipMoveToEntity(cmClipMoveWork_t *traceWork,
                         svEntity_t *svEntity);
int32_t
SV_SweptSightTraceThroughEntity(
    cmClipSightTraceWork_t *sightTraceWork,
    svEntity_t *svEntity);
void SV_SightTraceThroughEntity(
    cmPointTraceWork_t *sightTraceWork,
    svEntity_t *svEntity);
int32_t
SV_PointSightTraceThroughEntity(
    cmPointSightTraceWork_t *sightTraceWork,
    svEntity_t *svEntity);

worldSector_t *CM_AllocWorldSector(const float mins[2],
                                          const float maxs[2]);
void CM_InitWorldSector(void);
void FloodAreaConnections(void);
void SV_UnlinkEntityFromWorldSector(svEntity_t *svEntity);
void CM_RebucketWorldSectorLinks(worldSector_t *sector,
                                 const float mins[2],
                                 const float maxs[2]);
void SV_LinkEntityToWorldSector(svEntity_t *svEntity,
                                const vec3_t mins,
                                const vec3_t maxs);
int32_t SV_ClipHandleForEntity(
    const sharedEntity_t *entity);
void SV_LinkEntity(sharedEntity_t *gentity);
qboolean CM_IsBigStaticModel(const vec3_t mins, const vec3_t maxs);
void CM_LinkStaticModel(worldSectorAreaLink_t *areaLink);
void CM_AreaEntities_r(worldSector_t *sector,
                       coduo_area_entities_work_t *work);
int32_t
CM_AreaEntities(const vec3_t mins, const vec3_t maxs,
                int32_t *entityList,
                int32_t maxcount,
                int32_t contentMask);
qboolean CM_TraceLineSkipsBox(const vec3_t start,
                                    const vec3_t end,
                                    const vec3_t mins,
                                    const vec3_t maxs,
                                    float fraction);
void CM_PointTraceStaticModels_r(cmPointTraceStaticModelsWork_t *work,
                                 worldSector_t *sector,
                                 float startFraction,
                                 float endFraction,
                                 const vec3_t start,
                                 const vec3_t end);
void CM_PointTraceStaticModels(trace_t *trace,
                               const vec3_t start,
                               const vec3_t end,
                               int32_t contentsMask);
void SV_TraceEntities_r(cmClipMoveWork_t *traceWork,
                  worldSector_t *sector,
                  float startFraction,
                  float endFraction,
                  const vec3_t start,
                  const vec3_t end);
void SV_TraceEntities(cmClipMoveWork_t *traceWork);
int32_t
SV_SweptSightTraceEntities_r(cmClipSightTraceWork_t *sightTraceWork,
             worldSector_t *sector,
             float startFraction,
             float endFraction,
             const vec3_t start,
             const vec3_t end);
int32_t
SV_SweptSightTraceEntities(cmClipSightTraceWork_t *sightTraceWork);
void SV_SightTraceEntities_r(cmPointTraceWork_t *sightTraceWork,
                  worldSector_t *sector,
                  float startFraction,
                  float endFraction,
                  const vec3_t start,
                  const vec3_t end);
void SV_SightTraceEntities(cmPointTraceWork_t *sightTraceWork);
int32_t
SV_PointSightTraceEntities_r(cmPointSightTraceWork_t *sightTraceWork,
             worldSector_t *sector,
             float startFraction,
             float endFraction,
             const vec3_t start,
             const vec3_t end);
int32_t
SV_PointSightTraceEntities(cmPointSightTraceWork_t *sightTraceWork);
qboolean
CM_SightTraceStaticModels_r(cmSightTraceStaticModelsWork_t *work,
                            worldSector_t *sector,
                            float startFraction,
                            float endFraction,
                            const vec3_t start,
                            const vec3_t end);
qboolean CM_SightTraceStaticModels(const vec3_t start,
                                   const vec3_t end,
                                   int32_t contentsMask);
cplane_t *CM_PlaneForIndex(int32_t planeIndex);

/* NOT_FROM_ORIGINAL_SOURCE: typed world-sector store helper. */
static inline worldSector_t *
CM_StoreWorldSector(const worldSector_t *sector)
{
    return (worldSector_t *)sector;
}

/* NOT_FROM_ORIGINAL_SOURCE: typed world-sector load helper. */
static inline worldSector_t *
CM_LoadWorldSector(worldSector_t *sector)
{
    return sector;
}

/* NOT_FROM_ORIGINAL_SOURCE: typed entity-link store helper. */
static inline svEntity_t *
CM_StoreEntityLink(const svEntity_t *entityLink)
{
    return (svEntity_t *)entityLink;
}

/* NOT_FROM_ORIGINAL_SOURCE: typed entity-link load helper. */
static inline svEntity_t *
CM_LoadEntityLink(svEntity_t *link)
{
    return link;
}

/* NOT_FROM_ORIGINAL_SOURCE: typed area-link store helper. */
static inline worldSectorAreaLink_t *
CM_StoreAreaLink(const worldSectorAreaLink_t *link)
{
    return (worldSectorAreaLink_t *)link;
}

/* NOT_FROM_ORIGINAL_SOURCE: typed area-link load helper. */
static inline worldSectorAreaLink_t *
CM_LoadAreaLink(worldSectorAreaLink_t *link)
{
    return link;
}

#endif
