/*
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/.
 */

#pragma once

#define GLM_FORECE_DEPTH_ZERO_TO_ONE // TODO handle this better
#include <glm/glm.hpp>
#include <glm/gtc/type_aligned.hpp>
#include <glm/gtc/quaternion.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtx/matrix_decompose.hpp>
#include <glm/gtx/quaternion.hpp>
#include <glm/gtx/transform.hpp>
#include <glm/gtx/intersect.hpp>
#include <glm/gtx/io.hpp>
#include <numbers>

namespace OpenVulkano::Math
{
	namespace Utils
	{
		using namespace glm;

		template<typename T, typename = std::enable_if_t<std::is_floating_point_v<T>>>
		constexpr T Radians(T degree)
		{
			constexpr T CONVERSION = std::numbers::pi_v<T> / 180.0;
			return degree * CONVERSION;
		}
		
		template<typename T>
		constexpr T Square(T val)
		{
			return val * val;
		}
	}

	template<typename T> using Matrix2_SIMD = glm::tmat2x2<T, glm::aligned>;
	template<typename T> using Matrix3_SIMD = glm::tmat3x3<T, glm::aligned>;
	template<typename T> using Matrix4_SIMD = glm::tmat4x4<T, glm::aligned>;
	template<typename T> using Matrix2 = glm::tmat2x2<T, glm::packed>;
	template<typename T> using Matrix3 = glm::tmat3x3<T, glm::packed>;
	template<typename T> using Matrix4 = Matrix4_SIMD<T>;
	//template<typename T> using Matrix4 = glm::tmat4x4<T, glm::packed>;

	typedef Matrix2<float> Matrix2f;
	typedef Matrix2<double> Matrix2d;
	typedef Matrix2<int32_t> Matrix2i;
	typedef Matrix2<int64_t> Matrix2l;

	typedef Matrix3<float> Matrix3f;
	typedef Matrix3<double> Matrix3d;
	typedef Matrix3<int32_t> Matrix3i;
	typedef Matrix3<int64_t> Matrix3l;

	typedef Matrix4<float> Matrix4f;
	typedef Matrix4<double> Matrix4d;
	typedef Matrix4<int32_t> Matrix4i;
	typedef Matrix4<int64_t> Matrix4l;

	typedef Matrix2_SIMD<float> Matrix2f_SIMD;
	typedef Matrix2_SIMD<double> Matrix2d_SIMD;
	typedef Matrix2_SIMD<int32_t> Matrix2i_SIMD;
	typedef Matrix2_SIMD<int64_t> Matrix2l_SIMD;

	typedef Matrix3_SIMD<float> Matrix3f_SIMD;
	typedef Matrix3_SIMD<double> Matrix3d_SIMD;
	typedef Matrix3_SIMD<int32_t> Matrix3i_SIMD;
	typedef Matrix3_SIMD<int64_t> Matrix3l_SIMD;

	typedef Matrix4_SIMD<float> Matrix4f_SIMD;
	typedef Matrix4_SIMD<double> Matrix4d_SIMD;
	typedef Matrix4_SIMD<int32_t> Matrix4i_SIMD;
	typedef Matrix4_SIMD<int64_t> Matrix4l_SIMD;

	template<typename T> using Vector2_SIMD = glm::tvec2<T, glm::aligned>;
	template<typename T> using Vector3_SIMD = glm::tvec3<T, glm::aligned>;
	template<typename T> using Vector4_SIMD = glm::tvec4<T, glm::aligned>;
	template<typename T> using Vector2 = glm::tvec2<T, glm::packed>;
	template<typename T> using Vector3 = glm::tvec3<T, glm::packed>;
	template<typename T> using Vector4 = Vector4_SIMD<T>;
	//template<typename T> using Vector4 = glm::tvec4<T, glm::packed>;

