#ifndef CODUO_CM_TERRAIN_PRIVATE_H
#define CODUO_CM_TERRAIN_PRIVATE_H

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

#include "cm_trace_core_private.h"

enum {
    CM_PATCH_MAX_PLANES = 4096,
    CM_PATCH_FACET_BORDER_LIMIT = 26
};

/* Inherited patch-collision records shared by the construction and trace
 * paths. The Linux builder writes these exact fields at
 * 0x0804ec66..0x0804f52d; terrain tracing reads the same records at
 * 0x0804f85b..0x08051408. */
typedef struct patchPlane_s {
    vec3_t normal;
    float dist;
    uint32_t signbits;
} patchPlane_t;

typedef struct facet_s {
    int32_t surfacePlane;
    int32_t numBorders;
    int32_t borderPlanes[CM_PATCH_FACET_BORDER_LIMIT];
    qboolean borderInward[CM_PATCH_FACET_BORDER_LIMIT];
    qboolean borderNoAdjust[CM_PATCH_FACET_BORDER_LIMIT];
} facet_t;

struct patchCollide_s {
    int32_t numPlanes;
    patchPlane_t *planes;
    int32_t numFacets;
    facet_t *facets;
};

_Static_assert(sizeof(patchPlane_t) == 0x14,
               "patchPlane_t size mismatch");
_Static_assert(offsetof(patchPlane_t, signbits) == 0x10,
               "patchPlane_t.signbits mismatch");
_Static_assert(sizeof(facet_t) == 0x140,
               "facet_t size mismatch");
_Static_assert(offsetof(facet_t, borderPlanes) == 0x08,
               "facet_t.borderPlanes mismatch");
_Static_assert(offsetof(facet_t, borderInward) == 0x70,
               "facet_t.borderInward mismatch");
_Static_assert(offsetof(facet_t, borderNoAdjust) == 0xd8,
               "facet_t.borderNoAdjust mismatch");

#if UINTPTR_MAX == UINT32_MAX
_Static_assert(offsetof(patchCollide_t, numPlanes) == 0x00,
               "patchCollide_t.numPlanes mismatch");
_Static_assert(offsetof(patchCollide_t, planes) == 0x04,
               "patchCollide_t.planes mismatch");
_Static_assert(offsetof(patchCollide_t, numFacets) == 0x08,
               "patchCollide_t.numFacets mismatch");
_Static_assert(offsetof(patchCollide_t, facets) == 0x0c,
               "patchCollide_t.facets mismatch");
_Static_assert(sizeof(patchCollide_t) == 0x10,
               "patchCollide_t size mismatch");
#endif

extern cvar_t *cm_playerCurveClip;

qboolean CM_TerrainTraceThroughPlane(const vec4_t plane,
                                           const vec3_t start,
                                           const vec3_t end,
                                           float *enterFraction,
                                           float *leaveFraction,
                                           int32_t *enterPlaneChanged);
void CM_TracePointThroughTerrainCollide(
    traceWork_t *traceWork,
    const patchCollide_t *patchCollide);
qboolean CM_SightTracePointThroughTerrainCollide(
    const traceWork_t *traceWork,
    const patchCollide_t *patchCollide);
void CM_TraceThroughTerrainCollide(
    traceWork_t *traceWork,
    const patchCollide_t *patchCollide);
patchCollide_t *
CM_GeneratePatchCollide(uint32_t width, uint32_t height, int32_t maxError,
                        const vec3_t *points, vec3_t *bounds);
qboolean CM_SightTraceThroughTerrainCollide(
    const traceWork_t *traceWork,
    const patchCollide_t *patchCollide);
qboolean CM_PositionTestInTerrainCollide(
    const traceWork_t *traceWork,
    const patchCollide_t *patchCollide);

#endif
