#pragma once

#include <cmath>

namespace ta::foundation::math
{
    inline constexpr float pi = 3.14159265359f;

    struct Vector3
    {
        float x = 0.0f;
        float y = 0.0f;
        float z = 0.0f;

        bool operator==(const Vector3&) const = default;

        constexpr Vector3() = default;
        constexpr Vector3(float x_value, float y_value, float z_value) noexcept
            : x(x_value), y(y_value), z(z_value)
        {
        }

        [[nodiscard]] constexpr Vector3 add(const Vector3 other) const noexcept
        {
            return {x + other.x, y + other.y, z + other.z};
        }

        [[nodiscard]] float distance(const Vector3 other) const noexcept
        {
            const float dx = x - other.x;
            const float dy = y - other.y;
            const float dz = z - other.z;
            return std::sqrt(dx * dx + dy * dy + dz * dz);
        }

        [[nodiscard]] constexpr float length() const noexcept
        {
            return x * x + y * y + z * z;
        }

        [[nodiscard]] constexpr float dot(const Vector3 other) const noexcept
        {
            return x * other.x + y * other.y + z * other.z;
        }

        [[nodiscard]] Vector3 normalize() const noexcept
        {
            const float squared_length = length();
            if (squared_length <= 0.0f)
                return *this;
            const float magnitude = std::sqrt(squared_length);
            return {x / magnitude, y / magnitude, z / magnitude};
        }

        void to_vectors(Vector3* forward, Vector3* right, Vector3* up) const noexcept
        {
            constexpr float degrees_to_radians = pi / 180.0f;
            const float sin_x = std::sin(x * degrees_to_radians);
            const float cos_x = std::cos(x * degrees_to_radians);
            const float sin_y = std::sin(y * degrees_to_radians);
            const float cos_y = std::cos(y * degrees_to_radians);
            const float sin_z = std::sin(z * degrees_to_radians);
            const float cos_z = std::cos(z * degrees_to_radians);

            if (forward)
                *forward = {cos_x * cos_y, -sin_x, cos_x * sin_y};
            if (right)
                *right = {
                    -sin_z * sin_x * cos_y + cos_z * sin_y,
                    -sin_z * cos_x,
                    -sin_z * sin_x * sin_y - cos_z * cos_y
                };
            if (up)
                *up = {
                    cos_z * sin_x * cos_y + sin_z * sin_y,
                    cos_z * cos_x,
                    cos_z * sin_x * sin_y - sin_z * cos_y
                };
        }
    };

    struct Matrix4x4
    {
        float values[16]{};
        bool operator==(const Matrix4x4&) const = default;
    };
}
