module;

#include <type_traits>
#include <concepts>
#include <immintrin.h>
#include <string>
#include <sstream>
#include <iostream>
#include <cmath>

export module Crafter.Math:MatrixRowMajor;

import :BasicTypes;
import :Vector;
import :Misc;

namespace Crafter {
	export template <typename T, uint32_t CollumSize, uint32_t RowSize, uint32_t Repeats>
	class MatrixRowMajor {
	public:
		T m[RowSize][CollumSize*Repeats];

		MatrixRowMajor() = default;

		MatrixRowMajor(
			float x0, float y0, float z0, float w0,
			float x1, float y1, float z1, float w1,
			float x2, float y2, float z2, float w2,
			float x3, float y3, float z3, float w3
		) requires(CollumSize == 4 && RowSize == 4 && Repeats == 1 && std::same_as<T, float>) {
			m[0][0] = x0;
			m[0][1] = y0;
			m[0][2] = z0;
			m[0][3] = w0;

			m[1][0] = x1;
			m[1][1] = y1;
			m[1][2] = z1;
			m[1][3] = w1;

			m[2][0] = x2;
			m[2][1] = y2;
			m[2][2] = z2;
			m[2][3] = w2;

			m[3][0] = x3;
			m[3][1] = y3;
			m[3][2] = z3;
			m[3][3] = w3;
		}

		Vector<T, 3> operator*(Vector<T, 3> b) const requires(CollumSize == 4 && RowSize == 4 && Repeats == 1 && std::same_as<T, float>) {
			return Vector<T, 3>(
				b.x * m[0][0] + b.y * m[1][0] + b.z * m[2][0] + m[3][0],
				b.x * m[0][1] + b.y * m[1][1] + b.z * m[2][1] + m[3][1],
				b.x * m[0][2] + b.y * m[1][2] + b.z * m[2][2] + m[3][2]
			);
		}

		MatrixRowMajor<T, CollumSize, RowSize, Repeats> operator*(MatrixRowMajor<T, CollumSize, RowSize, Repeats> b) const requires(CollumSize == 4 && RowSize == 4 && Repeats == 1 && std::same_as<T, float>) {
			MatrixRowMajor<T, CollumSize, RowSize, Repeats> result;

			result.m[0][0] = b.m[0][0] * m[0][0] + b.m[0][1] * m[1][0] + b.m[0][2] * m[2][0] + b.m[0][3] * m[3][0];
			result.m[1][0] = b.m[1][0] * m[0][0] + b.m[1][1] * m[1][0] + b.m[1][2] * m[2][0] + b.m[1][3] * m[3][0];
			result.m[2][0] = b.m[2][0] * m[0][0] + b.m[2][1] * m[1][0] + b.m[2][2] * m[2][0] + b.m[2][3] * m[3][0];
			result.m[3][0] = b.m[3][0] * m[0][0] + b.m[3][1] * m[1][0] + b.m[3][2] * m[2][0] + b.m[3][3] * m[3][0];
			
			result.m[0][1] = b.m[0][0] * m[0][1] + b.m[0][1] * m[1][1] + b.m[0][2] * m[2][1] + b.m[0][3] * m[3][1];
			result.m[1][1] = b.m[1][0] * m[0][1] + b.m[1][1] * m[1][1] + b.m[1][2] * m[2][1] + b.m[1][3] * m[3][1];
			result.m[2][1] = b.m[2][0] * m[0][1] + b.m[2][1] * m[1][1] + b.m[2][2] * m[2][1] + b.m[2][3] * m[3][1];
			result.m[3][1] = b.m[3][0] * m[0][1] + b.m[3][1] * m[1][1] + b.m[3][2] * m[2][1] + b.m[3][3] * m[3][1];

			result.m[0][2] = b.m[0][0] * m[0][2] + b.m[0][1] * m[1][2] + b.m[0][2] * m[2][2] + b.m[0][3] * m[3][2];
			result.m[1][2] = b.m[1][0] * m[0][2] + b.m[1][1] * m[1][2] + b.m[1][2] * m[2][2] + b.m[1][3] * m[3][2];
			result.m[2][2] = b.m[2][0] * m[0][2] + b.m[2][1] * m[1][2] + b.m[2][2] * m[2][2] + b.m[2][3] * m[3][2];
			result.m[3][2] = b.m[3][0] * m[0][2] + b.m[3][1] * m[1][2] + b.m[3][2] * m[2][2] + b.m[3][3] * m[3][2];

			result.m[0][3] = b.m[0][0] * m[0][3] + b.m[0][1] * m[1][3] + b.m[0][2] * m[2][3] + b.m[0][3] * m[3][3];
			result.m[1][3] = b.m[1][0] * m[0][3] + b.m[1][1] * m[1][3] + b.m[1][2] * m[2][3] + b.m[1][3] * m[3][3];
			result.m[2][3] = b.m[2][0] * m[0][3] + b.m[2][1] * m[1][3] + b.m[2][2] * m[2][3] + b.m[2][3] * m[3][3];
			result.m[3][3] = b.m[3][0] * m[0][3] + b.m[3][1] * m[1][3] + b.m[3][2] * m[2][3] + b.m[3][3] * m[3][3];

			return result;
		}

