#pragma once

#include <psemek/gfx/pixmap.hpp>
#include <psemek/pcg/seamless.hpp>
#include <psemek/util/functional.hpp>

#include <vector>
#include <type_traits>

namespace psemek::pcg
{

	namespace detail
	{

		std::vector<float> gauss_coeffs(int size, float sigma);

	}

	template <typename T, typename To, typename From>
	gfx::basic_pixmap<T> blur(gfx::basic_pixmap<T> const & p, To && to, From && from, int size, float sigma, seamless_tag)
	{
		gfx::basic_pixmap<T> res({p.width(), p.height()}, T{});

		using R = std::decay_t<decltype(to(*p.begin()))>;

		auto c = detail::gauss_coeffs(size, sigma);

		for (std::size_t y = 0; y < p.height(); ++y)
		{
			for (std::size_t x = 0; x < p.width(); ++x)
			{
				auto v = R{};
				for (int j = - size; j <= size; ++j)
				{
					for (int i = - size; i <= size; ++i)
					{
						int ix = (x + i);
						while (ix < 0) ix += p.width();
						if (static_cast<std::size_t>(ix) >= p.width()) ix = ix % p.width();

						int iy = (y + j);
						while (iy < 0) iy += p.height();
						if (static_cast<std::size_t>(iy) >= p.height()) iy = iy % p.height();

						v += to(p(ix, iy)) * c[i + size] * c[j + size];
					}
				}
				res(x, y) = from(v);
			}
		}

		return res;
	}

	template <typename T>
	gfx::basic_pixmap<T> blur(gfx::basic_pixmap<T> const & p, int size, float sigma, seamless_tag)
	{
		return blur(p, util::id, util::id, size, sigma, seamless);
	}

	gfx::pixmap_rgb blur(gfx::pixmap_rgb const & p, int size, float sigma, seamless_tag);
	gfx::pixmap_rgba blur(gfx::pixmap_rgba const & p, int size, float sigma, seamless_tag);

}
