/*
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 "history/view/history_view_element.h"
#include "history/view/history_view_bottom_info.h"
#include "ui/effects/animations.h"

class HistoryItem;
struct HistoryMessageEdited;
struct HistoryMessageForwarded;
struct HistoryMessageReplyMarkup;
struct HistoryMessageSuggestion;
struct HistoryMessageReply;

namespace Data {
struct ReactionId;
} // namespace Data

namespace Ui {
struct BubbleRounding;
class RoundCheckbox;
} // namespace Ui

namespace HistoryView {

class ViewButton;
class WebPage;
class TranscribeButton;

namespace Reactions {
class InlineList;
} // namespace Reactions

namespace ReplyButton {
struct ButtonParameters;
} // namespace ReplyButton

// Special type of Component for the channel actions log.
struct LogEntryOriginal : RuntimeComponent<LogEntryOriginal, Element> {
	LogEntryOriginal();
	LogEntryOriginal(LogEntryOriginal &&other);
	LogEntryOriginal &operator=(LogEntryOriginal &&other);
	~LogEntryOriginal();

	std::unique_ptr<WebPage> page;
};

struct Factcheck : RuntimeComponent<Factcheck, Element> {
	std::unique_ptr<WebPage> page;
	bool expanded = false;
};

struct PsaTooltipState : RuntimeComponent<PsaTooltipState, Element> {
	QString type;
	mutable ClickHandlerPtr link;
	mutable Ui::Animations::Simple buttonVisibleAnimation;
	mutable bool buttonVisible = true;
};

struct InstantViewMediaRuntime
: RuntimeComponent<InstantViewMediaRuntime, Element> {
	QString pageUrl;
};

enum class BadgeRole : uchar {
	User,
	Admin,
	Creator,
};

struct RightBadge : RuntimeComponent<RightBadge, Element> {
	Ui::Text::String tag;
	Ui::Text::String boosts;
	mutable ClickHandlerPtr tagLink;
	mutable ClickHandlerPtr boostsLink;
	int width = 0;
	BadgeRole role = BadgeRole::User;
	bool overridden = false;
	bool special = false;
	mutable std::unique_ptr<Ui::RippleAnimation> ripple;
	mutable QPoint lastPoint;
};

struct TextAppearing : RuntimeComponent<TextAppearing, Element> {
	std::vector<Ui::Text::LineLayoutInfo> lines;
	int textWidth = 0;
	int shownLine = 0;
	int revealedLineWidth = 0;
	int startLineWidth = 0;
	int targetLineWidth = 0;
	int shownWidth = 0;
	int shownHeight = 0;
	int targetHeight = 0;
	crl::time widthDuration = 0;
	Ui::Animations::Simple widthAnimation;
	Ui::Animations::Simple heightAnimation;
	bool geometryValid = false;
	bool startedForText = false;
	bool finalizing = false;
	bool use = false;
	mutable QImage lineCache;
	mutable QImage gradientMask;
};

struct BottomRippleMask {
	QImage image;
	int shift = 0;
};

extern const char kOptionUnlimitedMessageWidth[];

class Message final : public Element {
public:
	Message(
		not_null<ElementDelegate*> delegate,
		not_null<HistoryItem*> data,
		Element *replacing);
	~Message();

	void clickHandlerPressedChanged(
		const ClickHandlerPtr &handler,
		bool pressed) override;

	[[nodiscard]] const HistoryMessageEdited *displayedEditBadge() const;
	[[nodiscard]] HistoryMessageEdited *displayedEditBadge();

	bool embedReactionsInBubble() const override;

	int marginTop() const override;
	int marginBottom() const override;
	void draw(Painter &p, const PaintContext &context) const override;
	PointState pointState(QPoint point) const override;
	TextState textState(
		QPoint point,
		StateRequest request) const override;
	void updatePressed(QPoint point) override;
	void drawInfo(
		Painter &p,
		const PaintContext &context,
		int right,
		int bottom,
		int width,
		InfoDisplayType type) const override;
	TextState bottomInfoTextState(
		int right,
		int bottom,
		QPoint point,
		InfoDisplayType type) const override;
	TextForMimeData selectedText(TextSelection selection) const override;
	SelectedQuote selectedQuote(TextSelection selection) const override;
	TextSelection selectionFromQuote(
		const SelectedQuote &quote) const override;
	TextSelection adjustSelection(
		TextSelection selection,
		TextSelectType type) const override;

	Reactions::ButtonParameters reactionButtonParameters(
		QPoint position,
		const TextState &reactionState) const override;
	ReplyButton::ButtonParameters replyButtonParameters(
		QPoint position,
		const TextState &replyState) const override;
	int reactionsOptimalWidth() const override;

	void unloadHeavyPart() override;

	// hasFromPhoto() returns true even if we don't display the photo
	// but we need to skip a place at the left side for this photo
	bool hasFromPhoto() const override;
	bool displayFromPhoto() const override;
	bool hasFromName() const override;
	bool displayFromName() const override;
	bool displayForwardedFrom() const override;
	bool hasOutLayout() const override;
	bool drawBubble() const override;
	bool hasBubble() const override;
	TopicButton *displayedTopicButton() const override;
	bool unwrapped() const override;
	int minWidthForMedia() const override;
	bool displayRightActionComments() const;
	std::optional<QSize> rightActionSize() const override;
	void drawRightAction(
		Painter &p,
		const PaintContext &context,
		int left,
		int top,
		int outerWidth) const override;
	[[nodiscard]] ClickHandlerPtr rightActionLink(
		std::optional<QPoint> pressPoint) const override;
	[[nodiscard]] TimeId displayedEditDate() const override;
	[[nodiscard]] bool toggleSelectionByHandlerClick(
		const ClickHandlerPtr &handler) const override;
	[[nodiscard]] bool allowTextSelectionByHandler(
		const ClickHandlerPtr &handler) const override;
	[[nodiscard]] int infoWidth() const override;
	[[nodiscard]] int bottomInfoFirstLineWidth() const override;
	[[nodiscard]] bool bottomInfoIsWide() const override;
	[[nodiscard]] bool isSignedAuthorElided() const override;

	void itemDataChanged() override;

	VerticalRepaintRange verticalRepaintRange() const override;

	void applyGroupAdminChanges(
		const base::flat_set<UserId> &changes) override;

	void animateReaction(Ui::ReactionFlyAnimationArgs &&args) override;

	auto takeEffectAnimation()
	-> std::unique_ptr<Ui::ReactionFlyAnimation> override;

	QRect effectIconGeometry() const override;
	QRect innerGeometry() const override;
	QPoint mediaTopLeft() const override;
	[[nodiscard]] BottomRippleMask bottomRippleMask(int buttonHeight) const;

	void setInstantViewMediaRuntime(QString pageUrl);

private:
	struct CommentsButton;
	struct LinkRipple;
	struct FromNameStatus;
	struct RightAction;

	void refreshDataIdHook() override;
	bool hasHeavyPart() const override;

	bool updateBottomInfo();

	void initPaidInformation();
	void refreshSuggestedInfo(
		not_null<HistoryItem*> item,
		not_null<const HistoryMessageSuggestion*> suggest,
		const HistoryMessageReply *reply);
	void initLogEntryOriginal();
	void initPsa();
	void fromNameUpdated(int width) const;

	[[nodiscard]] TextSelection skipTextSelection(
		TextSelection selection) const;
	[[nodiscard]] TextSelection unskipTextSelection(
		TextSelection selection) const;

	void toggleCommentsButtonRipple(bool pressed);
	void createCommentsButtonRipple();

	void toggleTopicButtonRipple(bool pressed);
	void createTopicButtonRipple();

	void toggleLinkRipple(bool pressed);
	void recordLinkRipplePoint(
		QPoint point,
		QPoint textOrigin) const;
	void paintLinkRipple(
		Painter &p,
		const ClickHandlerPtr &handler,
		QRect linkRect,
		QPoint textPosition) const;
	void createLinkRippleMask(
		const QPainterPath &path,
		QPoint textPosition,
		int useWidth,
		style::margins padding,
		int radius) const;
	void createLinkRippleMask(
		QRect linkRect,
		QPoint textPosition,
		style::margins padding,
		int radius) const;

	void toggleRightActionRipple(bool pressed);
	void toggleBadgeRipple(bool pressed);

	void toggleReplyRipple(bool pressed);
	void toggleSummaryHeaderRipple(bool pressed);

	void paintCommentsButton(
		Painter &p,
		QRect &g,
		const PaintContext &context) const;
	void paintFromName(
		Painter &p,
		QRect &trect,
		const PaintContext &context) const;
	void paintTopicButton(
		Painter &p,
		QRect &trect,
		const PaintContext &context) const;
	void paintForwardedInfo(
		Painter &p,
		QRect &trect,
		const PaintContext &context) const;
	void paintReplyInfo(
		Painter &p,
		QRect &trect,
		const PaintContext &context) const;
	void paintSummaryHeaderInfo(
		Painter &p,
		QRect &trect,
		const PaintContext &context) const;
	// This method draws "via @bot" if it is not painted
	// in forwarded info or in from name.
	void paintViaBotIdInfo(
		Painter &p,
		QRect &trect,
		const PaintContext &context) const;
	void paintText(
		Painter &p,
		QRect &trect,
		const PaintContext &context) const;

	bool getStateCommentsButton(
		QPoint point,
		QRect &g,
		not_null<TextState*> outResult) const;
	bool getStateFromName(
		QPoint point,
		QRect &trect,
		not_null<TextState*> outResult) const;
	bool getStateTopicButton(
		QPoint point,
		QRect &trect,
		not_null<TextState*> outResult) const;
	bool getStateForwardedInfo(
		QPoint point,
		QRect &trect,
		not_null<TextState*> outResult,
		StateRequest request) const;
	bool getStateReplyInfo(
		QPoint point,
		QRect &trect,
		not_null<TextState*> outResult) const;
	bool getStateSummaryHeaderInfo(
		QPoint point,
		QRect &trect,
		not_null<TextState*> outResult) const;
	bool getStateViaBotIdInfo(
		QPoint point,
		QRect &trect,
		not_null<TextState*> outResult) const;
	bool getStateText(
		QPoint point,
		QRect &trect,
		not_null<TextState*> outResult,
		StateRequest request) const;

	void updateMediaInBubbleState();
	QRect countGeometry() const;
	[[nodiscard]] Ui::BubbleRounding countMessageRounding() const;
	[[nodiscard]] Ui::BubbleRounding countBubbleRounding(
		Ui::BubbleRounding messageRounding) const;
	[[nodiscard]] Ui::BubbleRounding countBubbleRounding() const;

	int resizeContentGetHeight(int newWidth);
	QSize performCountOptimalSize() override;
	QSize performCountCurrentSize(int newWidth) override;
	bool hasVisibleText() const override;
	[[nodiscard]] int visibleTextLength() const;
	[[nodiscard]] int visibleMediaTextLength() const;
	[[nodiscard]] bool needInfoDisplay() const;
	[[nodiscard]] bool invertMedia() const;
	[[nodiscard]] bool hasFastReply() const;
	[[nodiscard]] bool displayFastReply() const;

	[[nodiscard]] bool isPinnedContext() const;
	[[nodiscard]] bool isCommentsRootView() const;

	[[nodiscard]] bool displayFastShare() const;
	[[nodiscard]] bool displayGoToOriginal() const;
	[[nodiscard]] ClickHandlerPtr fastReplyLink() const;
	[[nodiscard]] ClickHandlerPtr prepareRightActionLink() const;

	void ensureRightAction() const;
	void refreshTopicButton();
	void refreshInfoSkipBlock(HistoryItem *textItem);
	[[nodiscard]] int monospaceMaxWidth() const;
	[[nodiscard]] int bubbleTextWidth(int bubbleWidth) const;
	[[nodiscard]] int bubbleTextualWidth() const;

	void ensureSummarizeButton() const;
	void paintSummarize(
		Painter &p,
		int x,
		int y,
		bool right,
		const PaintContext &context,
		QRect g) const;

	void updateViewButtonExistence();
	[[nodiscard]] int viewButtonHeight() const;

	[[nodiscard]] WebPage *logEntryOriginal() const;
	[[nodiscard]] WebPage *factcheckBlock() const;

	[[nodiscard]] ClickHandlerPtr createGoToCommentsLink() const;
	[[nodiscard]] ClickHandlerPtr psaTooltipLink() const;
	void psaTooltipToggled(bool shown) const;
	void invalidateTextDependentCache() override;

	bool textAppearValidate(not_null<TextAppearing*> appearing);
	bool textAppearCheckLine(not_null<TextAppearing*> appearing);
	void textAppearStartWidthAnimation(not_null<TextAppearing*> appearing);
	void textAppearStartHeightAnimation(
		not_null<TextAppearing*> appearing,
		int targetHeight);
	void textAppearWidthCallback();
	void textAppearHeightCallback();
	[[nodiscard]] int textAppearTargetHeight(
		not_null<TextAppearing*> appearing) const;

	void refreshRightBadge();
	[[nodiscard]] int rightBadgeWidth() const;
	void validateFromNameText(PeerData *from) const;
	void ensureFromNameStatusLink(not_null<PeerData*> peer) const;

	mutable std::unique_ptr<RightAction> _rightAction;
	mutable ClickHandlerPtr _fastReplyLink;
	mutable std::unique_ptr<ViewButton> _viewButton;
	std::unique_ptr<TopicButton> _topicButton;
	mutable std::unique_ptr<LinkRipple> _linkRipple;
	mutable QPoint _linkRippleLastPoint;
	mutable std::unique_ptr<CommentsButton> _comments;
	mutable std::unique_ptr<TranscribeButton> _summarize;

	mutable Ui::Text::String _fromName;
	mutable std::unique_ptr<FromNameStatus> _fromNameStatus;
	mutable std::unique_ptr<Ui::RoundCheckbox> _selectionRoundCheckbox;
	mutable uint32 _fromNameVersion : 16 = 0;
	uint32 _nonTextMaxWidth : 16 = 0;
	mutable int _bubbleTextualWidthMinimum : 16 = -1;
	mutable int _bubbleTextualWidthCache : 16 = 0;
	uint32 _bubbleWidthLimit : 26 = 0;
	uint32 _invertMedia : 1 = 0;
	uint32 _hideReply : 1 = 0;
	uint32 _postShowingAuthor : 1 = 0;
	mutable uint32 _fromLinkRipplePointSet : 1 = 0;

	BottomInfo _bottomInfo;
	mutable QPoint _lastMediaPosition;

};

} // namespace HistoryView
