#include "cm_world_sector_private.h"

int32_t
SV_SweptSightTraceEntities_r(cmClipSightTraceWork_t *sightTraceWork,
             worldSector_t *sector,
             float startFraction,
             float endFraction,
             const vec3_t start,
             const vec3_t end)
{
    int32_t hit;

    if ((sightTraceWork->contentsMask & sector->entityContentsMask) == 0) {
        return 0;
    }

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

    if (startDistance >= offset && endDistance >= offset) {
        hit = SV_SweptSightTraceEntities_r(sightTraceWork,
                           CM_LoadWorldSector(sector->children[0]),
                           startFraction,
                           endFraction,
                           start,
                           end);
    } else if (startDistance <= -offset && endDistance <= -offset) {
        hit = SV_SweptSightTraceEntities_r(sightTraceWork,
                           CM_LoadWorldSector(sector->children[1]),
                           startFraction,
                           endFraction,
                           start,
                           end);
    } else {
        int32_t side;
        float firstFraction;
        float secondFraction;

        if (startDistance < endDistance) {
            const float inverseDistance =
                1.0f / (startDistance - endDistance);

            side = 1;
            firstFraction = (startDistance + offset) * inverseDistance;
            secondFraction = (startDistance - offset) * inverseDistance;
        } else if (endDistance < startDistance) {
            const float inverseDistance =
                1.0f / (startDistance - endDistance);

            side = 0;
            firstFraction = (startDistance - offset) * inverseDistance;
            secondFraction = (startDistance + offset) * inverseDistance;
        } else {
            side = 0;
            firstFraction = 0.0f;
            secondFraction = 1.0f;
        }

        if (secondFraction > 1.0f) {
            secondFraction = 1.0f;
        }

        const float secondMidFraction =
            startFraction +
            (endFraction - startFraction) * secondFraction;
        vec3_t middle;

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

        hit = SV_SweptSightTraceEntities_r(sightTraceWork,
                           CM_LoadWorldSector(sector->children[side]),
                           startFraction,
                           secondMidFraction,
                           start,
                           middle);
        if (hit != 0) {
            return hit;
        }

        if (firstFraction < 0.0f) {
            firstFraction = 0.0f;
        }

        const float firstMidFraction =
            startFraction + (endFraction - startFraction) * firstFraction;

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

        hit = SV_SweptSightTraceEntities_r(sightTraceWork,
                           CM_LoadWorldSector(sector->children[1 - side]),
                           firstMidFraction,
                           endFraction,
                           middle,
                           end);
    }

    if (hit != 0) {
        return hit;
    }

    for (svEntity_t *svEntity =
             CM_LoadEntityLink(sector->entityLinkHead);
         svEntity != NULL;
         svEntity = CM_LoadEntityLink(svEntity->nextInWorldSector)) {
        hit = SV_SweptSightTraceThroughEntity(sightTraceWork, svEntity);
        if (hit != 0) {
            return hit;
        }
    }

    return 0;
}
