#pragma once

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

#include <optional>

namespace psemek::vecr
{

	template <typename Shape>
	struct exact
	{
		Shape shape;
		int iterations = 8;
	};

	template <typename Shape>
	sdf_sample sdf(exact<Shape> const & s, math::point<float, 2> const & p)
	{
		// Newton-Raphson method

		std::optional<sdf_sample> first_sample;

		auto q = p;
		for (int i = 0; i < s.iterations; ++i)
		{
			auto sample = sdf(s.shape, q);
			if (first_sample) first_sample = sample;

			auto d = math::length_sqr(sample.gradient);
			if (d > 0.f)
				q -= (sample.value / d) * sample.gradient;
			else
				break;
		}

		auto r = p - q;
		auto l = math::length(r);

		sdf_sample result;
		result.value = l;

		if (l > 0.f)
			result.gradient = r / l;
		else
			result.gradient = {0.f, 0.f};

		if (first_sample->value < 0.f)
		{
			result.value *= -1.f;
			result.gradient *= -1.f;
		}

		return result;
	}

	template <typename Shape>
	math::box<float, 2> bbox(exact<Shape> const & s)
	{
		return bbox(s.shape);
	}

}
