#include "cm_leaf_queries_private.h"
#include "cm_trace_core_private.h"
#include "cm_world_sector_private.h"
#include "coduo_x87emu.h" /* defines EMULATE_X87; x87 shim when it is 1 */

int32_t CM_PointContents(const vec3_t point,
                                       int32_t model)
{
    const coduo_collision_leaf_t *leaf;
    int32_t contents;

    if (model != 0) {
        const coduo_collision_model_t *cmod = CM_ClipHandleToModel(model);
        leaf = &cmod->leaf;
    } else {
        const int32_t leafnum = CM_PointLeafnum_r(point, 0);
        leaf = &cm_leafs[leafnum];
    }

    contents = 0;

    for (int32_t leafBrushIndex = 0;
         leafBrushIndex < (int32_t)leaf->numLeafBrushes; ++leafBrushIndex) {
        const int32_t brushIndex =
            cm_leafbrushes[leaf->firstLeafBrush + leafBrushIndex];
        const coduo_collision_brush_t *brush = &cm_brushes[brushIndex];
        qboolean outside = 0;

        for (int32_t axis = 0; axis <= 2; ++axis) {
            if (point[axis] < brush->mins[axis] ||
                brush->maxs[axis] < point[axis]) {
                outside = 1;
                break;
            }
        }

        if (outside) {
            continue;
        }

        const coduo_collision_brush_side_t *side = brush->sides;

        for (int32_t sideCount = brush->numSides; sideCount != 0;
             --sideCount, ++side) {
            const cplane_t *plane = side->plane;
            /* Stock keeps the dot product in st(0) and compares it against
             * plane->dist unrounded (0x80576c8..0x80576fa: no fstp DWORD) ->
             * 80-bit dot, full-width compare. */
#if EMULATE_X87
            x87f distance = x87f_add(x87f_add(
                x87f_mul(x87f_load_f32(point[0]), x87f_load_f32(plane->normal[0])),
                x87f_mul(x87f_load_f32(point[1]), x87f_load_f32(plane->normal[1]))),
                x87f_mul(x87f_load_f32(point[2]), x87f_load_f32(plane->normal[2])));

            if (x87f_lt(x87f_load_f32(plane->dist), distance)) {
                outside = 1;
                break;
            }
#else
            long double distance;

            distance = ((point[0] * plane->normal[0]) +
                        (point[1] * plane->normal[1])) +
                       (point[2] * plane->normal[2]);

            if (plane->dist < distance) {
                outside = 1;
                break;
            }
#endif
        }

        if (!outside) {
            contents |= brush->contents;
        }
    }

    return contents;
}

int32_t SV_PointContents(const vec3_t point,
                                       int32_t passEntityNum,
                                       int32_t contentMask)
{
    int32_t contents = CM_PointContents(point, 0);
    int32_t entityList[CODUO_MAX_GENTITIES];
    int32_t entityCount =
        CM_AreaEntities(point, point, entityList, CODUO_MAX_GENTITIES,
                        contentMask);

    for (int32_t entityIndex = 0; entityIndex < entityCount; entityIndex++) {
        int32_t entityNum = entityList[entityIndex];

        if (entityNum == passEntityNum) {
            continue;
        }

        sharedEntity_t *entity = SV_GentityNum(entityNum);
        int32_t clipHandle = SV_ClipHandleForEntity(entity);

        contents |= CM_TransformedPointContents(point, clipHandle,
                                                entity->currentOrigin,
                                                entity->currentAngles);
    }

    return contents & contentMask;
}