	typedef Vector2<float>    Vector2f;
	typedef Vector2<double>   Vector2d;
	typedef Vector2<int8_t>   Vector2c;
	typedef Vector2<int16_t>  Vector2s;
	typedef Vector2<int32_t>  Vector2i;
	typedef Vector2<int64_t>  Vector2l;
	typedef Vector2<uint8_t>  Vector2uc;
	typedef Vector2<uint16_t> Vector2us;
	typedef Vector2<uint32_t> Vector2ui;
	typedef Vector2<uint64_t> Vector2ul;

	typedef Vector3<float>    Vector3f;
	typedef Vector3<double>   Vector3d;
	typedef Vector3<int8_t>   Vector3c;
	typedef Vector3<int16_t>  Vector3s;
	typedef Vector3<int32_t>  Vector3i;
	typedef Vector3<int64_t>  Vector3l;
	typedef Vector3<uint8_t>  Vector3uc;
	typedef Vector3<uint16_t> Vector3us;
	typedef Vector3<uint32_t> Vector3ui;
	typedef Vector3<uint64_t> Vector3ul;

	typedef Vector4<float>    Vector4f;
	typedef Vector4<double>   Vector4d;
	typedef Vector4<int8_t>   Vector4c;
	typedef Vector4<int16_t>  Vector4s;
	typedef Vector4<int32_t>  Vector4i;
	typedef Vector4<int64_t>  Vector4l;
	typedef Vector4<uint8_t>  Vector4uc;
	typedef Vector4<uint16_t> Vector4us;
	typedef Vector4<uint32_t> Vector4ui;
	typedef Vector4<uint64_t> Vector4ul;

	typedef Vector2_SIMD<float>    Vector2f_SIMD;
	typedef Vector2_SIMD<double>   Vector2d_SIMD;
	typedef Vector2_SIMD<int8_t>   Vector2c_SIMD;
	typedef Vector2_SIMD<int16_t>  Vector2s_SIMD;
	typedef Vector2_SIMD<int32_t>  Vector2i_SIMD;
	typedef Vector2_SIMD<int64_t>  Vector2l_SIMD;
	typedef Vector2_SIMD<uint8_t>  Vector2uc_SIMD;
	typedef Vector2_SIMD<uint16_t> Vector2us_SIMD;
	typedef Vector2_SIMD<uint32_t> Vector2ui_SIMD;
	typedef Vector2_SIMD<uint64_t> Vector2ul_SIMD;

	typedef Vector3_SIMD<float>    Vector3f_SIMD;
	typedef Vector3_SIMD<double>   Vector3d_SIMD;
	typedef Vector3_SIMD<int8_t>   Vector3c_SIMD;
	typedef Vector3_SIMD<int16_t>  Vector3s_SIMD;
	typedef Vector3_SIMD<int32_t>  Vector3i_SIMD;
	typedef Vector3_SIMD<int64_t>  Vector3l_SIMD;
	typedef Vector3_SIMD<uint8_t>  Vector3uc_SIMD;
	typedef Vector3_SIMD<uint16_t> Vector3us_SIMD;
	typedef Vector3_SIMD<uint32_t> Vector3ui_SIMD;
	typedef Vector3_SIMD<uint64_t> Vector3ul_SIMD;

	typedef Vector4_SIMD<float>    Vector4f_SIMD;
	typedef Vector4_SIMD<double>   Vector4d_SIMD;
	typedef Vector4_SIMD<int8_t>   Vector4c_SIMD;
	typedef Vector4_SIMD<int16_t>  Vector4s_SIMD;
	typedef Vector4_SIMD<int32_t>  Vector4i_SIMD;
	typedef Vector4_SIMD<int64_t>  Vector4l_SIMD;
	typedef Vector4_SIMD<uint8_t>  Vector4uc_SIMD;
	typedef Vector4_SIMD<uint16_t> Vector4us_SIMD;
	typedef Vector4_SIMD<uint32_t> Vector4ui_SIMD;
	typedef Vector4_SIMD<uint64_t> Vector4ul_SIMD;

	template<typename T> using Quaternion = glm::tquat<T, glm::aligned>;
	typedef Quaternion<float> QuaternionF;
	typedef Quaternion<double> QuaternionD;
	typedef Quaternion<int> QuaternionI;
}
