#pragma once

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

#include <cstdint>

namespace psemek::cg
{

	template <typename T>
	struct triangular_prism
	{
		triangular_prism() = default;
		triangular_prism(math::triangle<math::point<T, 3>> const & t, math::vector<T, 3> const & d);

		std::array<math::point<T, 3>, 6> vertices;
		std::array<math::vector<T, 3>, 4> edge_directions;
	};

	template <typename T>
	triangular_prism<T>::triangular_prism(math::triangle<math::point<T, 3>> const & t, math::vector<T, 3> const & d)
	{
		for (std::size_t i = 0; i < 3; ++i)
		{
			vertices[i] = t[i];
			vertices[i + 3] = t[i] + d;
		}

		if (math::dot(math::normal(vertices[0], vertices[1], vertices[2]), d) < 0)
		{
			std::swap(vertices[1], vertices[2]);
			std::swap(vertices[4], vertices[5]);
		}

		for (std::size_t i = 0; i < 3; ++i)
		{
			edge_directions[i] = math::normalized(vertices[(i + 1) % 3] - vertices[i]);
		}
		edge_directions[3] = math::normalized(d);
	}

	template <typename T>
	auto const & vertices(triangular_prism<T> const & p)
	{
		return p.vertices;
	}

	template <typename T>
	auto const & edges(triangular_prism<T> const &)
	{
		static const std::array<math::segment<std::uint8_t>, 9> result =
		{{
			{ 0, 1 },
			{ 1, 2 },
			{ 2, 0 },

			{ 3, 4 },
			{ 4, 5 },
			{ 5, 3 },

			{ 0, 3 },
			{ 1, 4 },
			{ 2, 5 },
		}};

		return result;
	}

	template <typename T>
	auto const & faces(triangular_prism<T> const &)
	{
		static std::array<std::vector<std::uint8_t>, 5> result =
		{{
			{ 0, 2, 1 },
			{ 3, 4, 5 },
			{ 0, 1, 4, 3 },
			{ 1, 2, 5, 4 },
			{ 2, 0, 3, 5 },
		}};
		return result;
	}

	template <typename T>
	auto const & edge_directions(triangular_prism<T> const & p)
	{
		return p.edge_directions;
	}

	template <typename T>
	struct irregular_triangular_prism
	{
		irregular_triangular_prism() = default;
		irregular_triangular_prism(math::triangle<math::point<T, 3>> const & low_triangle, math::triangle<math::point<T, 3>> const & high_triangle);

		std::array<math::point<T, 3>, 6> vertices;
		std::array<math::vector<T, 3>, 9> edge_directions;
	};

	template <typename T>
	irregular_triangular_prism<T>::irregular_triangular_prism(math::triangle<math::point<T, 3>> const & low_triangle, math::triangle<math::point<T, 3>> const & high_triangle)
	{
		for (std::size_t i = 0; i < 3; ++i)
		{
			vertices[i] = low_triangle[i];
			vertices[i + 3] = high_triangle[i];
		}

		for (std::size_t i = 0; i < 3; ++i)
		{
			edge_directions[i + 0] = math::normalized(vertices[(i + 1) % 3] - vertices[i]);
			edge_directions[i + 3] = math::normalized(vertices[3 + ((i + 1) % 3)] - vertices[3 + i]);
			edge_directions[i + 6] = math::normalized(vertices[i + 3] - vertices[i]);
		}
	}

	template <typename T>
	auto const & vertices(irregular_triangular_prism<T> const & p)
	{
		return p.vertices;
	}

	template <typename T>
	auto const & edges(irregular_triangular_prism<T> const &)
	{
		return edges(triangular_prism<T>{});
	}

	template <typename T>
	auto const & faces(irregular_triangular_prism<T> const &)
	{
		return faces(triangular_prism<T>{});
	}

	template <typename T>
	auto const & edge_directions(irregular_triangular_prism<T> const & p)
	{
		return p.edge_directions;
	}

}
