#include "cm_trace_core_private.h"

int32_t
CM_SightTraceThroughBrush(const traceWork_t *traceWork,
                          const coduo_collision_brush_t *brush)
{
    float enterFraction = 0.0f;
    float leaveFraction = 1.0f;
    float sign = -1.0f;
    const float *brushBounds = brush->mins;
    qboolean maxsPass = 0;

    if (traceWork->sphere.use != 0) {
        for (;;) {
            for (int32_t axis = 0; axis <= 2; ++axis) {
                const float startDistance =
                    (traceWork->start[axis] - brushBounds[axis]) * sign -
                    traceWork->sphereExtents[axis];
                const float endDistance =
                    (traceWork->end[axis] - brushBounds[axis]) * sign -
                    traceWork->sphereExtents[axis];

                if (startDistance > 0.0f) {
                    const float fractionDelta =
                        startDistance - endDistance;

                    if (endDistance > 0.0f) {
                        return 0;
                    }

                    if (enterFraction * fractionDelta < startDistance) {
                        enterFraction = startDistance / fractionDelta;
                        if (leaveFraction <= enterFraction) {
                            return 0;
                        }
                    }
                } else if (endDistance > 0.0f) {
                    const float fractionDelta =
                        startDistance - endDistance;

                    if (leaveFraction * fractionDelta < startDistance) {
                        leaveFraction = startDistance / fractionDelta;
                        if (leaveFraction <= enterFraction) {
                            return 0;
                        }
                    }
                }
            }

            if (maxsPass != 0) {
                break;
            }

            sign = 1.0f;
            brushBounds = brush->maxs;
            maxsPass = 1;
        }

        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;
            const float planeDist = plane->dist + traceWork->sphere.radius;
            const float sphereOffset =
                ((plane->normal[0] * traceWork->sphere.offset[0]) +
                 (plane->normal[1] * traceWork->sphere.offset[1])) +
                (plane->normal[2] * traceWork->sphere.offset[2]);
            vec3_t start;
            vec3_t end;

            if (sphereOffset > 0.0f) {
                start[0] =
                    traceWork->start[0] - traceWork->sphere.offset[0];
                start[1] =
                    traceWork->start[1] - traceWork->sphere.offset[1];
                start[2] =
                    traceWork->start[2] - traceWork->sphere.offset[2];
                end[0] = traceWork->end[0] - traceWork->sphere.offset[0];
                end[1] = traceWork->end[1] - traceWork->sphere.offset[1];
                end[2] = traceWork->end[2] - traceWork->sphere.offset[2];
            } else {
                start[0] =
                    traceWork->start[0] + traceWork->sphere.offset[0];
                start[1] =
                    traceWork->start[1] + traceWork->sphere.offset[1];
                start[2] =
                    traceWork->start[2] + traceWork->sphere.offset[2];
                end[0] = traceWork->end[0] + traceWork->sphere.offset[0];
                end[1] = traceWork->end[1] + traceWork->sphere.offset[1];
                end[2] = traceWork->end[2] + traceWork->sphere.offset[2];
            }

            const float startDistance =
                (((start[0] * plane->normal[0]) +
                  (start[1] * plane->normal[1])) +
                 (start[2] * plane->normal[2])) -
                planeDist;
            const float endDistance =
                (((end[0] * plane->normal[0]) +
                  (end[1] * plane->normal[1])) +
                 (end[2] * plane->normal[2])) -
                planeDist;

            if (startDistance > 0.0f) {
                const float fractionDelta = startDistance - endDistance;

                if (endDistance > 0.0f) {
                    return 0;
                }

                if (enterFraction * fractionDelta < startDistance) {
                    enterFraction = startDistance / fractionDelta;
                    if (leaveFraction <= enterFraction) {
                        return 0;
                    }
                }
            } else if (endDistance > 0.0f) {
                const float fractionDelta = startDistance - endDistance;

                if (leaveFraction * fractionDelta < startDistance) {
                    leaveFraction = startDistance / fractionDelta;
                    if (leaveFraction <= enterFraction) {
                        return 0;
                    }
                }
            }
        }
    } else {
        for (;;) {
            for (int32_t axis = 0; axis <= 2; ++axis) {
                const float startDistance =
                    (traceWork->start[axis] - brushBounds[axis]) * sign -
                    traceWork->maxs[axis];
                const float endDistance =
                    (traceWork->end[axis] - brushBounds[axis]) * sign -
                    traceWork->maxs[axis];

                if (startDistance > 0.0f) {
                    const float fractionDelta =
                        startDistance - endDistance;

                    if (endDistance > 0.0f) {
                        return 0;
                    }

                    if (enterFraction * fractionDelta < startDistance) {
                        enterFraction = startDistance / fractionDelta;
                        if (leaveFraction <= enterFraction) {
                            return 0;
                        }
                    }
                } else if (endDistance > 0.0f) {
                    const float fractionDelta =
                        startDistance - endDistance;

                    if (leaveFraction * fractionDelta < startDistance) {
                        leaveFraction = startDistance / fractionDelta;
                        if (leaveFraction <= enterFraction) {
                            return 0;
                        }
                    }
                }
            }

            if (maxsPass != 0) {
                break;
            }

            sign = 1.0f;
            brushBounds = brush->maxs;
            maxsPass = 1;
        }

        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;
            const float *offset = traceWork->offsets[plane->signbits];
            const float planeDist =
                plane->dist -
                (((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])) -
                planeDist;
            const float endDistance =
                (((traceWork->end[0] * plane->normal[0]) +
                  (traceWork->end[1] * plane->normal[1])) +
                 (traceWork->end[2] * plane->normal[2])) -
                planeDist;

            if (startDistance > 0.0f) {
                const float fractionDelta = startDistance - endDistance;

                if (endDistance > 0.0f) {
                    return 0;
                }

                if (enterFraction * fractionDelta < startDistance) {
                    enterFraction = startDistance / fractionDelta;
                    if (leaveFraction <= enterFraction) {
                        return 0;
                    }
                }
            } else if (endDistance > 0.0f) {
                const float fractionDelta = startDistance - endDistance;

                if (leaveFraction * fractionDelta < startDistance) {
                    leaveFraction = startDistance / fractionDelta;
                    if (leaveFraction <= enterFraction) {
                        return 0;
                    }
                }
            }
        }
    }

    return (int32_t)(brush - cm_brushes) + 1;
}
