#ifndef CODUO_CORE_MATH_PRIVATE_H
#define CODUO_CORE_MATH_PRIVATE_H

#include <float.h>
#include <stdint.h>
#include <time.h>

#include "coduo_engine_structs.h"
#include "q_math.h"

#define CODUO_VEC3_COMPARE_EPSILON_SQ 1.0000001e-6f
#define CODUO_FLOAT_SIGN_BIT (1U << 31)

#ifndef CODUO_ENGINE_HOST_LONG_DOUBLE_IS_X87
#define CODUO_ENGINE_HOST_LONG_DOUBLE_IS_X87 \
    (LDBL_MANT_DIG == 64 && LDBL_MAX_EXP == 16384)
#endif

#ifndef CODUO_ENGINE_HAS_X87_INLINE_ASM
#if (defined(__i386__) || defined(__i486__) || defined(__i586__) || \
     defined(__i686__) || defined(__x86_64__)) && \
    CODUO_ENGINE_HOST_LONG_DOUBLE_IS_X87 && \
    (defined(__GNUC__) || defined(__clang__))
#define CODUO_ENGINE_HAS_X87_INLINE_ASM 1
#else
#define CODUO_ENGINE_HAS_X87_INLINE_ASM 0
#endif
#endif

extern const vec3_t vec3_origin;

int32_t Q_rand(int32_t *value);
long double Q_random(int32_t *value);
long double Q_crandom(int32_t *value);
void gunrandom(float *x, float *y);
long double Q_rsqrt(float value);
long double Q_acos(float value);
long double DotProduct(const vec3_t left, const vec3_t right);
qboolean VectorCompareEpsilon(const vec3_t left, const vec3_t right);
long double VectorLength(const vec3_t vector);
long double Distance(const vec3_t left, const vec3_t right);
long double DistanceSquared(const vec3_t left, const vec3_t right);
long double Distance2D(const vec2_t left, const vec2_t right);
long double DistanceSquared2D(const vec2_t left, const vec2_t right);
void CrossProduct(const vec3_t left, const vec3_t right, vec3_t out);
long double VectorNormalize(vec3_t vector);
long double VectorNormalize2D(vec2_t vector);
long double VectorNormalize4(vec4_t vector);
void VectorNormalizeFast(vec3_t vector);
float VectorNormalize2(const vec3_t in, vec3_t out);
long double VectorMax(const vec3_t vector);
void VectorRotate(const vec3_t in, vec3_t matrix[3], vec3_t out);
void RotatePointAroundVector(vec3_t dst, const vec3_t dir,
                             const vec3_t point, float degrees);
void RotateAroundDirection(vec3_t axis[3], float yaw);
void MakeNormalVectors(const vec3_t forward, vec3_t right, vec3_t up);
long double vectoyaw(const vec3_t vector);
long double vectosignedyaw(const vec2_t vector);
long double vectopitch(const vec3_t vector);
long double vectosignedpitch(const vec3_t vector);
void vectoangles(const vec3_t vector, vec3_t angles);
void vectosignedangles(const vec3_t vector, vec3_t angles);
void AngleVectors(const vec3_t angles, vec3_t forward, vec3_t right,
                  vec3_t up);
void YawVectors(float angleDegrees, vec3_t forward, vec3_t right);
void PerpendicularVector(vec3_t dst, const vec3_t src);
void TriangleNormal(const vec3_t point, const vec3_t edgePoint0,
                  const vec3_t edgePoint1, vec3_t out);
void ProjectPointOntoVector(const vec3_t point, const vec3_t lineStart,
                            const vec3_t lineEnd, vec3_t out);
void MatrixMultiply(float in1[3][3], float in2[3][3], float out[3][3]);
void MatrixMultiplyEquals(const vec3_t left[3], vec3_t right[3]);
void MatrixMultiply43(const float left[3][4], const float right[3][4],
                      float out[3][4]);
void MatrixMultiply43Compact(const coduo_compact_affine_matrix43_t *left,
                             const coduo_compact_affine_matrix43_t *right,
                             coduo_compact_affine_matrix43_t *out);
void DObjSkelMatrixMultiply43(const DObjSkelMat *left,
                              const coduo_compact_affine_matrix43_t *right,
                              coduo_compact_affine_matrix43_t *out);
void DObjSkel2MatrixMultiply43(const DObjSkelMat *left,
                               const coduo_compact_affine_matrix43_t *right,
                               DObjSkelMat *out);
void MatrixInverse3x3(vec3_t in[3], vec3_t out[3]);
void MatrixInverseOrthogonal43(const coduo_compact_affine_matrix43_t *in,
                               coduo_compact_affine_matrix43_t *out);
