#pragma once

#include <psemek/vecr/sdf.hpp>
#include <psemek/math/point.hpp>
#include <psemek/math/box.hpp>

#include <functional>
#include <memory>

namespace psemek::vecr
{

	namespace detail
	{

		struct any_base
		{
			virtual sdf_sample sdf_func(math::point<float, 2> const & p) const = 0;
			virtual math::box<float, 2> bbox_func() const = 0;

			virtual ~any_base() {}
		};

		template <typename Shape>
		struct any_impl
			: any_base
		{
			any_impl(Shape && shape)
				: shape(std::move(shape))
			{}

			any_impl(Shape const & shape)
				: shape(shape)
			{}

			Shape shape;

			sdf_sample sdf_func(math::point<float, 2> const & p) const override
			{
				return sdf(shape, p);
			}

			math::box<float, 2> bbox_func() const override
			{
				return bbox(shape);
			}
		};

	}

	struct any
	{
		any() = default;
		any(any const &) = default;
		any(any &&) = default;

		template <typename Shape>
		any(Shape && shape)
		{
			*this = std::forward<Shape>(shape);
		}

		any & operator = (any &&) = default;
		any & operator = (any const &) = default;

		template <typename Shape>
		any & operator = (Shape && shape)
		{
			impl_ = std::make_shared<detail::any_impl<std::decay_t<Shape>>>(std::forward<Shape>(shape));
			return *this;
		}

		explicit operator bool() const
		{
			return static_cast<bool>(impl_);
		}

		friend sdf_sample sdf(any const & s, math::point<float, 2> const & p);
		friend math::box<float, 2> bbox(any const & s);

	private:
		std::shared_ptr<detail::any_base> impl_;
	};

	inline sdf_sample sdf(any const & s, math::point<float, 2> const & p)
	{
		return s.impl_ ? s.impl_->sdf_func(p) : sdf_sample{};
	}

	inline math::box<float, 2> bbox(any const & s)
	{
		return s.impl_ ? s.impl_->bbox_func() : math::box<float, 2>{};
	}

}
