/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.

For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "info/userpic/info_userpic_colors_editor.h"

#include "base/random.h"
#include "ui/wrap/fade_wrap.h"
#include "info/userpic/info_userpic_emoji_builder_preview.h"
#include "info/userpic/info_userpic_color_circle_button.h"
#include "info/userpic/info_userpic_emoji_builder_common.h"
#include "ui/wrap/vertical_layout.h"
#include "ui/widgets/color_editor.h"
#include "ui/wrap/padding_wrap.h"
#include "ui/widgets/buttons.h"
#include "ui/vertical_list.h"
#include "ui/rect.h"
#include "styles/style_info_userpic_builder.h"
#include "styles/style_userpic_button.h"

namespace UserpicBuilder {
namespace {

constexpr auto kMaxColors = int(4);

[[nodiscard]] QColor RandomColor(const QColor &c) {
	auto random = bytes::vector(2);
	base::RandomFill(random.data(), random.size());
	auto result = QColor();
	result.setHslF(
		(uchar(random[0]) % 100) / 100.,
		(uchar(random[1]) % 50) / 100. + 0.5,
		c.lightnessF());
	return result;
}

class ColorsLine final : public Ui::RpWidget {
public:
	using Chosen = CircleButton;
	using Success = bool;
	ColorsLine(
		not_null<Ui::RpWidget*> parent,
		not_null<std::vector<QColor>*> colors);

	void init();
	void fillButtons();

	[[nodiscard]] Chosen *chosen() const;
	[[nodiscard]] rpl::producer<Chosen*> chosenChanges() const;

private:
	struct ButtonState {
		bool shown = false;
		int left = 0;
	};
	[[nodiscard]] std::vector<ButtonState> calculatePositionFor(int count);
	void processChange(
		const std::vector<QColor> wasColors,
		const std::vector<QColor> nowColors);
	void setLastChosen() const;

	const not_null<std::vector<QColor>*> _colors;

	base::unique_qptr<Ui::RpWidget> _container;

	std::vector<not_null<CircleButton*>> _colorButtons;
	std::vector<not_null<Ui::FadeWrap<Ui::RpWidget>*>> _wraps;

	Ui::Animations::Simple _chooseAnimation;
	Ui::Animations::Simple _positionAnimation;
	Chosen *_chosen = nullptr;

