#ifndef VECTOR_HPP
#define VECTOR_HPP
#include <corecrt_math.h>

constexpr auto flt_max = 3.402823466e+38F; // max value
constexpr auto flt_min = 1.175494351e-38F; // min normalized positive value

namespace math
{
    inline double degree_to_radian( double degree ) {
        return static_cast< double >( degree * ( std::numbers::pi / 180 ) );
    }

	struct vector3
	{
		double x, y, z;

		inline vector3( ) : x( 0 ), y( 0 ), z( 0 ) { }
		inline vector3( double x, double y, double z ) : x( x ), y( y ), z( z ) { }
        vector3 operator+( const vector3& other ) const { return { this->x + other.x, this->y + other.y, this->z + other.z }; }
        vector3 operator-( const vector3& other ) const { return { this->x - other.x, this->y - other.y, this->z - other.z }; }
        vector3 operator*( double offset ) const { return { this->x * offset, this->y * offset, this->z * offset }; }
        vector3 operator/( double offset ) const { return { this->x / offset, this->y / offset, this->z / offset }; }

        vector3& operator*=( const double other ) { this->x *= other; this->y *= other; this->z *= other; return *this; }
        vector3& operator/=( const double other ) { this->x /= other; this->y /= other; this->z /= other; return *this; }

        vector3& operator=( const vector3& other ) { this->x = other.x; this->y = other.y; this->z = other.z; return *this; }
        vector3& operator+=( const vector3& other ) { this->x += other.x; this->y += other.y; this->z += other.z; return *this; }
        vector3& operator-=( const vector3& other ) { this->x -= other.x; this->y -= other.y; this->z -= other.z; return *this; }
        vector3& operator*=( const vector3& other ) { this->x *= other.x; this->y *= other.y; this->z *= other.z; return *this; }
        vector3& operator/=( const vector3& other ) { this->x /= other.x; this->y /= other.y; this->z /= other.z; return *this; }

        operator bool( ) { return bool( this->x || this->y || this->z ); }
        friend bool operator==( const vector3& a, const vector3& b ) { return a.x == b.x && a.y == b.y && a.z == b.z; }
        friend bool operator!=( const vector3& a, const vector3& b ) { return !( a == b ); }

        __forceinline vector3 operator/( float v ) const {
            const float scale = 1.f / v;
            return vector3( x * scale, y * scale, z * scale );
        }

        inline double vector_scalar( const vector3& v ) { return x * v.x + y * v.y + z * v.z; }
        inline float distance_to( vector3 v ) { return double( sqrt( pow( v.x - x, 2.0 ) + pow( v.y - y, 2.0 ) + pow( v.z - z, 2.0 ) ) ) / 100; }
        inline double distance( vector3 v ) { return double( sqrt( pow( v.x - x, 2.0 ) + pow( v.y - y, 2.0 ) + pow( v.z - z, 2.0 ) ) ); }
        bool is_valid( ) const { return std::isfinite( this->x ) && std::isfinite( this->y ) && std::isfinite( this->z ); }
        inline double length( ) { return sqrt( x * x + y * y + z * z ); }
       
        bool is_empty( const float& treshhold = 0.00f ) const {
            if ( fabs( this->x ) <= treshhold || fabs( this->y ) <= treshhold || fabs( this->z ) <= treshhold ) return true;
            else return false;
        }

        void add_scale( const vector3& v, float scale )
        {
            x += v.x * scale;
            y += v.y * scale;
            z += v.z * scale;
        }

        inline vector3 relative( vector3 pos )
        {
            x = x - pos.x;
            y = y - pos.y;
            return { x, y, 0 };
        }

        inline vector3 normalize( ) 
        {
            x = fmod( x + 180.0f, 360.0f );

            if ( x < 0 )
                x += 360.0f;

            x -= 180.0f;

            y = fmod( y + 180.0f, 360.0f );

            if ( y < 0 )
                y += 360.0f;
            y -= 180.0f;

            return { x,y,0 };
        }

        inline void angle_rotation( const vector3& angles )
        {
            double	sp, sy, cp, cy;

            sy = sin( degree_to_radian( angles.y ) );
            cy = cos( degree_to_radian( angles.y ) );

            sp = sin( degree_to_radian( angles.x ) );
            cp = cos( degree_to_radian( angles.x ) );

            x = cp * cy;
            y = cp * sy;
            z = -sp;
        }

