#pragma once

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

#include <iterator>

namespace psemek::cg
{

	template <typename Iterator>
	auto polygon_area(Iterator begin, Iterator end)
	{
		using scalar_type = std::remove_cvref_t<decltype((*begin)[0])>;
		scalar_type result{};

		using point_type = std::remove_cvref_t<decltype(*begin)>;
		auto origin = point_type::zero();

		for (auto it = begin, prev = std::ranges::prev(end); it != end; prev = it++)
		{
			result += math::volume(origin, *prev, *it);
		}

		return result / scalar_type{2};
	}

	template <typename Iterator, typename Point>
	auto polygon_inertia(Iterator begin, Iterator end, Point const & origin)
	{
		using scalar_type = std::remove_cvref_t<decltype((*begin)[0])>;
		scalar_type result{};

		for (auto it = begin, prev = std::ranges::prev(end); it != end; prev = it++)
		{
			auto const v0 = *prev - origin;
			auto const v1 = *it - origin;
			result += (v1[1] - v0[1]) * (v0[0] * v0[0] * v0[0] + v0[0] * v0[0] * v1[0] + v0[0] * v1[0] * v1[0] + v1[0] * v1[0] * v1[0]);
			result -= (v1[0] - v0[0]) * (v0[1] * v0[1] * v0[1] + v0[1] * v0[1] * v1[1] + v0[1] * v1[1] * v1[1] + v1[1] * v1[1] * v1[1]);
		}

		return result / scalar_type{12};
	}

	template <typename Iterator>
	auto polygon_center(Iterator begin, Iterator end)
	{
		auto const first = *begin;

		auto offset = decltype(*begin - *begin)::zero();

		std::size_t count = 0;

		for (; begin != end; ++begin)
		{
			offset += *begin - first;
			++count;
		}

		using scalar_type = std::remove_cvref_t<decltype((*begin)[0])>;
		return first + offset / static_cast<scalar_type>(count);
	}

	template <typename Iterator>
	auto polygon_mass_center(Iterator begin, Iterator end)
	{
		using scalar_type = std::remove_cvref_t<decltype((*begin)[0])>;
		using result_type = std::remove_cvref_t<decltype(*begin)>;

		auto result = result_type::zero();

		for (auto it = begin, prev = std::prev(end); it != end; prev = it++)
		{
			auto const v0 = *prev;
			auto const v1 = *it;
			result[0] += (v1[1] - v0[1]) * (v0[0] * v0[0] + v0[0] * v1[0] + v1[0] * v1[0]);
			result[1] -= (v1[0] - v0[0]) * (v0[1] * v0[1] + v0[1] * v1[1] + v1[1] * v1[1]);
		}

		scalar_type a = polygon_area(begin, end) * 6;

		result[0] /= a;
		result[1] /= a;

		return result;
	}

}
