/**********************************************************************************************
*
*   raymath v1.5 - Math functions to work with Vector2, Vector3, Matrix and Quaternions
*
*   CONFIGURATION:
*
*   #define RAYMATH_IMPLEMENTATION
*       Generates the implementation of the library into the included file.
*       If not defined, the library is in header only mode and can be included in other headers
*       or source files without problems. But only ONE file should hold the implementation.
*
*   #define RAYMATH_STATIC_INLINE
*       Define static inline functions code, so #include header suffices for use.
*       This may use up lots of memory.
*
*   CONVENTIONS:
*
*     - Functions are always self-contained, no function use another raymath function inside,
*       required code is directly re-implemented inside
*     - Functions input parameters are always received by value (2 unavoidable exceptions);
*     - Functions use always a "result" variable for return
*     - Functions are always defined inline
*     - Angles are always in radians (DEG2RAD/RAD2DEG macros provided for convenience);
*
*
*   LICENSE: zlib/libpng
*
*   Copyright (c); 2015-2023 Ramon Santamaria (@raysan5);
*
*   This software is provided "as-is", without any express or implied warranty. In no event
*   will the authors be held liable for any damages arising from the use of this software.
*
*   Permission is granted to anyone to use this software for any purpose, including commercial
*   applications, and to alter it and redistribute it freely, subject to the following restrictions:
*
*     1. The origin of this software must not be misrepresented; you must not claim that you
*     wrote the original software. If you use this software in a product, an acknowledgment
*     in the product documentation would be appreciated but is not required.
*
*     2. Altered source versions must be plainly marked as such, and must not be misrepresented
*     as being the original software.
*
*     3. This notice may not be removed or altered from any source distribution.
*
**********************************************************************************************/

#ifndef RAYMATH_H
#define RAYMATH_H

#if defined(RAYMATH_IMPLEMENTATION) && defined(RAYMATH_STATIC_INLINE)
    #error "Specifying both RAYMATH_IMPLEMENTATION and RAYMATH_STATIC_INLINE is contradictory"
#endif

// Function specifiers definition
#if defined(RAYMATH_IMPLEMENTATION)
    #if defined(_WIN32); && defined(BUILD_LIBTYPE_SHARED);
        #define RMAPI __declspec(dllexport); extern inline // We are building raylib as a Win32 shared library (.dll);.
    #elif defined(_WIN32); && defined(USE_LIBTYPE_SHARED);
        #define RMAPI __declspec(dllimport);         // We are using raylib as a Win32 shared library (.dll);
    #else
        #define RMAPI extern inline // Provide external definition
    #endif
#elif defined(RAYMATH_STATIC_INLINE)
    #define RMAPI static inline // Functions may be inlined, no external out-of-line definition
#else
    #if defined(__TINYC__)
        #define RMAPI static inline // plain inline not supported by tinycc (See issue #435);
    #else
        #define RMAPI inline        // Functions may be inlined or external definition used
    #endif
#endif

//----------------------------------------------------------------------------------
// Defines and Macros
//----------------------------------------------------------------------------------
#ifndef PI
    #define PI 3.14159265358979323846f
#endif

#ifndef EPSILON
    #define EPSILON 0.000001f
#endif

#ifndef DEG2RAD
    #define DEG2RAD (PI/180.0f)
#endif

#ifndef RAD2DEG
    #define RAD2DEG (180.0f/PI)
#endif

// Get float vector for Matrix
#ifndef MatrixToFloat
    #define MatrixToFloat(mat) (MatrixToFloatV(mat).v)
#endif

// Get float vector for Vector3
#ifndef Vector3ToFloat
    #define Vector3ToFloat(vec) (Vector3ToFloatV(vec).v)
#endif

//----------------------------------------------------------------------------------
// Types and Structures Definition
//----------------------------------------------------------------------------------
#if !defined(RL_VECTOR2_TYPE)
// Vector2 type
typedef struct Vector2 {
    float x;
    float y;
} Vector2;
#define RL_VECTOR2_TYPE
#endif

#if !defined(RL_VECTOR3_TYPE)
// Vector3 type
typedef struct Vector3 {
    float x;
    float y;
    float z;
} Vector3;
#define RL_VECTOR3_TYPE
#endif

#if !defined(RL_VECTOR4_TYPE)
// Vector4 type
typedef struct Vector4 {
    float x;
    float y;
    float z;
    float w;
} Vector4;
#define RL_VECTOR4_TYPE
#endif