        vector3 multiply( const vector3& point )
        {
            float num = x * 100.00f;
            float num2 = y * 100.00f;
            float num3 = z * 100.00f;
            float num4 = x * num;
            float num5 = y * num2;
            float num6 = z * num3;
            float num7 = x * num2;
            float num8 = x * num3;
            float num9 = y * num3;

            vector3 result;
            result.x = ( 1.00f - ( num5 + num6 ) ) * point.x +
                ( num7 - num9 ) * point.y +
                ( num8 + num9 ) * point.z;
            result.y = ( num7 + num9 ) * point.x +
                ( 1.00f - ( num4 + num6 ) ) * point.y +
                ( num9 - num8 ) * point.z;
            result.z = ( num8 - num9 ) * point.x +
                ( num9 + num8 ) * point.y +
                ( 1.00f - ( num4 + num5 ) ) * point.z;
            return result;
        }
	};

    struct vector2
    {
        double x, y;

        vector2( ) : x( ), y( ) { }
        vector2( double x, double y ) : x( x ), y( y ) { }

        vector2 operator + ( const vector2& other ) const { return { this->x + other.x, this->y + other.y }; }
        vector2 operator - ( const vector2& other ) const { return { this->x - other.x, this->y - other.y }; }
        vector2 operator * ( double offset ) const { return { this->x * offset, this->y * offset }; }
        vector2 operator / ( double offset ) const { return { this->x / offset, this->y / offset }; }

        vector2& operator *= ( const double other ) { this->x *= other; this->y *= other; return *this; }
        vector2& operator /= ( const double other ) { this->x /= other; this->y /= other; return *this; }

        vector2& operator = ( const vector2& other ) { this->x = other.x; this->y = other.y; return *this; }
        vector2& operator += ( const vector2& other ) { this->x += other.x; this->y += other.y; return *this; }
        vector2& operator -= ( const vector2& other ) { this->x -= other.x; this->y -= other.y; return *this; }
        vector2& operator *= ( const vector2& other ) { this->x *= other.x; this->y *= other.y; return *this; }
        vector2& operator /= ( const vector2& other ) { this->x /= other.x; this->y /= other.y; return *this; }

        operator bool( ) { return bool( this->x || this->y ); }
        friend bool operator == ( const vector2& A, const vector2& B ) { return A.x == B.x && A.y == A.y; }
        friend bool operator != ( const vector2& A, const vector2& B ) { return !( A == B ); }

        double vector_scalar( const vector2& V ) { return x * V.x + y * V.y; }
        double distance_to( vector2 V ) { return double( sqrtf( powf( V.x - this->x, 2.0 ) + powf( V.y - this->y, 2.0 ) ) ); }
        
        static vector2 from_angle( double angle, double magnitude = 1.0f ) {
            return vector2( cos( angle ) * magnitude, sin( angle ) * magnitude );
        }
    };
}

namespace bounds
{
    struct bounds_t
    {
        float left, right, top, bottom;
    };

    struct bone_t
    {
        math::vector3 screen;
        int index;
    };

    static std::array<bounds::bone_t, 14> bone_itter =
    {
        bounds::bone_t{ math::vector3( ), 110 }, // Head
        bounds::bone_t{ math::vector3( ), 66 }, // Neck
        bounds::bone_t{ math::vector3( ), 9 }, // Right Shoulder
        bounds::bone_t{ math::vector3( ), 10 }, // Right Elbow
        bounds::bone_t{ math::vector3( ), 11 }, // Right Hand
        bounds::bone_t{ math::vector3( ), 38 }, // Left Shoulder
        bounds::bone_t{ math::vector3( ), 39 }, // Left Elbow
        bounds::bone_t{ math::vector3( ), 40 }, // Left Hand
        bounds::bone_t{ math::vector3( ), 71 }, // Right Hip
        bounds::bone_t{ math::vector3( ), 72 }, // Right Knee
        bounds::bone_t{ math::vector3( ), 75 }, // Right Foot
        bounds::bone_t{ math::vector3( ), 78 }, // Left Hip
        bounds::bone_t{ math::vector3( ), 79 }, // Left Knee
        bounds::bone_t{ math::vector3( ), 82 } // Left Foot
    };
}

#endif // ! guard