	rpl::event_stream<Chosen*> _chosenChanges;

};

ColorsLine::ColorsLine(
	not_null<Ui::RpWidget*> parent,
	not_null<std::vector<QColor>*> colors)
: Ui::RpWidget(parent)
, _colors(colors) {
}

void ColorsLine::init() {
	fillButtons();
	processChange(*_colors, *_colors);
	setLastChosen();
}

void ColorsLine::fillButtons() {
	_container = base::make_unique_q<Ui::RpWidget>(this);
	const auto container = _container.get();
	sizeValue(
	) | rpl::on_next([=](const QSize &s) {
		container->setGeometry(Rect(s));
	}, container->lifetime());

	const auto minus = Ui::CreateChild<Ui::FadeWrap<Ui::IconButton>>(
		container,
		object_ptr<Ui::IconButton>(
			container,
			st::userpicBuilderEmojiColorMinus));
	_wraps.push_back(minus);
	minus->toggle(_colors->size() > 1, anim::type::instant);
	minus->entity()->setClickedCallback([=] {
		if (_colors->size() < 2) {
			return;
		}
		const auto wasColors = *_colors;
		_colors->erase(_colors->end() - 1);
		const auto nowColors = *_colors;
		processChange(wasColors, nowColors);
		setLastChosen();
	});

	for (auto i = 0; i < kMaxColors; i++) {
		const auto wrap = Ui::CreateChild<Ui::FadeWrap<CircleButton>>(
			container,
			object_ptr<CircleButton>(container));
		const auto button = wrap->entity();
		button->resize(height(), height());
		button->setIndex(i);
		_wraps.push_back(wrap);
		_colorButtons.push_back(button);
		button->setClickedCallback([=] {
			const auto wasChosen = _chosen;
			_chosen = button;
			const auto nowChosen = _chosen;
			_chosenChanges.fire_copy(_chosen);

			_chooseAnimation.stop();
			_chooseAnimation.start([=](float64 progress) {
				if (wasChosen) {
					wasChosen->setSelectedProgress(1. - progress);
				}
				nowChosen->setSelectedProgress(progress);
			}, 0., 1., st::universalDuration);
		});
		if (i < _colors->size()) {
			button->setBrush((*_colors)[i]);
		} else {
			wrap->hide(anim::type::instant);
		}
	}

	const auto plus = Ui::CreateChild<Ui::FadeWrap<Ui::IconButton>>(
		container,
		object_ptr<Ui::IconButton>(
			container,
			st::userpicBuilderEmojiColorPlus));
	_wraps.push_back(plus);
	plus->toggle(_colors->size() < kMaxColors, anim::type::instant);
	plus->entity()->setClickedCallback([=] {
		if (_colors->size() >= kMaxColors) {
			return;
		}
		const auto wasColors = *_colors;
		_colors->push_back(RandomColor(_colors->back()));
		const auto nowColors = *_colors;
		processChange(wasColors, nowColors);
		setLastChosen();
	});
	for (const auto &wrap : _wraps) {
		wrap->setDuration(st::universalDuration);
	}
}

std::vector<ColorsLine::ButtonState> ColorsLine::calculatePositionFor(
		int count) {
	// Minus - Color - Color - Color - Color - Plus.
	auto result = std::vector<ButtonState>(6);
	const auto fullWidth = _container->width();
	const auto width = _container->height();
	const auto colorsWidth = width * count + width * (count - 1);
	const auto left = (fullWidth - colorsWidth) / 2;
	for (auto i = 0; i < _colorButtons.size(); i++) {
		result[i + 1] = {
			.shown = (i < count),
			.left = left + (i * width * 2),
		};
	}
	result[0] = {
		.shown = (count > 1),
		.left = (left - width * 2),
	};
	result[result.size() - 1] = {
		.shown = (count < kMaxColors),
		.left = (left + colorsWidth + width),
	};
	return result;
}

void ColorsLine::processChange(
		const std::vector<QColor> wasColors,
		const std::vector<QColor> nowColors) {
	const auto wasPosition = calculatePositionFor(wasColors.size());
	const auto nowPosition = calculatePositionFor(nowColors.size());
	for (auto i = 0; i < nowPosition.size(); i++) {
		const auto colorIndex = i - 1;
		if ((colorIndex > 0) && (colorIndex < _colors->size())) {
			_colorButtons[colorIndex]->setBrush((*_colors)[colorIndex]);
		}
		_wraps[i]->toggle(nowPosition[i].shown, anim::type::normal);
	}
	_positionAnimation.stop();
	_positionAnimation.start([=](float64 value) {
		for (auto i = 0; i < nowPosition.size(); i++) {
			const auto wasLeft = wasPosition[i].left;
			const auto nowLeft = nowPosition[i].left;
			const auto left = anim::interpolate(wasLeft, nowLeft, value);
			_wraps[i]->moveToLeft(left, 0);
		}
	}, 0., 1., st::universalDuration);
}

void ColorsLine::setLastChosen() const {
	for (auto i = 0; i < _colorButtons.size(); i++) {
		if (i == (_colors->size() - 1)) {
			_colorButtons[i]->clicked({}, Qt::LeftButton);
		}
	}
}

ColorsLine::Chosen *ColorsLine::chosen() const {
	return _chosen;
}

rpl::producer<ColorsLine::Chosen*> ColorsLine::chosenChanges() const {
	return _chosenChanges.events();
}

} // namespace

object_ptr<Ui::RpWidget> CreateGradientEditor(
		not_null<Ui::RpWidget*> parent,
		DocumentData *document,
		std::vector<QColor> startColors,
		BothWayCommunication<std::vector<QColor>> communication) {
	auto container = object_ptr<Ui::VerticalLayout>(parent.get());

	struct State {
		std::vector<QColor> colors;
	};
	const auto preview = container->add(
		object_ptr<EmojiUserpic>(
			container,
			Size(st::defaultUserpicButton.photoSize),
			false),
		style::al_top);
	preview->setDuration(0);
	if (document) {
		preview->setDocument(document);
	}

	Ui::AddSkip(container);
	Ui::AddDivider(container);
	Ui::AddSkip(container);

	const auto state = container->lifetime().make_state<State>();
	state->colors = std::move(startColors);
	const auto buttonsContainer = container->add(object_ptr<ColorsLine>(
		container,
		&state->colors));
	buttonsContainer->resize(0, st::userpicBuilderEmojiAccentColorSize);

	Ui::AddSkip(container);
	Ui::AddDivider(container);
	Ui::AddSkip(container);

	auto ownedEditor = object_ptr<ColorEditor>(
		container,
		ColorEditor::Mode::HSL,
		state->colors.back());
	container->resizeToWidth(ownedEditor->width());
	const auto editor = container->add(std::move(ownedEditor));

	buttonsContainer->chosenChanges(
	) | rpl::on_next([=](ColorsLine::Chosen *chosen) {
		if (chosen) {
			const auto color = state->colors[chosen->index()];
			editor->showColor(color);
			editor->setCurrent(color);
		}
	}, editor->lifetime());

	const auto save = crl::guard(container.data(), [=] {
		communication.result(state->colors);
	});
	// editor->submitRequests(
	// ) | rpl::on_next([=] {
	// }, editor->lifetime());
	editor->colorValue(
	) | rpl::on_next([=](QColor c) {
		if (const auto chosen = buttonsContainer->chosen()) {
			chosen->setBrush(c);
			state->colors[chosen->index()] = c;
		}
		preview->setGradientColors(state->colors);
	}, preview->lifetime());

	base::take(
		communication.triggers
	) | rpl::on_next([=] {
		save();
	}, container->lifetime());

	buttonsContainer->init();

	return container;
}

} // namespace UserpicBuilder

