/*
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 "base/object_ptr.h"
#include "data/data_stories.h"
#include "ui/effects/animations.h"

namespace style {
struct ReportBox;
} // namespace style

namespace base {
class PowerSaveBlocker;
} // namespace base

namespace ChatHelpers {
class Show;
struct FileChosen;
} // namespace ChatHelpers

namespace Data {
struct FileOrigin;
class DocumentMedia;
} // namespace Data

namespace HistoryView::Reactions {
struct ChosenReaction;
enum class AttachSelectorResult;
} // namespace HistoryView::Reactions

namespace Ui {
class RpWidget;
class BoxContent;
class PopupMenu;
} // namespace Ui

namespace Ui::Toast {
struct Config;
} // namespace Ui::Toast

namespace Main {
class Session;
class SessionShow;
} // namespace Main

namespace Media::Player {
struct TrackState;
} // namespace Media::Player

namespace Media::Stories {

class Header;
class Slider;
class ReplyArea;
class Reactions;
class RecentViews;
class Sibling;
class Delegate;
struct SiblingView;
enum class SiblingType;
struct ContentLayout;
class CaptionFullView;
class RepostView;
enum class ReactionsMode;
class StoryAreaView;
struct RepostClickHandler;

enum class HeaderLayout {
	Normal,
	Outside,
};

enum class PauseState {
	Playing,
	Paused,
	Inactive,
	InactivePaused,
};

struct SiblingLayout {
	QRect geometry;
	QRect userpic;
	QRect nameBoundingRect;
	int nameFontSize = 0;

	friend inline bool operator==(SiblingLayout, SiblingLayout) = default;
};

struct Layout {
	QRect content;
	QRect header;
	QRect slider;
	QRect reactions;
	int controlsWidth = 0;
	QPoint controlsBottomPosition;
	QRect views;
	QRect autocompleteRect;
	HeaderLayout headerLayout = HeaderLayout::Normal;
	SiblingLayout siblingLeft;
	SiblingLayout siblingRight;

	friend inline bool operator==(Layout, Layout) = default;
};

class Controller final : public base::has_weak_ptr {
public:
	explicit Controller(not_null<Delegate*> delegate);
	~Controller();

	[[nodiscard]] Data::Story *story() const;
	[[nodiscard]] not_null<Ui::RpWidget*> wrap() const;
	[[nodiscard]] Layout layout() const;
	[[nodiscard]] rpl::producer<Layout> layoutValue() const;
	[[nodiscard]] ContentLayout contentLayout() const;
	[[nodiscard]] bool closeByClickAt(QPoint position) const;
	[[nodiscard]] Data::FileOrigin fileOrigin() const;
	[[nodiscard]] TextWithEntities captionText() const;
	[[nodiscard]] bool skipCaption() const;
	[[nodiscard]] bool repost() const;
	void toggleLiked();
	void showFullCaption();
	void captionClosing();
	void captionClosed();

	[[nodiscard]] QMargins repostCaptionPadding() const;
	void drawRepostInfo(Painter &p, int x, int y, int availableWidth) const;
	[[nodiscard]] RepostClickHandler lookupRepostHandler(
		QPoint position) const;

	[[nodiscard]] std::shared_ptr<ChatHelpers::Show> uiShow() const;
	[[nodiscard]] auto stickerOrEmojiChosen() const
	-> rpl::producer<ChatHelpers::FileChosen>;

	void show(not_null<Data::Story*> story, Data::StoriesContext context);
	void jumpTo(not_null<Data::Story*> story, Data::StoriesContext context);
	void ready();

	void updateVideoPlayback(const Player::TrackState &state);
	[[nodiscard]] ClickHandlerPtr lookupAreaHandler(QPoint point) const;

	[[nodiscard]] bool subjumpAvailable(int delta) const;
	[[nodiscard]] bool subjumpFor(int delta);
	[[nodiscard]] bool jumpFor(int delta);
	[[nodiscard]] bool paused() const;
	void togglePaused(bool paused);
	void contentPressed(bool pressed);
	void setMenuShown(bool shown);

	[[nodiscard]] PauseState pauseState() const;
	[[nodiscard]] float64 currentVolume() const;
	void toggleVolume();
	void changeVolume(float64 volume);
	void volumeChangeFinished();

	void repaint();
	void repaintSibling(not_null<Sibling*> sibling);
	[[nodiscard]] SiblingView sibling(SiblingType type) const;

	[[nodiscard]] const Data::StoryViews &views(int limit, bool initial);
	[[nodiscard]] rpl::producer<> moreViewsLoaded() const;

	void unfocusReply();
	void shareRequested();
	void deleteRequested();
	void reportRequested();
	void toggleInProfileRequested(bool inProfile);

	[[nodiscard]] bool ignoreWindowMove(QPoint position) const;
	void tryProcessKeyInput(not_null<QKeyEvent*> e);

	[[nodiscard]] bool allowStealthMode() const;
	void setupStealthMode();

	using AttachStripResult = HistoryView::Reactions::AttachSelectorResult;
	[[nodiscard]] AttachStripResult attachReactionsToMenu(
		not_null<Ui::PopupMenu*> menu,
		QPoint desiredPosition);

	[[nodiscard]] rpl::lifetime &lifetime();

private:
	class PhotoPlayback;
	class Unsupported;
	using ChosenReaction = HistoryView::Reactions::ChosenReaction;
	struct StoriesList {
		not_null<PeerData*> peer;
		Data::StoriesIds ids;
		std::vector<StoryId> sorted;
		int total = 0;

		friend inline bool operator==(
			const StoriesList &,
			const StoriesList &) = default;
	};
	struct CachedSource {
		PeerId peerId = 0;
		StoryId shownId = 0;

		explicit operator bool() const {
			return peerId != 0;
		}
	};
	struct ActiveArea {
		QRectF original;
		float64 radiusOriginal = 0.;
		QRect geometry;
		float64 rotation = 0.;
		float64 radius = 0.;
		ClickHandlerPtr handler;
		std::unique_ptr<StoryAreaView> view;
	};

	void initLayout();
	bool changeShown(Data::Story *story);
	void subscribeToSession();
	void updatePhotoPlayback(const Player::TrackState &state);
	void updatePlayback(const Player::TrackState &state);
	void updatePowerSaveBlocker(const Player::TrackState &state);
	void maybeMarkAsRead(const Player::TrackState &state);
	void markAsRead();

	void updateContentFaded();
	void updatePlayingAllowed();
	void setPlayingAllowed(bool allowed);
	void rebuildActiveAreas(const Layout &layout) const;
	void toggleWeatherMode() const;

	void hideSiblings();
	void showSiblings(not_null<Main::Session*> session);
	void showSibling(
		std::unique_ptr<Sibling> &sibling,
		not_null<Main::Session*> session,
		CachedSource cached);

	void subjumpTo(int index);
	void checkWaitingFor();
	void moveFromShown();

	void refreshViewsFromData();
	[[nodiscard]] auto viewsGotMoreCallback()
		-> Fn<void(Data::StoryViews)>;

	[[nodiscard]] bool shown() const;
	[[nodiscard]] PeerData *shownPeer() const;
	[[nodiscard]] int shownCount() const;
	[[nodiscard]] StoryId shownId(int index) const;
	[[nodiscard]] std::unique_ptr<RepostView> validateRepostView(
		not_null<Data::Story*> story);
	void rebuildFromContext(not_null<PeerData*> peer, FullStoryId storyId);
	void checkMoveByDelta();
	void loadMoreToList();
	void preloadNext();
	void rebuildCachedSourcesList(
		const std::vector<Data::StoriesSourceInfo> &lists,
		int index);

	[[nodiscard]] int repostSkipTop() const;
	void updateAreas(Data::Story *story);
	bool reactionChosen(ReactionsMode mode, ChosenReaction chosen);

	const not_null<Delegate*> _delegate;

	rpl::variable<std::optional<Layout>> _layout;

	const not_null<Ui::RpWidget*> _wrap;
	const std::unique_ptr<Header> _header;
	const std::unique_ptr<Slider> _slider;
	const std::unique_ptr<ReplyArea> _replyArea;
	const std::unique_ptr<Reactions> _reactions;
	const std::unique_ptr<RecentViews> _recentViews;
	std::unique_ptr<Unsupported> _unsupported;
	std::unique_ptr<PhotoPlayback> _photoPlayback;
	std::unique_ptr<CaptionFullView> _captionFullView;
	std::unique_ptr<RepostView> _repostView;

	Ui::Animations::Simple _contentFadeAnimation;
	bool _contentFaded = false;

	bool _windowActive = false;
	bool _replyActive = false;
	bool _layerShown = false;
	bool _menuShown = false;
	bool _tooltipShown = false;
	bool _paused = false;

	FullStoryId _shown;
	TextWithEntities _captionText;
	Data::StoriesContext _context;
	std::optional<Data::StoriesSource> _source;
	std::optional<StoriesList> _list;
	FullStoryId _waitingForId;
	int _waitingForDelta = 0;
	int _index = 0;
	int _sliderIndex = 0;
	int _sliderCount = 0;
	bool _started = false;
	bool _viewed = false;

	std::vector<Data::StoryLocation> _locations;
	std::vector<Data::SuggestedReaction> _suggestedReactions;
	std::vector<Data::ChannelPost> _channelPosts;
	std::vector<Data::UrlArea> _urlAreas;
	std::vector<Data::WeatherArea> _weatherAreas;
	mutable std::vector<ActiveArea> _areas;
	mutable rpl::variable<bool> _weatherInCelsius;

	std::vector<CachedSource> _cachedSourcesList;
	int _cachedSourceIndex = -1;
	bool _showingUnreadSources = false;

	Data::StoryViews _viewsSlice;
	rpl::event_stream<> _moreViewsLoaded;
	base::has_weak_ptr _viewsLoadGuard;

	std::unique_ptr<Sibling> _siblingLeft;
	std::unique_ptr<Sibling> _siblingRight;

	std::unique_ptr<base::PowerSaveBlocker> _powerSaveBlocker;

	Main::Session *_session = nullptr;
	rpl::lifetime _sessionLifetime;

	rpl::lifetime _contextLifetime;

	rpl::lifetime _lifetime;

};

[[nodiscard]] Ui::Toast::Config PrepareToggleInProfileToast(
	bool channel,
	int count,
	bool inProfile);
[[nodiscard]] Ui::Toast::Config PrepareTogglePinToast(
	bool channel,
	int count,
	bool pin);
void ReportRequested(
	std::shared_ptr<Main::SessionShow> show,
	FullStoryId id,
	const style::ReportBox *stOverride = nullptr);
[[nodiscard]] object_ptr<Ui::BoxContent> PrepareShortInfoBox(
	not_null<PeerData*> peer);
[[nodiscard]] ClickHandlerPtr MakeChannelPostHandler(
	not_null<Main::Session*> session,
	FullMsgId item);
[[nodiscard]] ClickHandlerPtr MakeUrlAreaHandler(
	base::weak_ptr<Controller> weak,
	const QString &url);

} // namespace Media::Stories
