#include "cm_trace_core_private.h"

enum {
    CM_TRANSFORMED_BOX_SIGHT_TEMP_MODEL_HANDLE = 0x1ff
};

static const vec3_t cm_transformed_box_sight_trace_zero_vec = {
    0.0f, 0.0f, 0.0f
};

int32_t CM_TransformedBoxSightTrace(
    int32_t result,
    const vec3_t start,
    const vec3_t end,
    const vec3_t mins,
    const vec3_t maxs,
    int32_t model,
    int32_t brushMask,
    const vec3_t origin,
    const vec3_t angles,
    qboolean capsule)
{
    vec3_t traceMins;
    vec3_t traceMaxs;
    vec3_t offset;
    vec3_t start_l;
    vec3_t end_l;
    qboolean rotated;
    vec3_t matrix[3];
    /*
     * As in CM_TransformedBoxTrace, stock leaves the record's trailing three
     * copied dwords uninitialized.  CM_SightTrace overwrites them before its
     * first read, so initialize the dead lanes to keep the C execution
     * defined without changing retail behavior.
     */
    cmTraceSphereRecord_t sphere = {0};

    const float *inputMins = mins;
    if (inputMins == NULL) {
        inputMins = cm_transformed_box_sight_trace_zero_vec;
    }

    const float *inputMaxs = maxs;
    if (inputMaxs == NULL) {
        inputMaxs = cm_transformed_box_sight_trace_zero_vec;
    }

    for (int32_t axis = 0; axis < 3; ++axis) {
        offset[axis] = (inputMins[axis] + inputMaxs[axis]) * 0.5f;
        traceMins[axis] = inputMins[axis] - offset[axis];
        traceMaxs[axis] = inputMaxs[axis] - offset[axis];
        start_l[axis] = start[axis] + offset[axis];
        end_l[axis] = end[axis] + offset[axis];
    }

    start_l[0] -= origin[0];
    start_l[1] -= origin[1];
    start_l[2] -= origin[2];
    end_l[0] -= origin[0];
    end_l[1] -= origin[1];
    end_l[2] -= origin[2];

    if (model == CM_TRANSFORMED_BOX_SIGHT_TEMP_MODEL_HANDLE ||
        (angles[0] == 0.0f && angles[1] == 0.0f && angles[2] == 0.0f)) {
        rotated = 0;
    } else {
        rotated = 1;
    }

    sphere.sphere.use = capsule;
    sphere.sphere.radius =
        traceMaxs[2] < traceMaxs[0] ? traceMaxs[2] : traceMaxs[0];
    sphere.sphere.halfheight = traceMaxs[2];

    const float cylinderOffset = traceMaxs[2] - sphere.sphere.radius;
    if (rotated != 0) {
        CreateRotationMatrix(angles, matrix);
        RotatePoint(start_l, matrix);
        RotatePoint(end_l, matrix);
        sphere.sphere.offset[0] = matrix[0][2] * cylinderOffset;
        sphere.sphere.offset[1] = -matrix[1][2] * cylinderOffset;
        sphere.sphere.offset[2] = matrix[2][2] * cylinderOffset;
    } else {
        sphere.sphere.offset[0] = 0.0f;
        sphere.sphere.offset[1] = 0.0f;
        sphere.sphere.offset[2] = cylinderOffset;
    }

    return CM_SightTrace(result, start_l, end_l, traceMins, traceMaxs, model,
                         origin, brushMask, capsule, &sphere);
}
