#pragma once

#include <psemek/math/interval.hpp>
#include <psemek/math/box.hpp>
#include <psemek/math/point.hpp>
#include <psemek/math/simplex.hpp>
#include <psemek/math/orientation.hpp>
#include <psemek/util/range.hpp>

namespace psemek::math
{

	template <typename T>
	bool contains(interval<T> const & i, T const & x)
	{
		return (i.min <= x) && (x <= i.max);
	}

	template <typename T, std::size_t N>
	bool contains(box<T, N> const & b, point<T, N> const & p)
	{
		for (std::size_t i = 0; i < N; ++i)
			if (!contains(b[i], p[i]))
				return false;
		return true;
	}

	template <typename T>
	bool contains(interval<T> const & i1, interval<T> const i2)
	{
		return (i1.min <= i2.min) && (i2.max <= i1.max);
	}

	template <typename T, std::size_t N>
	bool contains(box<T, N> const & b1, box<T, N> const & b2)
	{
		for (std::size_t i = 0; i < N; ++i)
			if (!contains(b1[i], b2[i]))
				return false;
		return true;
	}

	template <typename T>
	bool half_open_contains(interval<T> const & i, T const & x)
	{
		return (i.min <= x) && (x < i.max);
	}

	template <typename T, std::size_t N>
	bool half_open_contains(box<T, N> const & b, point<T, N> const & p)
	{
		for (std::size_t i = 0; i < N; ++i)
			if (!half_open_contains(b[i], p[i]))
				return false;
		return true;
	}

	template <typename RobustTag, typename T>
	bool contains(RobustTag robust_tag, simplex<point<T, 2>, 2> const & t, point<T, 2> const & p)
	{
		return true
			&& orientation(robust_tag, t[0], t[1], p) != sign_t::negative
			&& orientation(robust_tag, t[1], t[2], p) != sign_t::negative
			&& orientation(robust_tag, t[2], t[0], p) != sign_t::negative
			;
	}

	template <typename T>
	bool contains(simplex<point<T, 2>, 2> const & t, point<T, 2> const & p)
	{
		return contains(default_robust_tag, t, p);
	}

	template <typename Iterator, typename T>
	bool polygon_contains(Iterator begin, Iterator end, point<T, 2> const & p)
	{
		int count = 0;
		for (auto it = begin, prev = std::prev(end); it != end; prev = it++)
		{
			auto p0 = *prev;
			auto p1 = *it;
			if (p0[1] > p1[1])
				std::swap(p0, p1);

			if (p0[1] <= p[1] && p[1] < p1[1])
			{
				if (orientation(p0, p1, p) == sign_t::positive)
					++count;
			}
		}

		return (count % 2) == 1;
	}

	template <typename Polygon, typename T>
	bool polygon_contains(Polygon const & polygon, point<T, 2> const & p)
	{
		return polygon_contains(util::xbegin(polygon), util::xend(polygon), p);
	}

}