void MatrixInverse44(const float in[4][4], float out[4][4]);
void ProjectPointOnPlane(vec3_t dst, const vec3_t point, const vec3_t normal);
void MatrixTransformVector(const vec3_t in, vec3_t matrix[3], vec3_t out);
void MatrixTransposeTransformVector(const vec3_t in, vec3_t matrix[3],
                                    vec3_t out);
void MatrixTransformPoint43Compact(const vec3_t in,
                  const coduo_compact_affine_matrix43_t *matrix, vec3_t out);
void MatrixTransformPoint43Affine(const vec3_t in, const DObjSkelMat *matrix,
                  vec3_t out);
void MatrixInverseTransformVector43(
    const vec3_t in, const coduo_compact_affine_matrix43_t *matrix, vec3_t out);
void MatrixTransformPoint43CompactInPlace(vec3_t point,
                  const coduo_compact_affine_matrix43_t *matrix);
void VectorAngleMultiply(vec2_t point, float angleDegrees);
void QuatMultiply(const vec4_t left, const vec4_t right, vec4_t out);
void QuatToMatrix(float quatAndMatrix[9]);
long double QuatNormalizedVectorPartLengthSquared(const vec4_t vector);
long double SinSquaredDegrees(float angleDegrees);
long double QuatRelativeVectorPartLengthSquared(const vec4_t left,
                                                const vec4_t right);
long double RotationToYaw(const vec2_t vector);
void QuatFromYAxisDegrees(float angleDegrees, vec4_t quat);
void QuatFromZAxisDegrees(float angleDegrees, vec4_t quat);
void QuatFromXAxisDegrees(float angleDegrees, vec4_t quat);
uint32_t ColorBytes3(float red, float green, float blue);
uint32_t ColorBytes4(float red, float green, float blue, float alpha);
long double NormalizeColor(const vec3_t source, vec3_t dest);
long double AngleMod(float angleDegrees);
long double LerpAngle(float from, float to, float frac);
long double AngleSubtract(float angle1, float angle2);
void AnglesSubtract(const vec3_t angles1, const vec3_t angles2, vec3_t out);
long double AngleNormalize360(float angleDegrees);
long double AngleNormalize180(float angleDegrees);
long double AngleNormalize360Accurate(float angleDegrees);
long double AngleNormalize180Accurate(float angleDegrees);
long double AngleDelta(float angle1, float angle2);
float RadiusFromBounds(const vec3_t mins, const vec3_t maxs);
void AnglesToAxis(const vec3_t angles, coduo_compact_affine_matrix43_t *matrix);
void YawToAxis(float angleDegrees, coduo_compact_affine_matrix43_t *matrix);
void AxisToAngles(const vec3_t axis[3], vec3_t angles);
void Axis4ToAngles(const DObjSkelMat *matrix, vec3_t angles);
void AxisToSignedAngles(const vec3_t axis[3], vec3_t angles);
qboolean PlaneFromPoints(vec4_t plane, const vec3_t point0, const vec3_t point1,
                      const vec3_t point2);
qboolean BoxDistSqrdExceeds(const vec3_t point0, const vec3_t point1,
                      const vec3_t point2, float epsilon);
void NormalToLatLong(const vec3_t normal, uint8_t encoded[2]);
void Vec10Copy(const float in[10], float out[10]);
long double RoundFloat(float value);
long double ColorNormalize(const vec3_t source, vec3_t dest);
void VectorRotateAngles(const vec3_t point, const vec3_t angles, vec3_t out);
void VectorRotateAnglesAroundPoint(const vec3_t point, const vec3_t angles, const vec3_t origin,
                  vec3_t out);
void VectorPolar(vec3_t out, float radius, float angle);
void VectorSnap(vec3_t vector);
void _Vector5Add(const float left[5], const float right[5], float out[5]);
void _Vector5Scale(const float source[5], float scale, float out[5]);
void _Vector53Copy(const vec3_t source, vec3_t dest);
long double round_decimal(float value, int32_t decimals);
long double PitchForYawOnNormal(float angleDegrees, const vec3_t normal);
void Rand_Init(uint32_t seed);
long double flrand(float min, float max);
int32_t irand(int32_t min, int32_t max);
long double Q_SwayRand(float frequency0, float frequency1, float time);
void SinCosFloat(float angle, float *sinValue, float *cosValue);
void SinCosDouble(double angle, double *sinValue, double *cosValue);
const char *Com_StringContains(const char *haystack, const char *needle,
                         qboolean caseSensitive);
qboolean Com_Filter(const char *filter, const char *name,
                      qboolean caseSensitive);
qboolean Com_FilterPath(const char *filter, const char *name,
                        qboolean caseSensitive);
uint32_t Com_HashKey(const char *text, int32_t length);
time_t Com_RealTime(qtime_t *qtime);
int32_t Q_rint(float value);

#endif
