#pragma once

#include <psemek/gfx/color.hpp>
#include <psemek/gfx/texture.hpp>

#include <psemek/math/vector.hpp>
#include <psemek/math/point.hpp>
#include <psemek/math/matrix.hpp>
#include <psemek/math/box.hpp>

#include <psemek/util/pimpl.hpp>

#include <vector>
#include <memory>

namespace psemek::gfx
{

	struct painter
	{
		using color = color_rgba;

		enum class font
		{
			font_9x12,
		};

		enum class x_align
		{
			left,
			center,
			right,
		};

		enum class y_align
		{
			top,
			center,
			bottom,
		};

		struct text_options
		{
			font f = font::font_9x12;
			math::vector<float, 2> scale = {1.f, 1.f};
			x_align x = x_align::center;
			y_align y = y_align::center;
			color c = {255, 255, 255, 255};
		};

		painter();
		~painter();

		// 2D
		void triangle(math::point<float, 2> const & p0, math::point<float, 2> const & p1, math::point<float, 2> const & p2, color const & c);
		void triangle(math::point<float, 2> const & p0, math::point<float, 2> const & p1, math::point<float, 2> const & p2, color const & c0, color const & c1, color const & c2);
		void rect(math::box<float, 2> const & box, color const & c);
		void circle(math::point<float, 2> const & center, float radius, color const & c, int quality = 24);
		void circle(math::point<float, 2> const & center, float radius, color const & c0, color const & c1, int quality = 24);
		void line(math::point<float, 2> const & p0, math::point<float, 2> const & p1, float width, color const & c, bool smooth = true);
		void line(math::point<float, 2> const & p0, math::point<float, 2> const & p1, float w0, float w1, color const & c0, color const & c1, bool smooth = true);
		void besier(math::point<float, 2> const & p0, math::point<float, 2> const & p1, math::point<float, 2> const & p2, float width, color const & c, int quality = 8, bool smooth = true);
		void polygon(util::span<math::point<float, 2> const> points, color const & c);

		// 2D text
		math::vector<float, 2> text_size(std::string_view str, font f = font::font_9x12);
		void text(math::point<float, 2> const & p, std::string_view str, text_options const & opts);

		void texture(texture_2d const & texture, math::box<float, 2> const & box, color const & c = {0, 0, 0, 0});

		// 3D
		void axes(math::point<float, 3> const & p, float length, float width);
		void sphere(math::point<float, 3> const & p, float radius, color const & c, int quality = 6);
		void line3d(math::point<float, 3> const & p0, math::point<float, 3> const & p1, float width, color const & c);
		void text3d(math::point<float, 3> const & p, std::string_view str, text_options const & opts, math::matrix<float, 3, 3> const & transform);

		// Should be called on each frame
		void render(math::matrix<float, 4, 4> const & transform);

	private:
		psemek_declare_pimpl
	};

}