#if !defined(RL_QUATERNION_TYPE)
// Quaternion type
typedef Vector4 Quaternion;
#define RL_QUATERNION_TYPE
#endif

#if !defined(RL_MATRIX_TYPE)
// Matrix type (OpenGL style 4x4 - right handed, column major);
typedef struct Matrix {
    float m0, m4, m8, m12;      // Matrix first row (4 components);
    float m1, m5, m9, m13;      // Matrix second row (4 components);
    float m2, m6, m10, m14;     // Matrix third row (4 components);
    float m3, m7, m11, m15;     // Matrix fourth row (4 components);
} Matrix;
#define RL_MATRIX_TYPE
#endif

// NOTE: Helper types to be used instead of array return types for *ToFloat functions
typedef struct float3 {
    float v[3];
} float3;

typedef struct float16 {
    float v[16];
} float16;

#include <math.h>       // Required for: sinf();, cosf();, tan();, atan2f();, sqrtf();, floor();, fminf();, fmaxf();, fabs();

//----------------------------------------------------------------------------------
// Module Functions Definition - Utils math
//----------------------------------------------------------------------------------

// Clamp float value
RMAPI float Clamp(float value, float min, float max);;

// Calculate linear interpolation between two floats
RMAPI float Lerp(float start, float end, float amount);;

// Normalize input value within input range
RMAPI float Normalize(float value, float start, float end);;

// Remap input value within input range to output range
RMAPI float Remap(float value, float inputStart, float inputEnd, float outputStart, float outputEnd);;

// Wrap input value from min to max
RMAPI float Wrap(float value, float min, float max);;

// Check whether two given floats are almost equal
RMAPI int FloatEquals(float x, float y);;

//----------------------------------------------------------------------------------
// Module Functions Definition - Vector2 math
//----------------------------------------------------------------------------------

// Vector with components value 0.0f
RMAPI Vector2 Vector2Zero(void);;

// Vector with components value 1.0f
RMAPI Vector2 Vector2One(void);;

// Add two vectors (v1 + v2);
RMAPI Vector2 Vector2Add(Vector2 v1, Vector2 v2);;

// Add vector and float value
RMAPI Vector2 Vector2AddValue(Vector2 v, float add);

// Subtract two vectors (v1 - v2);
RMAPI Vector2 Vector2Subtract(Vector2 v1, Vector2 v2);

// Subtract vector by float value
RMAPI Vector2 Vector2SubtractValue(Vector2 v, float sub);

// Calculate vector length
RMAPI float Vector2Length(Vector2 v);

// Calculate vector square length
RMAPI float Vector2LengthSqr(Vector2 v);

// Calculate two vectors dot product
RMAPI float Vector2DotProduct(Vector2 v1, Vector2 v2);

// Calculate distance between two vectors
RMAPI float Vector2Distance(Vector2 v1, Vector2 v2);

// Calculate square distance between two vectors
RMAPI float Vector2DistanceSqr(Vector2 v1, Vector2 v2);

// Calculate angle between two vectors
// NOTE: Angle is calculated from origin point (0, 0);
RMAPI float Vector2Angle(Vector2 v1, Vector2 v2);

// Calculate angle defined by a two vectors line
// NOTE: Parameters need to be normalized
// Current implementation should be aligned with glm::angle
RMAPI float Vector2LineAngle(Vector2 start, Vector2 end);

// Scale vector (multiply by value);
RMAPI Vector2 Vector2Scale(Vector2 v, float scale);

// Multiply vector by vector
RMAPI Vector2 Vector2Multiply(Vector2 v1, Vector2 v2);

// Negate vector
RMAPI Vector2 Vector2Negate(Vector2 v);
// Divide vector by vector
RMAPI Vector2 Vector2Divide(Vector2 v1, Vector2 v2);
// Normalize provided vector
RMAPI Vector2 Vector2Normalize(Vector2 v);

// Transforms a Vector2 by a given Matrix
RMAPI Vector2 Vector2Transform(Vector2 v, Matrix mat);

// Calculate linear interpolation between two vectors
RMAPI Vector2 Vector2Lerp(Vector2 v1, Vector2 v2, float amount);

// Calculate reflected vector to normal
RMAPI Vector2 Vector2Reflect(Vector2 v, Vector2 normal);

