#pragma once

#include <psemek/cg/body/body.hpp>
#include <psemek/cg/body/box.hpp>

#include <psemek/math/matrix.hpp>
#include <psemek/math/gauss.hpp>
#include <psemek/math/homogeneous.hpp>

namespace psemek::cg
{

	template <typename T, std::size_t N>
	struct frustum;

	template <typename T, std::size_t N>
	frustum(math::matrix<T, N, N>) -> frustum<T, N-1>;

	template <typename T>
	struct frustum<T, 3>
	{
		frustum() = default;
		frustum(math::matrix<T, 4, 4> const & m);

		std::array<math::point<T, 3>, 8> vertices;
	};

	template <typename T>
	frustum<T, 3>::frustum(math::matrix<T, 4, 4> const & m)
	{
		bool flip = (math::det(m) < 0);

		for (std::size_t z = 0; z < 2; ++z)
		{
			for (std::size_t y = 0; y < 2; ++y)
			{
				for (std::size_t x = 0; x < 2; ++x)
				{
					std::size_t i = z * 4 + y * 2 + (flip ? 1 - x : x);

					math::vector<T, 4> p;
					p[0] = (x == 0) ? -1 : 1;
					p[1] = (y == 0) ? -1 : 1;
					p[2] = (z == 0) ? -1 : 1;
					p[3] = 1;

					math::gauss(m, p);

					vertices[i] = math::as_point(p);
				}
			}
		}
	}

	template <typename T>
	auto const & vertices(frustum<T, 3> const & f)
	{
		return f.vertices;
	}

	template <typename T>
	auto const & edges(frustum<T, 3> const &)
	{
		return edges(box<T, 3>{});
	}

	template <typename T>
	auto const & faces(frustum<T, 3> const &)
	{
		return faces(box<T, 3>{});
	}

}
