#pragma once
#include "mof/Vector3D.hpp"
#include <mof/real.hpp>
#include <memory>


namespace mof{

    class Matrix3D
    {
    public:
        struct Array
        {
            real elements[4][4];
        };
    private:
        std::shared_ptr<Array> m_pImpl;
    public:
        
        Matrix3D();
        explicit Matrix3D(const Array &);
        ~Matrix3D();
        real at(int row , int column) const;
        Array getArray() const;

        static Matrix3D createIdentity();
        static Matrix3D createTransposed(const Matrix3D& matrix);
        static Matrix3D createRotation(const mof::Vector3D& angle);
        static Matrix3D createTranslation(const mof::Vector3D& position);
        static Matrix3D createScaling(const mof::Vector3D& scale);
        static Matrix3D createLookAtLH( const Vector3D& eye , const Vector3D& lookAt , const Vector3D& up );
        static Vector3D getTranslation(const mof::Matrix3D& matrix);
        Vector3D getDiagonal() const;


        Matrix3D operator *(const mof::Matrix3D& matrix ) const;
        Matrix3D operator +(const mof::Matrix3D& matrix ) const;
        Matrix3D operator -(const mof::Matrix3D& matrix ) const;

        friend Vector3D operator *(const mof::Vector3D& vec , const mof::Matrix3D& matrix);
        friend Matrix3D operator *(const mof::Matrix3D& matrix , real f);
        friend Matrix3D operator *(real f ,  const mof::Matrix3D & matrix);
        friend std::ostream& operator <<(std::ostream& os , const mof::Matrix3D& mat);
    }; // class Matrix3D
        
    std::ostream& operator <<(std::ostream& os , const mof::Matrix3D& mat);

} // namespace mof