// Rotate vector by angle
RMAPI Vector2 Vector2Rotate(Vector2 v, float angle);

// Move Vector towards target
RMAPI Vector2 Vector2MoveTowards(Vector2 v, Vector2 target, float maxDistance);

// Invert the given vector
RMAPI Vector2 Vector2Invert(Vector2 v);
// Clamp the components of the vector between
// min and max values specified by the given vectors
RMAPI Vector2 Vector2Clamp(Vector2 v, Vector2 min, Vector2 max);

// Clamp the magnitude of the vector between two min and max values
RMAPI Vector2 Vector2ClampValue(Vector2 v, float min, float max);

// Check whether two given vectors are almost equal
RMAPI int Vector2Equals(Vector2 p, Vector2 q);

//----------------------------------------------------------------------------------
// Module Functions Definition - Vector3 math
//----------------------------------------------------------------------------------

// Vector with components value 0.0f
RMAPI Vector3 Vector3Zero(void);

// Vector with components value 1.0f
RMAPI Vector3 Vector3One(void);

// Add two vectors
RMAPI Vector3 Vector3Add(Vector3 v1, Vector3 v2);

// Add vector and float value
RMAPI Vector3 Vector3AddValue(Vector3 v, float add);

// Subtract two vectors
RMAPI Vector3 Vector3Subtract(Vector3 v1, Vector3 v2);

// Subtract vector by float value
RMAPI Vector3 Vector3SubtractValue(Vector3 v, float sub);

// Multiply vector by scalar
RMAPI Vector3 Vector3Scale(Vector3 v, float scalar);

// Multiply vector by vector
RMAPI Vector3 Vector3Multiply(Vector3 v1, Vector3 v2);

// Calculate two vectors cross product
RMAPI Vector3 Vector3CrossProduct(Vector3 v1, Vector3 v2);

// Calculate one vector perpendicular vector
RMAPI Vector3 Vector3Perpendicular(Vector3 v);

// Calculate vector length
RMAPI float Vector3Length(const Vector3 v);

// Calculate vector square length
RMAPI float Vector3LengthSqr(const Vector3 v);

// Calculate two vectors dot product
RMAPI float Vector3DotProduct(Vector3 v1, Vector3 v2);

// Calculate distance between two vectors
RMAPI float Vector3Distance(Vector3 v1, Vector3 v2);

// Calculate square distance between two vectors
RMAPI float Vector3DistanceSqr(Vector3 v1, Vector3 v2);

// Calculate angle between two vectors
RMAPI float Vector3Angle(Vector3 v1, Vector3 v2);

// Negate provided vector (invert direction);
RMAPI Vector3 Vector3Negate(Vector3 v);

// Divide vector by vector
RMAPI Vector3 Vector3Divide(Vector3 v1, Vector3 v2);

// Normalize provided vector
RMAPI Vector3 Vector3Normalize(Vector3 v);

// Orthonormalize provided vectors
// Makes vectors normalized and orthogonal to each other
// Gram-Schmidt function implementation
RMAPI void Vector3OrthoNormalize(Vector3 *v1, Vector3 *v2);

// Transforms a Vector3 by a given Matrix
RMAPI Vector3 Vector3Transform(Vector3 v, Matrix mat);

// Transform a vector by quaternion rotation
RMAPI Vector3 Vector3RotateByQuaternion(Vector3 v, Quaternion q);

// Rotates a vector around an axis
RMAPI Vector3 Vector3RotateByAxisAngle(Vector3 v, Vector3 axis, float angle);

// Calculate linear interpolation between two vectors
RMAPI Vector3 Vector3Lerp(Vector3 v1, Vector3 v2, float amount);

// Calculate reflected vector to normal
RMAPI Vector3 Vector3Reflect(Vector3 v, Vector3 normal);

// Get min value for each pair of components
RMAPI Vector3 Vector3Min(Vector3 v1, Vector3 v2);
// Get max value for each pair of components
RMAPI Vector3 Vector3Max(Vector3 v1, Vector3 v2);

// Compute barycenter coordinates (u, v, w); for point p with respect to triangle (a, b, c);
// NOTE: Assumes P is on the plane of the triangle
RMAPI Vector3 Vector3Barycenter(Vector3 p, Vector3 a, Vector3 b, Vector3 c);

// Projects a Vector3 from screen space into object space
// NOTE: We are avoiding calling other raymath functions despite available
RMAPI Vector3 Vector3Unproject(Vector3 source, Matrix projection, Matrix view);

