/*
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
*/
#pragma once

#include "chat_helpers/tabbed_selector.h"
#include "chat_helpers/stickers.h"
#include "base/variant.h"
#include "base/timer.h"

namespace Window {
class Controller;
} // namespace Window

namespace Ui {
class LinkButton;
class RippleAnimation;
} // namespace Ui

namespace ChatHelpers {

struct StickerIcon;

class StickersListWidget
	: public TabbedSelector::Inner
	, private base::Subscriber
	, private MTP::Sender {
public:
	StickersListWidget(
		QWidget *parent,
		not_null<Window::Controller*> controller);

	rpl::producer<not_null<DocumentData*>> chosen() const;
	rpl::producer<> scrollUpdated() const;
	rpl::producer<> checkForHide() const;

	void refreshRecent() override;
	void preloadImages() override;
	void clearSelection() override;
	object_ptr<TabbedSelector::InnerFooter> createFooter() override;

	void showStickerSet(uint64 setId);
	void showMegagroupSet(ChannelData *megagroup);

	void afterShown() override;
	void beforeHiding() override;

	void refreshStickers();

	void fillIcons(QList<StickerIcon> &icons);
	bool preventAutoHide();

	uint64 currentSet(int yOffset) const;

	void installedLocally(uint64 setId);
	void notInstalledLocally(uint64 setId);
	void clearInstalledLocally();

	void sendSearchRequest();
	void searchForSets(const QString &query);

	~StickersListWidget();

protected:
	void visibleTopBottomUpdated(
		int visibleTop,
		int visibleBottom) override;

	void mousePressEvent(QMouseEvent *e) override;
	void mouseReleaseEvent(QMouseEvent *e) override;
	void mouseMoveEvent(QMouseEvent *e) override;
	void resizeEvent(QResizeEvent *e) override;
	void paintEvent(QPaintEvent *e) override;
	void leaveEventHook(QEvent *e) override;
	void leaveToChildEvent(QEvent *e, QWidget *child) override;
	void enterFromChildEvent(QEvent *e, QWidget *child) override;

	TabbedSelector::InnerFooter *getFooter() const override;
	void processHideFinished() override;
	void processPanelHideFinished() override;
	int countDesiredHeight(int newWidth) override;

private:
	class Footer;

	enum class Section {
		Featured,
		Stickers,
		Search,
	};

	struct OverSticker {
		int section;
		int index;
		bool overDelete;
	};
	struct OverSet {
		int section;
	};
	struct OverButton {
		int section;
	};
	struct OverGroupAdd {
	};
	friend inline bool operator==(OverSticker a, OverSticker b) {
		return (a.section == b.section) && (a.index == b.index) && (a.overDelete == b.overDelete);
	}
	friend inline bool operator==(OverSet a, OverSet b) {
		return (a.section == b.section);
	}
	friend inline bool operator==(OverButton a, OverButton b) {
		return (a.section == b.section);
	}
	friend inline bool operator==(OverGroupAdd a, OverGroupAdd b) {
		return true;
	}
	using OverState = base::optional_variant<OverSticker, OverSet, OverButton, OverGroupAdd>;

	struct SectionInfo {
		int section = 0;
		int count = 0;
		int top = 0;
		int rowsCount = 0;
		int rowsTop = 0;
		int rowsBottom = 0;
	};

	struct Set {
		Set(
			uint64 id,
			MTPDstickerSet::Flags flags,
			const QString &title,
			const QString &shortName,
			ImagePtr thumbnail,
			bool externalLayout,
			int count,
			const Stickers::Pack &pack = Stickers::Pack());
		Set(Set &&other);
		Set &operator=(Set &&other);
		~Set();

		uint64 id = 0;
		MTPDstickerSet::Flags flags = MTPDstickerSet::Flags();
		QString title;
		QString shortName;
		ImagePtr thumbnail;
		Stickers::Pack pack;
		std::unique_ptr<Ui::RippleAnimation> ripple;
		bool externalLayout = false;
		int count = 0;
	};

	template <typename Callback>
	bool enumerateSections(Callback callback) const;
	SectionInfo sectionInfo(int section) const;
	SectionInfo sectionInfoByOffset(int yOffset) const;

	void displaySet(uint64 setId);
	void installSet(uint64 setId);
	void removeMegagroupSet(bool locally);
	void removeSet(uint64 setId);
	void sendInstallRequest(
		uint64 setId,
		const MTPInputStickerSet &input);
	void refreshSearchSets();
	void refreshSearchIndex();

	bool setHasTitle(const Set &set) const;
	bool stickerHasDeleteButton(const Set &set, int index) const;
	Stickers::Pack collectRecentStickers();
	void refreshRecentStickers(bool resize = true);
	void refreshFavedStickers();
	enum class GroupStickersPlace {
		Visible,
		Hidden,
	};
	void refreshMegagroupStickers(GroupStickersPlace place);
	void refreshSettingsVisibility();

	void updateSelected();
	void setSelected(OverState newSelected);
	void setPressed(OverState newPressed);
	std::unique_ptr<Ui::RippleAnimation> createButtonRipple(int section);
	QPoint buttonRippleTopLeft(int section) const;

	enum class ValidateIconAnimations {
		Full,
		Scroll,
		None,
	};
	void validateSelectedIcon(ValidateIconAnimations animations);

	std::vector<Set> &shownSets();
	const std::vector<Set> &shownSets() const;
	int featuredRowHeight() const;
	void readVisibleSets();

	void paintFeaturedStickers(Painter &p, QRect clip);
	void paintStickers(Painter &p, QRect clip);
	void paintMegagroupEmptySet(Painter &p, int y, bool buttonSelected);
	void paintSticker(Painter &p, Set &set, int y, int index, bool selected, bool deleteSelected);
	void paintEmptySearchResults(Painter &p);

	int stickersRight() const;
	bool featuredHasAddButton(int index) const;
	QRect featuredAddRect(int index) const;
	bool hasRemoveButton(int index) const;
	QRect removeButtonRect(int index) const;
	int megagroupSetInfoLeft() const;
	void refreshMegagroupSetGeometry();
	QRect megagroupSetButtonRectFinal() const;

	enum class AppendSkip {
		None,
		Archived,
		Installed,
	};
	void appendSet(
		std::vector<Set> &to,
		uint64 setId,
		bool externalLayout,
		AppendSkip skip = AppendSkip::None);

	void selectEmoji(EmojiPtr emoji);
	int stickersLeft() const;
	QRect stickerRect(int section, int sel);

	void removeRecentSticker(int section, int index);
	void removeFavedSticker(int section, int index);
	void setColumnCount(int count);
	void refreshFooterIcons();

	void cancelSetsSearch();
	void showSearchResults();
	void searchResultsDone(const MTPmessages_FoundStickerSets &result);
	void refreshSearchRows();
	void refreshSearchRows(const std::vector<uint64> *cloudSets);
	void fillLocalSearchRows(const QString &query);
	void fillCloudSearchRows(const std::vector<uint64> &cloudSets);
	void addSearchRow(not_null<const Stickers::Set*> set);

	void showPreview();

	ChannelData *_megagroupSet = nullptr;
	uint64 _megagroupSetIdRequested = 0;
	std::vector<Set> _mySets;
	std::vector<Set> _featuredSets;
	std::vector<Set> _searchSets;
	base::flat_set<uint64> _installedLocallySets;
	std::vector<bool> _custom;
	base::flat_set<not_null<DocumentData*>> _favedStickersMap;

	Section _section = Section::Stickers;

	uint64 _displayingSetId = 0;
	uint64 _removingSetId = 0;

	Footer *_footer = nullptr;
	int _rowsLeft = 0;
	int _columnCount = 1;
	QSize _singleSize;

	OverState _selected;
	OverState _pressed;
	QPoint _lastMousePosition;

	Text _megagroupSetAbout;
	QString _megagroupSetButtonText;
	int _megagroupSetButtonTextWidth = 0;
	QRect _megagroupSetButtonRect;
	std::unique_ptr<Ui::RippleAnimation> _megagroupSetButtonRipple;

	QString _addText;
	int _addWidth;

	object_ptr<Ui::LinkButton> _settings;

	base::Timer _previewTimer;
	bool _previewShown = false;

	std::map<QString, std::vector<uint64>> _searchCache;
	std::vector<std::pair<uint64, QStringList>> _searchIndex;
	base::Timer _searchRequestTimer;
	QString _searchQuery, _searchNextQuery;
	mtpRequestId _searchRequestId = 0;

	rpl::event_stream<not_null<DocumentData*>> _chosen;
	rpl::event_stream<> _scrollUpdated;
	rpl::event_stream<> _checkForHide;

};

} // namespace ChatHelpers
