#pragma once

#include <Core/Math/Vector.h>
#include <math.h>

namespace Juliet
{
    struct Matrix
    {
        float m[4][4];

        static Matrix Identity()
        {
            Matrix result = {};
            result.m[0][0] = 1.0f;
            result.m[1][1] = 1.0f;
            result.m[2][2] = 1.0f;
            result.m[3][3] = 1.0f;
            return result;
        }

        Matrix operator*(const Matrix& rhs) const
        {
            Matrix result = {};
            for (int i = 0; i < 4; ++i)
            {
                for (int j = 0; j < 4; ++j)
                {
                    for (int k = 0; k < 4; ++k)
                    {
                        result.m[i][j] += m[i][k] * rhs.m[k][j];
                    }
                }
            }
            return result;
        }
    };

    inline Matrix LookAt(const Vector3& eye, const Vector3& target, const Vector3& up)
    {
        // Left-Handed convention
        Vector3 zaxis = Normalize(target - eye); // Forward is +z
        Vector3 xaxis = Normalize(Cross(up, zaxis));
        Vector3 yaxis = Cross(zaxis, xaxis);

        Matrix result = {};
        // Row 0
        result.m[0][0] = xaxis.x;
        result.m[0][1] = xaxis.y;
        result.m[0][2] = xaxis.z;
        result.m[0][3] = -Dot(xaxis, eye);

        // Row 1
        result.m[1][0] = yaxis.x;
        result.m[1][1] = yaxis.y;
        result.m[1][2] = yaxis.z;
        result.m[1][3] = -Dot(yaxis, eye);

        // Row 2
        result.m[2][0] = zaxis.x;
        result.m[2][1] = zaxis.y;
        result.m[2][2] = zaxis.z;
        result.m[2][3] = -Dot(zaxis, eye);

        // Row 3
        result.m[3][3] = 1.0f;

        return result;
    }

    inline Matrix PerspectiveFov(float fovY, float aspectRatio, float nearZ, float farZ)
    {
        // Left-Handed Perspective
        float yScale = 1.0f / tanf(fovY * 0.5f);
        float xScale = yScale / aspectRatio;

        Matrix result = {};
        result.m[0][0]  = xScale;
        result.m[1][1]  = yScale;
        result.m[2][2]  = farZ / (farZ - nearZ);
        result.m[2][3]  = (-nearZ * farZ) / (farZ - nearZ);
        result.m[3][2]  = 1.0f;
        result.m[3][3]  = 0.0f;
        return result;
    }
} // namespace Juliet
