#pragma once

#include <psemek/vecr/any.hpp>
#include <psemek/gfx/color.hpp>

#include <functional>

namespace psemek::vecr
{

	gfx::color_4f colorize(gfx::color_rgba const & color, math::point<float, 2> const & p, sdf_sample const & sample);

	struct gradient
	{
		gfx::color_rgba color0 = {0, 0, 0, 0};
		gfx::color_rgba color1 = {0, 0, 0, 0};
		any shape = {};
	};

	gfx::color_4f colorize(gradient const & gradient, math::point<float, 2> const & p, sdf_sample const & sample);

	struct lighting
	{
		any shape = {};
		gfx::color_rgba color0 = {0, 0, 0, 0};
		gfx::color_rgba color1 = {0, 0, 0, 0};
		gfx::color_rgba specular_color = {0, 0, 0, 0};
		float specular_power = 0.f;
		float specular_intensity = 0.f;
		float slope = 1.f;
		math::vector<float, 3> direction = {0.f, 0.f, 1.f};
	};

	gfx::color_4f colorize(lighting const & lighting, math::point<float, 2> const & p, sdf_sample const & sample);

	struct any_colorizer
	{
		any_colorizer() = default;
		any_colorizer(any_colorizer &&) = default;
		any_colorizer(any_colorizer const &) = default;
		any_colorizer & operator = (any_colorizer const &) = default;
		any_colorizer & operator = (any_colorizer &&) = default;

		template <typename Colorizer>
		any_colorizer(Colorizer && colorizer)
		{
			(*this) = std::forward<Colorizer>(colorizer);
		}

		template <typename Colorizer>
		any_colorizer & operator = (Colorizer && colorizer)
		{
			if constexpr (std::is_invocable_v<Colorizer, math::point<float, 2> const &, sdf_sample const &>)
			{
				colorizer_ = std::move(colorizer);
			}
			else
			{
				colorizer_ = [colorizer = std::forward<Colorizer>(colorizer)](math::point<float, 2> const & p, sdf_sample const & sample){
					return colorize(colorizer, p, sample);
				};
			}
			return *this;
		}

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

		friend gfx::color_4f colorize(any_colorizer const & colorizer, math::point<float, 2> const & p, sdf_sample const & sample)
		{
			return colorizer.colorizer_ ? colorizer.colorizer_(p, sample) : gfx::color_4f::zero();
		}

	private:
		std::function<gfx::color_4f(math::point<float, 2> const &, sdf_sample const &)> colorizer_;
	};

}
