#pragma once

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

#include <psemek/math/box.hpp>

namespace psemek::cg
{

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

	template <typename T, std::size_t N>
	box(math::box<T, N>) -> box<T, N>;

	template <typename T>
	struct box<T, 2>
	{
		box() = default;
		box(math::box<T, 2> const & b);

		std::array<math::point<T, 2>, 4> vertices;
	};

	template <typename T>
	box<T, 2>::box(math::box<T, 2> const & b)
	{
		for (std::size_t y = 0; y < 2; ++y)
		{
			for (std::size_t x = 0; x < 2; ++x)
			{
				std::size_t i = y * 2 + x;

				vertices[i][0] = (x == 0) ? b[0].min : b[0].max;
				vertices[i][1] = (y == 0) ? b[1].min : b[1].max;
			}
		}
	}

	template <typename T>
	auto const & vertices(box<T, 2> const & b)
	{
		return b.vertices;
	}

	template <typename T>
	auto const & edges(box<T, 2> const &)
	{
		static const std::array<math::segment<std::uint8_t>, 12> result =
		{{
			{ 0b00, 0b01 },
			{ 0b01, 0b11 },
			{ 0b11, 0b10 },
			{ 0b10, 0b00 },
		}};

		return result;
	}

	template <typename T>
	struct box<T, 3>
	{
		box() = default;
		box(math::box<T, 3> const & b);

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

	template <typename T>
	box<T, 3>::box(math::box<T, 3> const & b)
	{
		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 + x;

					vertices[i][0] = (x == 0) ? b[0].min : b[0].max;
					vertices[i][1] = (y == 0) ? b[1].min : b[1].max;
					vertices[i][2] = (z == 0) ? b[2].min : b[2].max;
				}
			}
		}
	}

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

	template <typename T>
	auto const & edges(box<T, 3> const &)
	{
		static const std::array<math::segment<std::uint8_t>, 12> result =
		{{
			{ 0b000, 0b001 },
			{ 0b010, 0b011 },
			{ 0b100, 0b101 },
			{ 0b110, 0b111 },

			{ 0b000, 0b010 },
			{ 0b001, 0b011 },
			{ 0b100, 0b110 },
			{ 0b101, 0b111 },

			{ 0b000, 0b100 },
			{ 0b001, 0b101 },
			{ 0b010, 0b110 },
			{ 0b011, 0b111 },
		}};

		return result;
	}

	template <typename T>
	auto const & faces(box<T, 3> const &)
	{
		static const std::array<std::array<std::uint8_t, 4>, 6> result =
		{{
			{{ 0b000, 0b100, 0b110, 0b010 }},
			{{ 0b001, 0b011, 0b111, 0b101 }},
			{{ 0b000, 0b001, 0b101, 0b100 }},
			{{ 0b010, 0b110, 0b111, 0b011 }},
			{{ 0b000, 0b010, 0b011, 0b001 }},
			{{ 0b100, 0b101, 0b111, 0b110 }},
		}};

		return result;
	}

	template <typename T>
	auto const & face_normals(box<T, 3> const &)
	{
		static const std::array<math::vector<T, 3>, 6> result =
		{{
			{-1,  0,  0},
			{ 1,  0,  0},
			{ 0, -1,  0},
			{ 0,  1,  0},
			{ 0,  0, -1},
			{ 0,  0,  1},
		}};

		return result;
	}

	template <typename T>
	auto const & edge_directions(box<T, 3> const &)
	{
		static const std::array<math::vector<T, 3>, 3> result =
		{{
			{ 1,  0,  0},
			{ 0,  1,  0},
			{ 0,  0,  1},
		}};

		return result;
	}

}
