#pragma once

#include "Vector3.hpp"
#include "Vector4.hpp"
#include "Quaternion.hpp"

namespace TSE
{
    class Matrix4x4
    {
        public:
        float m[4][4]; // Row Major Layout

        /// @brief default Constructor. all values are 0
        Matrix4x4();
        /// @brief sets the diagonal to a custom value
        /// @param d the value for the diagonal
        Matrix4x4(float d);
        /// @brief copy constructor
        /// @param other the matrix to be copied
        Matrix4x4(const Matrix4x4& other);

        /// @brief checks if the matrix is an identity matrix aka the diagonal is set to 1, and everithing else is 0
        /// @return is identity matrix
        bool IsIdentityMatrix() const;
        /// @brief chacks if the matrix ist valid aka the values are not NaN
        /// @return is valid
        bool IsValid() const;
        /// @brief inverts the current matrix with the generic invertion method
        void Invert();
        /// @brief gets the conjuget of the current matrix
        /// @return the conjuget
        Matrix4x4 Conjucate() const;
        /// @brief Gets the determinant of the matrix
        /// @return the determinant
        float Determinant() const;
        /// @brief checks if the current matrix is an affine matrix aka a transformation matrix only translation, rotation, and scale set.
        /// @return is affine
        bool IsAffine() const;
        /// @brief inverts an affine matrix. simply a faster inversion method for affine matrecies.
        void InvertAffine();
        /// @brief gets the pointer to the contents of this matrix.
        /// @return the contens as row major
        const float* ToArrayRowMajor() const;
        /// @brief fills an array with the matrix contens as column major
        /// @param out the array to be filled
        void ToArrayColumnMajor(float out[16]) const;

        /// @brief generates an identity matrix
        /// @return identity matrix
        static Matrix4x4 Identity();
        /// @brief generates an orthographics projection matrix.
        /// @param left the left clipping plane
        /// @param right the right clipping plane
        /// @param bottom the bottom clipping plane
        /// @param top the top clipping plane
        /// @param near the near clipping plane
        /// @param far the far clipping plane
        /// @return an orthoraphic projection matrix
        static Matrix4x4 Orthographic(float left, float right, float bottom, float top, float near, float far);
        /// @brief generates a perspectiv projection marix
        /// @param fov the field of view in deg
        /// @param aspectRatio the aspect ratio of the render target
        /// @param near the near clipping plane
        /// @param far the far clipping plane
        /// @return a perspective projection matrix
        static Matrix4x4 Perspective(float fov, float aspectRatio, float near, float far);
        /// @brief generaties a translation matrix from a position
        /// @param pos the position to use
        /// @return the generated matrix
        static Matrix4x4 ToTranslationMatrix(const Vector3& pos);
        /// @brief generates a rotation matrix from a quaternion
        /// @param rot the quaternion to use
        /// @return the generated matrix
        static Matrix4x4 ToRotationMatrix(const Quaternion& rot);
        /// @brief generates a rotation matrix from an angle around the z axis (2D)
        /// @param angle the angle to rotate about
        /// @return the generated matrix
        static Matrix4x4 ToRotationMatrixFromZAxisOnly(float angle);
        /// @brief generates a scale matrix from a scale vector
        /// @param scale the scale to use
        /// @return the generated matrix
        static Matrix4x4 ToScaleMatrix(const Vector3& scale);
        /// @brief inverts a the given matrix, when possible it trys to use less calculations
        /// @param mat the matrix to invert
        /// @return the inverted matrix
        static Matrix4x4 Invert(const Matrix4x4& mat);

        Matrix4x4 operator*(const Matrix4x4& other) const;
        Matrix4x4 operator*=(const Matrix4x4& other);
        Matrix4x4 operator*(float scalar) const;
        Matrix4x4 operator*=(float scalar);
        Vector3 operator*(const Vector3& v) const;
        Vector4 operator*(const Vector4& v) const;
    };
} // namespace TSE
