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

#include "cm_terrain_private.h"

void CM_TracePointThroughTerrainCollide(
    traceWork_t *traceWork,
    const patchCollide_t *patchCollide)
{
    float planeFractions[CM_PATCH_MAX_PLANES];
    qboolean planeSides[CM_PATCH_MAX_PLANES];

    if (cm_playerCurveClip->integer == 0 || traceWork->isPoint == 0) {
        return;
    }

    const patchPlane_t *plane = patchCollide->planes;
    for (int32_t planeIndex = 0; planeIndex < patchCollide->numPlanes;
         ++planeIndex, ++plane) {
        const float *offset = traceWork->offsets[plane->signbits];
#if EMULATE_X87
        /* x87 (DLL 0x0804f85b): planeOffset is a 3-term dot rounded to its own
         * float slot; each distance is then ONE 80-bit chain — the dot, the
         * -dist and the +planeOffset — rounded once at the store
         * (fmul;fmul;faddp;fmul;faddp;fsub dist;fadd planeOffset;fstp). */
        const float planeOffset = x87f_store_f32(x87f_add(
            x87f_add(x87f_mul(x87f_load_f32(offset[0]),
                              x87f_load_f32(plane->normal[0])),
                     x87f_mul(x87f_load_f32(offset[1]),
                              x87f_load_f32(plane->normal[1]))),
            x87f_mul(x87f_load_f32(offset[2]),
                     x87f_load_f32(plane->normal[2]))));
        const float startDistance = x87f_store_f32(x87f_add(
            x87f_sub(
                x87f_add(x87f_add(x87f_mul(x87f_load_f32(traceWork->start[0]),
                                           x87f_load_f32(plane->normal[0])),
                                  x87f_mul(x87f_load_f32(traceWork->start[1]),
                                           x87f_load_f32(plane->normal[1]))),
                         x87f_mul(x87f_load_f32(traceWork->start[2]),
                                  x87f_load_f32(plane->normal[2]))),
                x87f_load_f32(plane->dist)),
            x87f_load_f32(planeOffset)));
        const float endDistance = x87f_store_f32(x87f_add(
            x87f_sub(
                x87f_add(x87f_add(x87f_mul(x87f_load_f32(traceWork->end[0]),
                                           x87f_load_f32(plane->normal[0])),
                                  x87f_mul(x87f_load_f32(traceWork->end[1]),
                                           x87f_load_f32(plane->normal[1]))),
                         x87f_mul(x87f_load_f32(traceWork->end[2]),
                                  x87f_load_f32(plane->normal[2]))),
                x87f_load_f32(plane->dist)),
            x87f_load_f32(planeOffset)));
#else
        const float planeOffset =
            ((offset[0] * plane->normal[0]) +
             (offset[1] * plane->normal[1])) +
            (offset[2] * plane->normal[2]);
        const float startDistance =
            (((traceWork->start[0] * plane->normal[0]) +
              (traceWork->start[1] * plane->normal[1])) +
             (traceWork->start[2] * plane->normal[2])) -
            plane->dist + planeOffset;
        const float endDistance =
            (((traceWork->end[0] * plane->normal[0]) +
              (traceWork->end[1] * plane->normal[1])) +
             (traceWork->end[2] * plane->normal[2])) -
            plane->dist + planeOffset;
#endif

        if (startDistance <= 0.0f) {
            planeSides[planeIndex] = 0;
        } else {
            planeSides[planeIndex] = 1;
        }

        if (startDistance == endDistance) {
            planeFractions[planeIndex] = 99999.0f;
        } else {
#if EMULATE_X87
            planeFractions[planeIndex] = x87f_store_f32(
                x87f_div(x87f_load_f32(startDistance),
                         x87f_sub(x87f_load_f32(startDistance),
                                  x87f_load_f32(endDistance))));
#else
            planeFractions[planeIndex] =
                startDistance / (startDistance - endDistance);
#endif
            if (planeFractions[planeIndex] <= 0.0f) {
                planeFractions[planeIndex] = 99999.0f;
            }
        }
    }

    const facet_t *facet = patchCollide->facets;
    for (int32_t facetIndex = 0;
         facetIndex < patchCollide->numFacets; ++facetIndex, ++facet) {
        const int32_t rootPlaneIndex = facet->surfacePlane;

        if (planeSides[rootPlaneIndex] == 0) {
            continue;
        }

        const float rootFraction = planeFractions[rootPlaneIndex];
        if (rootFraction < 0.0f || traceWork->trace.fraction < rootFraction) {
            continue;
        }

        int32_t childIndex;
        for (childIndex = 0; childIndex < facet->numBorders;
             ++childIndex) {
            const int32_t childPlaneIndex =
                facet->borderPlanes[childIndex];

            if (planeSides[childPlaneIndex] ==
                facet->borderInward[childIndex]) {
                if (planeFractions[childPlaneIndex] < rootFraction) {
                    break;
                }
            } else if (rootFraction < planeFractions[childPlaneIndex]) {
                break;
            }
        }

        if (childIndex == facet->numBorders) {
            plane = &patchCollide->planes[rootPlaneIndex];

            const float *offset = traceWork->offsets[plane->signbits];
#if EMULATE_X87
            /* Same structure as the per-plane pass above; this is the site that
             * actually sets trace.fraction for curved terrain. */
            const float planeOffset = x87f_store_f32(x87f_add(
                x87f_add(x87f_mul(x87f_load_f32(offset[0]),
                                  x87f_load_f32(plane->normal[0])),
                         x87f_mul(x87f_load_f32(offset[1]),
                                  x87f_load_f32(plane->normal[1]))),
                x87f_mul(x87f_load_f32(offset[2]),
                         x87f_load_f32(plane->normal[2]))));
            const float startDistance = x87f_store_f32(x87f_add(
                x87f_sub(
                    x87f_add(
                        x87f_add(x87f_mul(x87f_load_f32(traceWork->start[0]),
                                          x87f_load_f32(plane->normal[0])),
                                 x87f_mul(x87f_load_f32(traceWork->start[1]),
                                          x87f_load_f32(plane->normal[1]))),
                        x87f_mul(x87f_load_f32(traceWork->start[2]),
                                 x87f_load_f32(plane->normal[2]))),
                    x87f_load_f32(plane->dist)),
                x87f_load_f32(planeOffset)));
            const float endDistance = x87f_store_f32(x87f_add(
                x87f_sub(
                    x87f_add(
                        x87f_add(x87f_mul(x87f_load_f32(traceWork->end[0]),
                                          x87f_load_f32(plane->normal[0])),
                                 x87f_mul(x87f_load_f32(traceWork->end[1]),
                                          x87f_load_f32(plane->normal[1]))),
                        x87f_mul(x87f_load_f32(traceWork->end[2]),
                                 x87f_load_f32(plane->normal[2]))),
                    x87f_load_f32(plane->dist)),
                x87f_load_f32(planeOffset)));

            traceWork->trace.fraction = x87f_store_f32(x87f_div(
                x87f_sub(x87f_load_f32(startDistance), x87f_load_f32(0.125f)),
                x87f_sub(x87f_load_f32(startDistance),
                         x87f_load_f32(endDistance))));
#else
            const float planeOffset =
                ((offset[0] * plane->normal[0]) +
                 (offset[1] * plane->normal[1])) +
                (offset[2] * plane->normal[2]);
            const float startDistance =
                (((traceWork->start[0] * plane->normal[0]) +
                  (traceWork->start[1] * plane->normal[1])) +
                 (traceWork->start[2] * plane->normal[2])) -
                plane->dist + planeOffset;
            const float endDistance =
                (((traceWork->end[0] * plane->normal[0]) +
                  (traceWork->end[1] * plane->normal[1])) +
                 (traceWork->end[2] * plane->normal[2])) -
                plane->dist + planeOffset;

            traceWork->trace.fraction =
                (startDistance - 0.125f) / (startDistance - endDistance);
#endif
            if (traceWork->trace.fraction < 0.0f) {
                traceWork->trace.fraction = 0.0f;
            }

            traceWork->trace.normal[0] = plane->normal[0];
            traceWork->trace.normal[1] = plane->normal[1];
            traceWork->trace.normal[2] = plane->normal[2];
        }
    }
}
