#ifndef CODUO_CM_PATCH_PRIVATE_H
#define CODUO_CM_PATCH_PRIVATE_H

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

#include "cm_trace_core_private.h"

#define CODUO_PATCH_POINT_ENTER_EPSILON 0.125f
#define CODUO_PATCH_BARYCENTRIC_MIN -0.001f
#define CODUO_PATCH_BARYCENTRIC_MAX 1.001f

typedef struct coduo_patch_collision_plane_s {
    vec3_t normal;
    float dist;
} coduo_patch_collision_plane_t;

typedef struct coduo_patch_vertex_sphere_s {
    int32_t checkcount;
    vec3_t origin;
} coduo_patch_vertex_sphere_t;

typedef struct coduo_patch_edge_cylinder_s {
    int32_t checkcount;
    vec3_t origin;
    vec3_t radialAxes[2];
    vec3_t axis;
    float length;
} coduo_patch_edge_cylinder_t;

typedef struct coduo_patch_collision_facet_s {
    coduo_patch_collision_plane_t surfacePlane;
    coduo_patch_collision_plane_t edgePlanes[2];
    coduo_patch_vertex_sphere_t *vertexSpheres[3];
    coduo_patch_edge_cylinder_t *edgeCylinders[3];
} coduo_patch_collision_facet_t;

typedef struct coduo_patch_collide_s {
    uint16_t facetCount;
    uint8_t capsuleOffsetSign;
    uint8_t unk03;
    coduo_patch_collision_facet_t facets[];
} coduo_patch_collide_t;

_Static_assert(sizeof(coduo_patch_collision_plane_t) == 0x10,
               "coduo_patch_collision_plane_t size mismatch");
_Static_assert(offsetof(coduo_patch_collision_plane_t, normal) == 0x00,
               "coduo_patch_collision_plane_t.normal mismatch");
_Static_assert(offsetof(coduo_patch_collision_plane_t, dist) == 0x0c,
               "coduo_patch_collision_plane_t.dist mismatch");
#if UINTPTR_MAX == UINT32_MAX
_Static_assert(offsetof(coduo_patch_vertex_sphere_t, checkcount) == 0x00,
               "coduo_patch_vertex_sphere_t.checkcount mismatch");
_Static_assert(offsetof(coduo_patch_vertex_sphere_t, origin) == 0x04,
               "coduo_patch_vertex_sphere_t.origin mismatch");
_Static_assert(offsetof(coduo_patch_edge_cylinder_t, checkcount) == 0x00,
               "coduo_patch_edge_cylinder_t.checkcount mismatch");
_Static_assert(offsetof(coduo_patch_edge_cylinder_t, origin) == 0x04,
               "coduo_patch_edge_cylinder_t.origin mismatch");
_Static_assert(offsetof(coduo_patch_edge_cylinder_t, radialAxes) == 0x10,
               "coduo_patch_edge_cylinder_t.radialAxes mismatch");
_Static_assert(offsetof(coduo_patch_edge_cylinder_t, axis) == 0x28,
               "coduo_patch_edge_cylinder_t.axis mismatch");
_Static_assert(offsetof(coduo_patch_edge_cylinder_t, length) == 0x34,
               "coduo_patch_edge_cylinder_t.length mismatch");
_Static_assert(sizeof(coduo_patch_collision_facet_t) == 0x48,
               "coduo_patch_collision_facet_t size mismatch");
#endif
_Static_assert(offsetof(coduo_patch_collision_facet_t, surfacePlane) == 0x00,
               "coduo_patch_collision_facet_t.surfacePlane mismatch");
_Static_assert(offsetof(coduo_patch_collision_facet_t, edgePlanes) == 0x10,
               "coduo_patch_collision_facet_t.edgePlanes mismatch");
#if UINTPTR_MAX == UINT32_MAX
_Static_assert(offsetof(coduo_patch_collision_facet_t, vertexSpheres) == 0x30,
               "coduo_patch_collision_facet_t.vertexSpheres mismatch");
_Static_assert(offsetof(coduo_patch_collision_facet_t, edgeCylinders) == 0x3c,
               "coduo_patch_collision_facet_t.edgeCylinders mismatch");
#endif
_Static_assert(offsetof(coduo_patch_collide_t, facetCount) == 0x00,
               "coduo_patch_collide_t.facetCount mismatch");
_Static_assert(offsetof(coduo_patch_collide_t, capsuleOffsetSign) == 0x02,
               "coduo_patch_collide_t.capsuleOffsetSign mismatch");
#if UINTPTR_MAX == UINT32_MAX
_Static_assert(offsetof(coduo_patch_collide_t, facets) == 0x04,
               "coduo_patch_collide_t.facets mismatch");
#endif

void CM_TracePointThroughPatchCollide(traceWork_t *traceWork,
                                      const coduo_patch_collide_t *patchCollide);
void CM_TraceCapsuleThroughPatchCollide(
    traceWork_t *traceWork,
    const coduo_patch_collide_t *patchCollide);
void CM_TraceBoxThroughPatchCollide(traceWork_t *traceWork,
                                    const coduo_patch_collide_t *patchCollide);
void CM_TraceThroughPatchCollide(traceWork_t *traceWork,
                                 const coduo_patch_collide_t *patchCollide);
coduo_patch_collide_t *CM_GenerateTerrainCollide(
    int32_t indexCount, const int16_t *indices, uint32_t vertexCount,
    const vec3_t *vertices, vec3_t *bounds);
qboolean CM_SightTraceThroughPatchCollide(
    const traceWork_t *traceWork,
    const coduo_patch_collide_t *patchCollide);
qboolean
CM_TestCapsuleInPatchCollide(traceWork_t *traceWork,
                             const coduo_patch_collide_t *patchCollide);
qboolean CM_PositionTestInPatchCollide(traceWork_t *traceWork,
                                     const coduo_patch_collide_t *patchCollide);
int32_t CM_LittleShort(int16_t value);
uint32_t CM_LittleLong(uint32_t value);
long double CM_LittleFloat(float value);
void CM_LoadStaticModels(void);

#ifdef CODUO_COLLISION_DIGEST
/* Collision-parity harness only (see cm_collision_digest.c) — not part of the
 * reconstruction. coduo_engine_digest_terrain_collide lives in cm_patch_grid_helpers.c
 * because the curved-patch build struct is private to that file. */
void coduo_engine_collision_digest_bytes_external(const void *data, size_t length, uint64_t *accum);
void coduo_engine_digest_terrain_collide(const void *terrainCollide, uint64_t *accum,
                             int32_t *planeCountOut, int32_t *facetCountOut);
#endif

#endif
