#include <psemek/ui/color_picker.hpp>
#include <psemek/ui/frame.hpp>
#include <psemek/ui/color_view.hpp>
#include <psemek/ui/grid_layout.hpp>
#include <psemek/ui/slider.hpp>
#include <psemek/ui/label.hpp>
#include <psemek/ui/screen.hpp>
#include <psemek/util/to_string.hpp>

#include <functional>

namespace psemek::ui
{

	namespace
	{

		struct color_picker_impl
			: color_picker
		{
			color_picker_impl(element_factory & factory, bool alpha)
			{
				auto frame = std::make_shared<ui::frame>();
				frame->set_min_size(math::vector{250.f, 0.f});

				auto main_layout = factory.make_grid_layout();
				main_layout->set_size(1, 2);
				main_layout->set_column_weight(0, 0.f);
				main_layout->set_outer_margin(false);
				main_layout->set_width_first(false);

				color_view_ = std::make_shared<ui::color_view>();
				color_view_->set_square(true);

				auto sliders_layout = factory.make_grid_layout();
				sliders_layout->set_size(alpha ? 4 : 3, 2);
				sliders_layout->set_column_weight(0, 0.f);
				sliders_layout->set_outer_margin(false);

				hidden_label_ = factory.make_label("000");
				hidden_label_->set_hidden(true);

				auto hidden_screen = factory.make_screen();
				hidden_screen->add_child(hidden_label_, screen::x_policy::fill, screen::y_policy::fill);

				red_label_ = factory.make_label("");
				red_label_->set_halign(label::halignment::right);
				red_label_->set_valign(label::valignment::center);

				green_label_ = factory.make_label("");
				green_label_->set_halign(label::halignment::right);
				green_label_->set_valign(label::valignment::center);

				blue_label_ = factory.make_label("");
				blue_label_->set_halign(label::halignment::right);
				blue_label_->set_valign(label::valignment::center);

				alpha_label_ = factory.make_label("");
				alpha_label_->set_halign(label::halignment::right);
				alpha_label_->set_valign(label::valignment::center);

				auto slider_callback = [this](int){
					set_color(color(), true);
				};

				red_slider_ = factory.make_slider();
				red_slider_->set_value_range({0, 255}, false);
				red_slider_->on_value_changed(slider_callback, false);

				green_slider_ = factory.make_slider();
				green_slider_->set_value_range({0, 255}, false);
				green_slider_->on_value_changed(slider_callback, false);

				blue_slider_ = factory.make_slider();
				blue_slider_->set_value_range({0, 255}, false);
				blue_slider_->on_value_changed(slider_callback, false);

				alpha_slider_ = factory.make_slider();
				alpha_slider_->set_value_range({0, 255}, false);
				alpha_slider_->on_value_changed(slider_callback, false);

				hidden_screen->add_child(red_label_, screen::x_policy::fill, screen::y_policy::fill);

				sliders_layout->set(0, 0, hidden_screen);
				sliders_layout->set(1, 0, green_label_);
				sliders_layout->set(2, 0, blue_label_);
				sliders_layout->set(0, 1, red_slider_);
				sliders_layout->set(1, 1, green_slider_);
				sliders_layout->set(2, 1, blue_slider_);

				if (alpha)
				{
					sliders_layout->set(3, 0, alpha_label_);
					sliders_layout->set(3, 1, alpha_slider_);
				}

				main_layout->set(0, 0, color_view_);
				main_layout->set(0, 1, sliders_layout);

				frame->set_child(main_layout);

				set_child(frame);
			}

			struct shape const & shape() const override
			{
				return child_->shape();
			}

			void reshape(math::box<float, 2> const & box) override
			{
				child_->reshape(box);
			}

			void draw(painter &) const override
			{}

			math::box<float, 2> size_constraints() const override
			{
				return child_->size_constraints();
			}

			math::interval<float> width_constraints(float height) const override
			{
				return child_->width_constraints(height);
			}

			math::interval<float> height_constraints(float width) const override
			{
				return child_->height_constraints(width);
			}

			gfx::color_rgba color() const override
			{
				return {
					red_slider_->value(),
					green_slider_->value(),
					blue_slider_->value(),
					alpha_slider_->value()
				};
			}

			void set_color(gfx::color_rgba value, bool notify) override
			{
				color_view_->set_color(value);

				red_label_  ->set_tagged_text(util::to_string("[color:f00]", static_cast<int>(value[0]), "[/color]"));
				green_label_->set_tagged_text(util::to_string("[color:0f0]", static_cast<int>(value[1]), "[/color]"));
				blue_label_ ->set_tagged_text(util::to_string("[color:00f]", static_cast<int>(value[2]), "[/color]"));
				alpha_label_ ->set_tagged_text(util::to_string("[color:fff]", static_cast<int>(value[3]), "[/color]"));

				red_slider_->set_value(value[0], false);
				green_slider_->set_value(value[1], false);
				blue_slider_->set_value(value[2], false);
				alpha_slider_->set_value(value[3], false);

				if (notify && on_value_changed_callback_)
					post([cb = on_value_changed_callback_, value]{ cb(value); });
			}

			void style_updated() const override
			{
				color_picker::style_updated();

				auto st = merged_own_style();
				if (!st->font) return;

				auto glyphs = st->font->shape("0123456789", {});

				float max_width = 0.f;
				char max_width_char = '0';
				for (auto const & g : glyphs)
					if (math::make_max(max_width, g.position[0].length()))
						max_width_char = g.character;

				hidden_label_->set_text(std::string(3, max_width_char));
			}

		private:
			std::shared_ptr<color_view> color_view_;
			std::shared_ptr<label> hidden_label_;
			std::shared_ptr<label> red_label_;
			std::shared_ptr<label> green_label_;
			std::shared_ptr<label> blue_label_;
			std::shared_ptr<label> alpha_label_;
			std::shared_ptr<slider> red_slider_;
			std::shared_ptr<slider> green_slider_;
			std::shared_ptr<slider> blue_slider_;
			std::shared_ptr<slider> alpha_slider_;
		};

	}

	std::shared_ptr<color_picker> make_color_picker(element_factory & factory, bool alpha)
	{
		return std::make_shared<color_picker_impl>(factory, alpha);
	}

}