		static MatrixRowMajor<T, CollumSize, RowSize, Repeats> Perspective(float fovAngleY, float aspectRatio, float nearZ, float farZ) requires(CollumSize == 4 && RowSize == 4 && Repeats == 1 && std::same_as<T, float>) {
			MatrixRowMajor<T, CollumSize, RowSize, Repeats> M;
			float    SinFov;
			float    CosFov;
			XMScalarSinCos(&SinFov, &CosFov, 0.5f * fovAngleY);
		
			float Height = CosFov / SinFov;
			float Width = Height / aspectRatio;
			float fRange = farZ / (nearZ - farZ);
		
			M.m[0][0] = Width;
			M.m[0][1] = 0.0f;
			M.m[0][2] = 0.0f;
			M.m[0][3] = 0.0f;
		
			M.m[1][0] = 0.0f;
			M.m[1][1] = Height;
			M.m[1][2] = 0.0f;
			M.m[1][3] = 0.0f;
		
			M.m[2][0] = 0.0f;
			M.m[2][1] = 0.0f;
			M.m[2][2] = fRange;
			M.m[2][3] = -1.0f;
		
			M.m[3][0] = 0.0f;
			M.m[3][1] = 0.0f;
			M.m[3][2] = fRange * nearZ;
			M.m[3][3] = 0.0f;
			return M;
		}

		static MatrixRowMajor<T, CollumSize, RowSize, Repeats> Identity() requires(CollumSize == 4 && RowSize == 4 && Repeats == 1 && std::same_as<T, float>)  {
			return  MatrixRowMajor<T, CollumSize, RowSize, Repeats>(
				1, 0, 0, 0,
				0, 1, 0, 0,
				0, 0, 1, 0,
				0, 0, 0, 1
			);
		}

		static MatrixRowMajor<T, CollumSize, RowSize, Repeats> Scaling(float x, float y, float z) requires(CollumSize == 4 && RowSize == 4 && Repeats == 1 && std::same_as<T, float>)  {
			return  MatrixRowMajor<T, CollumSize, RowSize, Repeats>(
				x, 0, 0, 0,
				0, y, 0, 0,
				0, 0, z, 0,
				0, 0, 0, 1
			);
		}

		static MatrixRowMajor<T, CollumSize, RowSize, Repeats> Translation(float x, float y, float z) requires(CollumSize == 4 && RowSize == 4 && Repeats == 1 && std::same_as<T, float>)  {
			return  MatrixRowMajor<T, CollumSize, RowSize, Repeats>(
				1, 0, 0, 0,
				0, 1, 0, 0,
				0, 0, 1, 0,
				x, y, z, 1
			);
		}

		static MatrixRowMajor<T, CollumSize, RowSize, Repeats> Rotation(float Pitch, float Yaw, float Roll) requires(CollumSize == 4 && RowSize == 4 && Repeats == 1 && std::same_as<T, float>)  {
			float cp = cosf(Pitch);
			float sp = sinf(Pitch);
		
			float cy = cosf(Yaw);
			float sy = sinf(Yaw);
		
			float cr = cosf(Roll);
			float sr = sinf(Roll);
		
			MatrixRowMajor<T, CollumSize, RowSize, Repeats> M;
			M.m[0][0] = cr * cy + sr * sp * sy;
			M.m[0][1] = sr * cp;
			M.m[0][2] = sr * sp * cy - cr * sy;
			M.m[0][3] = 0.0f;
		
			M.m[1][0] = cr * sp * sy - sr * cy;
			M.m[1][1] = cr * cp;
			M.m[1][2] = sr * sy + cr * sp * cy;
			M.m[1][3] = 0.0f;
		
			M.m[2][0] = cp * sy;
			M.m[2][1] = -sp;
			M.m[2][2] = cp * cy;
			M.m[2][3] = 0.0f;
		
			M.m[3][0] = 0.0f;
			M.m[3][1] = 0.0f;
			M.m[3][2] = 0.0f;
			M.m[3][3] = 1.0f;

			return M;
		}
	};
}

template <>
struct std::formatter<Crafter::MatrixRowMajor<float, 4, 4, 1>> : std::formatter<std::string> {
    auto format(const Crafter::MatrixRowMajor<float, 4, 4, 1>& obj, format_context& ctx) const {
        return std::formatter<std::string>::format(std::format("{{{}, {}, {}, {}\n{}, {}, {}, {}\n{}, {}, {}, {}\n{}, {}, {}, {}}}", 
			obj.m[0][0], obj.m[0][1], obj.m[0][2], obj.m[0][3], 
			obj.m[1][0], obj.m[1][1], obj.m[1][2], obj.m[1][3], 
			obj.m[2][0], obj.m[2][1], obj.m[2][2], obj.m[2][3], 
			obj.m[3][0], obj.m[3][1], obj.m[3][2], obj.m[3][3]
		), ctx);
    }
};