#include "q_math.h"

#include <stdint.h>
#include <string.h>

#if defined(WINDOWS_BEHAVIOR) && defined(LINUX_BEHAVIOR)
#error "Select only one of WINDOWS_BEHAVIOR or LINUX_BEHAVIOR"
#elif !defined(WINDOWS_BEHAVIOR) && !defined(LINUX_BEHAVIOR)
#error "Select WINDOWS_BEHAVIOR or LINUX_BEHAVIOR"
#endif

#define PERPENDICULAR_VECTOR_2D_SIGN_BIT UINT32_C(0x80000000)

/*
 * The six retained machine-code bodies implement the same ordered mapping:
 *
 *   CoDUOMP.exe                 0x00431580
 *   uo_cgame_mp_x86.dll        0x300496e0
 *   uo_ui_mp_x86.dll           0x400016b0
 *   uo_game_mp_x86.dll         0x20016730
 *   coduo_lnxded               0x08066725
 *   game.mp.uo.i386.so         0x0003a193
 *
 * All bodies copy input[1] to output[0] before reading input[0], so the
 * order is observable when the arrays overlap. Windows then uses
 * FLD/FCHS, writes positive zero to output[2] while the negated value remains
 * in ST0, and finally stores output[1]. Linux instead toggles the stored
 * binary32 sign bit and writes output[1] before output[2]. Keep the platform
 * bodies separate so NaN conversion, x87 status, and overlap behavior remain
 * those of the original machine code.
 *
 * The Linux game dynamic symbol directly names its body CrossProductUp, and
 * the CoDUOMP/game recoveries retain that name. The stripped Linux engine has
 * no symbol name to recover, while the cgame/UI recoveries retain
 * PerpendicularVector2D. This file supplies only the latter recovered entry
 * point and is linked only by cgame, UI, and the engine, so it does not add a
 * second entry point to the codebases that preserve CrossProductUp.
 */
#if defined(WINDOWS_BEHAVIOR)
void PerpendicularVector2D(const vec2_t input, vec3_t output)
{
    uint32_t component;

    memcpy(&component, &input[1], sizeof(component));
    memcpy(&output[0], &component, sizeof(component));

#if (defined(__i386__) || defined(__x86_64__)) && \
    (defined(__GNUC__) || defined(__clang__))
    __asm__ __volatile__("flds %2\n\t"
                         "fchs\n\t"
                         "movl $0, %0\n\t"
                         "fstps %1"
                         : "=m"(output[2]), "=m"(output[1])
                         : "m"(input[0])
                         : "st", "memory");
#else
    /* The supporting non-x86 client build has no hardware x87. Keep its
     * existing normal-value fallback and the original store order; exact x87
     * exception behavior requires the future client EMULATE_X87 wiring. */
    const float negative = -input[0];

    component = 0;
    memcpy(&output[2], &component, sizeof(component));
    memcpy(&output[1], &negative, sizeof(negative));
#endif
}
#else
void PerpendicularVector2D(const vec2_t input, vec3_t output)
{
    uint32_t component;

    memcpy(&component, &input[1], sizeof(component));
    memcpy(&output[0], &component, sizeof(component));
    memcpy(&component, &input[0], sizeof(component));
    component ^= PERPENDICULAR_VECTOR_2D_SIGN_BIT;
    memcpy(&output[1], &component, sizeof(component));
    component = 0;
    memcpy(&output[2], &component, sizeof(component));
}
#endif