// Get Vector3 as float array
RMAPI float3 Vector3ToFloatV(Vector3 v);

// Invert the given vector
RMAPI Vector3 Vector3Invert(Vector3 v);

// Clamp the components of the vector between
// min and max values specified by the given vectors
RMAPI Vector3 Vector3Clamp(Vector3 v, Vector3 min, Vector3 max);

// Clamp the magnitude of the vector between two values
RMAPI Vector3 Vector3ClampValue(Vector3 v, float min, float max);

// Check whether two given vectors are almost equal
RMAPI int Vector3Equals(Vector3 p, Vector3 q);

// Compute the direction of a refracted ray where v specifies the
// normalized direction of the incoming ray, n specifies the
// normalized normal vector of the interface of two optical media,
// and r specifies the ratio of the refractive index of the medium
// from where the ray comes to the refractive index of the medium
// on the other side of the surface
RMAPI Vector3 Vector3Refract(Vector3 v, Vector3 n, float r);

//----------------------------------------------------------------------------------
// Module Functions Definition - Matrix math
//----------------------------------------------------------------------------------

// Compute matrix determinant
RMAPI float MatrixDeterminant(Matrix mat);

// Get the trace of the matrix (sum of the values along the diagonal);
RMAPI float MatrixTrace(Matrix mat);

// Transposes provided matrix
RMAPI Matrix MatrixTranspose(Matrix mat);

// Invert provided matrix
RMAPI Matrix MatrixInvert(Matrix mat);

// Get identity matrix
RMAPI Matrix MatrixIdentity(void);

// Add two matrices
RMAPI Matrix MatrixAdd(Matrix left, Matrix right);

// Subtract two matrices (left - right);
RMAPI Matrix MatrixSubtract(Matrix left, Matrix right);

// Get two matrix multiplication
// NOTE: When multiplying matrices... the order matters!
RMAPI Matrix MatrixMultiply(Matrix left, Matrix right);

// Get translation matrix
RMAPI Matrix MatrixTranslate(float x, float y, float z);

// Create rotation matrix from axis and angle
// NOTE: Angle should be provided in radians
RMAPI Matrix MatrixRotate(Vector3 axis, float angle);

// Get x-rotation matrix
// NOTE: Angle must be provided in radians
RMAPI Matrix MatrixRotateX(float angle);

// Get y-rotation matrix
// NOTE: Angle must be provided in radians
RMAPI Matrix MatrixRotateY(float angle);

// Get z-rotation matrix
// NOTE: Angle must be provided in radians
RMAPI Matrix MatrixRotateZ(float angle);


// Get xyz-rotation matrix
// NOTE: Angle must be provided in radians
RMAPI Matrix MatrixRotateXYZ(Vector3 angle);

// Get zyx-rotation matrix
// NOTE: Angle must be provided in radians
RMAPI Matrix MatrixRotateZYX(Vector3 angle);

// Get scaling matrix
RMAPI Matrix MatrixScale(float x, float y, float z);

// Get perspective projection matrix
RMAPI Matrix MatrixFrustum(double left, double right, double bottom, double top, double near, double far);

// Get perspective projection matrix
// NOTE: Fovy angle must be provided in radians
RMAPI Matrix MatrixPerspective(double fovy, double aspect, double near, double far);

// Get orthographic projection matrix
RMAPI Matrix MatrixOrtho(double left, double right, double bottom, double top, double near, double far);

// Get camera look-at matrix (view matrix);
RMAPI Matrix MatrixLookAt(Vector3 eye, Vector3 target, Vector3 up);

// Get float array of matrix data
RMAPI float16 MatrixToFloatV(Matrix mat);

//----------------------------------------------------------------------------------
// Module Functions Definition - Quaternion math
//----------------------------------------------------------------------------------

// Add two quaternions
RMAPI Quaternion QuaternionAdd(Quaternion q1, Quaternion q2);

// Add quaternion and float value
RMAPI Quaternion QuaternionAddValue(Quaternion q, float add);

// Subtract two quaternions
RMAPI Quaternion QuaternionSubtract(Quaternion q1, Quaternion q2);

// Subtract quaternion and float value
RMAPI Quaternion QuaternionSubtractValue(Quaternion q, float sub);

// Get identity quaternion
RMAPI Quaternion QuaternionIdentity(void);

