#include "cm_world_sector_private.h"

void CM_PointTraceStaticModels_r(cmPointTraceStaticModelsWork_t *work,
                                 worldSector_t *sector,
                                 float startFraction,
                                 float endFraction,
                                 const vec3_t start,
                                 const vec3_t end)
{
    /*
     * Stock's FCOMP/JAE gate continues when either fraction is unordered:
     * expressing the rejection directly as <= preserves that x87 path.
     */
    if (work->trace.fraction <= startFraction ||
        (work->contentsMask & sector->staticModelContentsMask) == 0) {
        return;
    }

    {
        const int32_t axis = sector->axis;
        const float startDistance = start[axis] - sector->dist;
        const float endDistance = end[axis] - sector->dist;

        /*
         * The original ordered JAE tests send unordered distances through
         * the split path.  Negating the ordered opposite comparisons keeps
         * that behavior without adding an emulation path.
         */
        if (!(startDistance >= 0.0f) || !(endDistance >= 0.0f)) {
            if (!(0.0f >= startDistance) || !(0.0f >= endDistance)) {
                const float splitFraction =
                    startDistance / (startDistance - endDistance);
                const float middleFraction =
                    startFraction +
                    (endFraction - startFraction) * splitFraction;
                vec3_t middle;
                uint32_t side;

                middle[0] = start[0] + (end[0] - start[0]) * splitFraction;
                middle[1] = start[1] + (end[1] - start[1]) * splitFraction;
                middle[2] = start[2] + (end[2] - start[2]) * splitFraction;

                side = (uint32_t)(startDistance < 0.0f);
                CM_PointTraceStaticModels_r(
                    work, CM_LoadWorldSector(sector->children[side]),
                    startFraction, middleFraction, start, middle);
                CM_PointTraceStaticModels_r(
                    work, CM_LoadWorldSector(sector->children[1 - side]),
                    middleFraction, endFraction, middle, end);
            } else {
                CM_PointTraceStaticModels_r(
                    work, CM_LoadWorldSector(sector->children[1]),
                    startFraction, endFraction, start, end);
            }
        } else {
            CM_PointTraceStaticModels_r(
                work, CM_LoadWorldSector(sector->children[0]),
                startFraction, endFraction, start, end);
        }

        for (worldSectorAreaLink_t *areaLink =
                 CM_LoadAreaLink(sector->staticModelLinkHead);
             areaLink != NULL;
             areaLink = CM_LoadAreaLink(areaLink->nextInWorldSector)) {
            if ((XModelGetContents(areaLink->model) & work->contentsMask) !=
                    0 &&
                CM_TraceLineSkipsBox(work->start,
                                     work->end,
                                     areaLink->linkMins,
                                     areaLink->linkMaxs,
                                     work->trace.fraction) == 0) {
                CM_TraceStaticModel(areaLink,
                                    &work->trace,
                                    work->start,
                                    work->end,
                                    work->contentsMask);
            }
        }
    }
}
