#pragma once

#include <psemek/math/vector.hpp>
#include <psemek/math/matrix.hpp>

namespace psemek::math
{

	template <typename T, std::size_t D>
	struct perspective;

	template <typename T>
	struct perspective<T, 3>
	{
		using scalar_type = T;
		using homogeneous_matrix_type = matrix<T, 4, 4>;

		// fov are in radians
		perspective(scalar_type fov_x, scalar_type fov_y, scalar_type near, scalar_type far);

		perspective(scalar_type left, scalar_type right, scalar_type bottom, scalar_type top, scalar_type near, scalar_type far);

		homogeneous_matrix_type homogeneous_matrix() const;

	private:
		homogeneous_matrix_type homogeneous_matrix_;
	};

	template <typename T>
	perspective(T, T, T, T) -> perspective<T, 3>;

	template <typename T>
	perspective(T, T, T, T, T, T) -> perspective<T, 3>;

	template <typename T>
	perspective<T, 3>::perspective(scalar_type fov_x, scalar_type fov_y, scalar_type near, scalar_type far)
		: homogeneous_matrix_(homogeneous_matrix_type::zero())
	{
		scalar_type dx = 1 / std::tan(fov_x / 2);
		scalar_type dy = 1 / std::tan(fov_y / 2);
		homogeneous_matrix_[0][0] = dx;
		homogeneous_matrix_[1][1] = dy;
		homogeneous_matrix_[2][2] = (near + far) / (near - far);
		homogeneous_matrix_[2][3] = 2 * near * far / (near - far);
		homogeneous_matrix_[3][2] = -1;
	}

	template <typename T>
	perspective<T, 3>::perspective(scalar_type left, scalar_type right, scalar_type bottom, scalar_type top, scalar_type near, scalar_type far)
		: homogeneous_matrix_(homogeneous_matrix_type::zero())
	{
		homogeneous_matrix_[0][0] = 2 * near / (right - left);
		homogeneous_matrix_[1][1] = 2 * near / (top - bottom);
		homogeneous_matrix_[0][2] = (right + left) / (right - left);
		homogeneous_matrix_[1][2] = (top + bottom) / (top - bottom);
		homogeneous_matrix_[2][2] = - (far + near) / (far - near);
		homogeneous_matrix_[2][3] = - 2 * far * near / (far - near);
		homogeneous_matrix_[3][2] = -1;
	}

	template <typename T>
	matrix<T, 4, 4> perspective<T, 3>::homogeneous_matrix() const
	{
		return homogeneous_matrix_;
	}

}