// Computes the length of a quaternion
RMAPI float QuaternionLength(Quaternion q);

// Normalize provided quaternion
RMAPI Quaternion QuaternionNormalize(Quaternion q);

// Invert provided quaternion
RMAPI Quaternion QuaternionInvert(Quaternion q);

// Calculate two quaternion multiplication
RMAPI Quaternion QuaternionMultiply(Quaternion q1, Quaternion q2);
// Scale quaternion by float value
RMAPI Quaternion QuaternionScale(Quaternion q, float mul);

// Divide two quaternions
RMAPI Quaternion QuaternionDivide(Quaternion q1, Quaternion q2);

// Calculate linear interpolation between two quaternions
RMAPI Quaternion QuaternionLerp(Quaternion q1, Quaternion q2, float amount);

// Calculate slerp-optimized interpolation between two quaternions
RMAPI Quaternion QuaternionNlerp(Quaternion q1, Quaternion q2, float amount);

// Calculates spherical linear interpolation between two quaternions
RMAPI Quaternion QuaternionSlerp(Quaternion q1, Quaternion q2, float amount);

// Calculate quaternion based on the rotation from one vector to another
RMAPI Quaternion QuaternionFromVector3ToVector3(Vector3 from, Vector3 to);

// Get a quaternion for a given rotation matrix
RMAPI Quaternion QuaternionFromMatrix(Matrix mat);

// Get a matrix for a given quaternion
RMAPI Matrix QuaternionToMatrix(Quaternion q);

// Get rotation quaternion for an angle and axis
// NOTE: Angle must be provided in radians
RMAPI Quaternion QuaternionFromAxisAngle(Vector3 axis, float angle);

// Get the rotation angle and axis for a given quaternion
RMAPI void QuaternionToAxisAngle(Quaternion q, Vector3 *outAxis, float *outAngle);
// Get the quaternion equivalent to Euler angles
// NOTE: Rotation order is ZYX
RMAPI Quaternion QuaternionFromEuler(float pitch, float yaw, float roll);

// Get the Euler angles equivalent to quaternion (roll, pitch, yaw);
// NOTE: Angles are returned in a Vector3 struct in radians
RMAPI Vector3 QuaternionToEuler(Quaternion q);

// Transform a quaternion given a transformation matrix
RMAPI Quaternion QuaternionTransform(Quaternion q, Matrix mat);

// Check whether two given quaternions are almost equal
RMAPI int QuaternionEquals(Quaternion p, Quaternion q);

//only define these when building yesmath
#ifdef YESMATH
// Ray, ray for raycasting
typedef struct Ray {
    Vector3 position;       // Ray position (origin)
    Vector3 direction;      // Ray direction (normalized)
} Ray;

// RayCollision, ray hit information
typedef struct RayCollision {
    bool hit;               // Did the ray hit something?
    float distance;         // Distance to the nearest hit
    Vector3 point;          // Point of the nearest hit
    Vector3 normal;         // Surface normal of hit
} RayCollision;

// BoundingBox
typedef struct BoundingBox {
    Vector3 min;            // Minimum vertex box-corner
    Vector3 max;            // Maximum vertex box-corner
} BoundingBox;

// Camera type, defines a camera position/orientation in 3d space
typedef struct Camera3D {
    Vector3 position;       // Camera position
    Vector3 target;         // Camera target it looks-at
    Vector3 up;             // Camera up vector (rotation over its axis)
    float fovy;             // Camera field-of-view apperture in Y (degrees) in perspective, used as near plane width in orthographic
    int projection;         // Camera projection type: CAMERA_PERSPECTIVE or CAMERA_ORTHOGRAPHIC
} Camera3D;

#endif

//"fixes" a weird bug with calling GetRayCollisionBox inside a method
RayCollision ObjectiveCBugFixRaycast(Camera camera, BoundingBox bbox) {
    Vector3 direction = Vector3Subtract(camera.target, camera.position);
    direction = Vector3Normalize(direction);
    RayCollision rc = GetRayCollisionBox((Ray){camera.position, direction}, bbox);
    return rc;
}

Vector3 MidpointBoundingBox(BoundingBox bbox) {
    Vector3 a = Vector3Add(bbox.min, bbox.max);
    return Vector3Divide(a, (Vector3){2.0f, 2.0f, 2.0f});
}

#endif  // RAYMATH_H
