/*
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 "ui/widgets/dropdown_menu.h"
#include "ui/effects/radial_animation.h"
#include "data/data_shared_media.h"
#include "data/data_user_photos.h"
#include "data/data_web_page.h"

namespace Media {
namespace Player {
struct TrackState;
} // namespace Player
namespace Clip {
class Controller;
} // namespace Clip
namespace View {
class GroupThumbs;
} // namespace View
} // namespace Media

namespace Ui {
class PopupMenu;
class LinkButton;
class RoundButton;
} // namespace Ui

namespace Window {
namespace Theme {
struct Preview;
} // namespace Theme
} // namespace Window

namespace Notify {
struct PeerUpdate;
} // namespace Notify

class MediaView : public TWidget, private base::Subscriber, public ClickHandlerHost {
	Q_OBJECT

public:
	MediaView();

	void setVisible(bool visible) override;

	void showPhoto(not_null<PhotoData*> photo, HistoryItem *context);
	void showPhoto(not_null<PhotoData*> photo, not_null<PeerData*> context);
	void showDocument(not_null<DocumentData*> document, HistoryItem *context);

	void leaveToChildEvent(QEvent *e, QWidget *child) override { // e -- from enterEvent() of child TWidget
		updateOverState(OverNone);
	}
	void enterFromChildEvent(QEvent *e, QWidget *child) override { // e -- from leaveEvent() of child TWidget
		updateOver(mapFromGlobal(QCursor::pos()));
	}

	void close();

	void activateControls();
	void onDocClick();

	void clipCallback(Media::Clip::Notification notification);
	PeerData *ui_getPeerForMouseAction();

	void clearData();

	~MediaView();

	// ClickHandlerHost interface
	void clickHandlerActiveChanged(const ClickHandlerPtr &p, bool active) override;
	void clickHandlerPressedChanged(const ClickHandlerPtr &p, bool pressed) override;

protected:
	void paintEvent(QPaintEvent *e) override;

	void keyPressEvent(QKeyEvent *e) override;
	void wheelEvent(QWheelEvent *e) override;
	void mousePressEvent(QMouseEvent *e) override;
	void mouseDoubleClickEvent(QMouseEvent *e) override;
	void mouseMoveEvent(QMouseEvent *e) override;
	void mouseReleaseEvent(QMouseEvent *e) override;
	void contextMenuEvent(QContextMenuEvent *e) override;
	void touchEvent(QTouchEvent *e);

	bool event(QEvent *e) override;
	bool eventFilter(QObject *obj, QEvent *e) override;

private slots:
	void onHideControls(bool force = false);

	void onScreenResized(int screen);

	void onToMessage();
	void onSaveAs();
	void onDownload();
	void onSaveCancel();
	void onShowInFolder();
	void onForward();
	void onDelete();
	void onOverview();
	void onCopy();
	void onMenuDestroy(QObject *obj);
	void receiveMouse();

	void onDropdown();

	void onTouchTimer();

	void updateImage();

	void onVideoPauseResume();
	void onVideoSeekProgress(TimeMs positionMs);
	void onVideoSeekFinished(TimeMs positionMs);
	void onVideoVolumeChanged(float64 volume);
	void onVideoToggleFullScreen();
	void onVideoPlayProgress(const AudioMsgId &audioId);

private:
	enum OverState {
		OverNone,
		OverLeftNav,
		OverRightNav,
		OverClose,
		OverHeader,
		OverName,
		OverDate,
		OverSave,
		OverMore,
		OverIcon,
		OverVideo,
	};

	void updateOver(QPoint mpos);
	void moveToScreen();
	bool moveToNext(int delta);
	void preloadData(int delta);
	struct Entity {
		base::optional_variant<
			not_null<PhotoData*>,
			not_null<DocumentData*>> data;
		HistoryItem *item;
	};
	Entity entityForUserPhotos(int index) const;
	Entity entityForSharedMedia(int index) const;
	Entity entityForCollage(int index) const;
	Entity entityByIndex(int index) const;
	Entity entityForItemId(const FullMsgId &itemId) const;
	bool moveToEntity(const Entity &entity, int preloadDelta = 0);
	void setContext(base::optional_variant<
		not_null<HistoryItem*>,
		not_null<PeerData*>> context);

	void refreshLang();
	void showSaveMsgFile();
	void updateMixerVideoVolume() const;

	struct SharedMedia;
	using SharedMediaType = SharedMediaWithLastSlice::Type;
	using SharedMediaKey = SharedMediaWithLastSlice::Key;
	std::optional<SharedMediaType> sharedMediaType() const;
	std::optional<SharedMediaKey> sharedMediaKey() const;
	std::optional<SharedMediaType> computeOverviewType() const;
	bool validSharedMedia() const;
	void validateSharedMedia();
	void handleSharedMediaUpdate(SharedMediaWithLastSlice &&update);

	struct UserPhotos;
	using UserPhotosKey = UserPhotosSlice::Key;
	std::optional<UserPhotosKey> userPhotosKey() const;
	bool validUserPhotos() const;
	void validateUserPhotos();
	void handleUserPhotosUpdate(UserPhotosSlice &&update);

	struct Collage;
	using CollageKey = WebPageCollage::Item;
	std::optional<CollageKey> collageKey() const;
	bool validCollage() const;
	void validateCollage();

	Data::FileOrigin fileOrigin() const;

	void refreshCaption(HistoryItem *item);
	void refreshMediaViewer();
	void refreshNavVisibility();
	void refreshGroupThumbs();

	void dropdownHidden();
	void updateDocSize();
	void updateControls();
	void updateActions();
	void resizeCenteredControls();

	void displayPhoto(not_null<PhotoData*> photo, HistoryItem *item);
	void displayDocument(DocumentData *document, HistoryItem *item);
	void displayFinished();
	void findCurrent();

	void updateCursor();
	void setZoomLevel(int newZoom);

	void updateVideoPlaybackState(const Media::Player::TrackState &state);
	void updateSilentVideoPlaybackState();
	void restartVideoAtSeekPosition(TimeMs positionMs);
	void toggleVideoPaused();

	void createClipController();
	void refreshClipControllerGeometry();
	void refreshCaptionGeometry();

	void initAnimation();
	void createClipReader();

	void initThemePreview();
	void destroyThemePreview();
	void updateThemePreviewGeometry();

	void documentUpdated(DocumentData *doc);
	void changingMsgId(not_null<HistoryItem*> row, MsgId newId);

	// Radial animation interface.
	float64 radialProgress() const;
	bool radialLoading() const;
	QRect radialRect() const;
	void radialStart();
	TimeMs radialTimeShift() const;

	void updateHeader();
	void snapXY();

	void step_state(TimeMs ms, bool timer);
	void step_radial(TimeMs ms, bool timer);

	void zoomIn();
	void zoomOut();
	void zoomReset();
	void zoomUpdate(int32 &newZoom);

	void paintDocRadialLoading(Painter &p, bool radial, float64 radialOpacity);
	void paintThemePreview(Painter &p, QRect clip);

	void updateOverRect(OverState state);
	bool updateOverState(OverState newState);
	float64 overLevel(OverState control) const;

	void checkGroupThumbsAnimation();
	void initGroupThumbs();

	QBrush _transparentBrush;

	PhotoData *_photo = nullptr;
	DocumentData *_doc = nullptr;
	std::unique_ptr<SharedMedia> _sharedMedia;
	std::optional<SharedMediaWithLastSlice> _sharedMediaData;
	std::optional<SharedMediaWithLastSlice::Key> _sharedMediaDataKey;
	std::unique_ptr<UserPhotos> _userPhotos;
	std::optional<UserPhotosSlice> _userPhotosData;
	std::unique_ptr<Collage> _collage;
	std::optional<WebPageCollage> _collageData;

	QRect _closeNav, _closeNavIcon;
	QRect _leftNav, _leftNavIcon, _rightNav, _rightNavIcon;
	QRect _headerNav, _nameNav, _dateNav;
	QRect _saveNav, _saveNavIcon, _moreNav, _moreNavIcon;
	bool _leftNavVisible = false;
	bool _rightNavVisible = false;
	bool _saveVisible = false;
	bool _headerHasLink = false;
	QString _dateText;
	QString _headerText;

	object_ptr<Media::Clip::Controller> _clipController = { nullptr };
	DocumentData *_autoplayVideoDocument = nullptr;
	bool _fullScreenVideo = false;
	int _fullScreenZoomCache = 0;

	std::unique_ptr<Media::View::GroupThumbs> _groupThumbs;
	QRect _groupThumbsRect;
	int _groupThumbsAvailableWidth = 0;
	int _groupThumbsLeft = 0;
	int _groupThumbsTop = 0;
	Text _caption;
	QRect _captionRect;

	TimeMs _animStarted;

	int _width = 0;
	int _x = 0, _y = 0, _w = 0, _h = 0;
	int _xStart = 0, _yStart = 0;
	int _zoom = 0; // < 0 - out, 0 - none, > 0 - in
	float64 _zoomToScreen = 0.; // for documents
	QPoint _mStart;
	bool _pressed = false;
	int32 _dragging = 0;
	QPixmap _current;
	Media::Clip::ReaderPointer _gif;
	int32 _full = -1; // -1 - thumb, 0 - medium, 1 - full

	// Video without audio stream playback information.
	bool _videoIsSilent = false;
	bool _videoPaused = false;
	bool _videoStopped = false;
	TimeMs _videoPositionMs = 0;
	TimeMs _videoDurationMs = 0;
	int32 _videoFrequencyMs = 1000; // 1000 ms per second.

	bool fileShown() const;
	bool gifShown() const;
	bool fileBubbleShown() const;
	void stopGif();

	const style::icon *_docIcon = nullptr;
	style::color _docIconColor;
	QString _docName, _docSize, _docExt;
	int _docNameWidth = 0, _docSizeWidth = 0, _docExtWidth = 0;
	QRect _docRect, _docIconRect;
	int _docThumbx = 0, _docThumby = 0, _docThumbw = 0;
	object_ptr<Ui::LinkButton> _docDownload;
	object_ptr<Ui::LinkButton> _docSaveAs;
	object_ptr<Ui::LinkButton> _docCancel;

	QRect _photoRadialRect;
	Ui::RadialAnimation _radial;

	History *_migrated = nullptr;
	History *_history = nullptr; // if conversation photos or files overview
	PeerData *_peer = nullptr;
	UserData *_user = nullptr; // if user profile photos overview

	// We save the information about the reason of the current mediaview show:
	// did we open a peer profile photo or a photo from some message.
	// We use it when trying to delete a photo: if we've opened a peer photo,
	// then we'll delete group photo instead of the corresponding message.
	bool _firstOpenedPeerPhoto = false;

	PeerData *_from = nullptr;
	Text _fromName;

	std::optional<int> _index; // Index in current _sharedMedia data.
	std::optional<int> _fullIndex; // Index in full shared media.
	std::optional<int> _fullCount;
	FullMsgId _msgid;
	bool _canForwardItem = false;
	bool _canDeleteItem = false;

	mtpRequestId _loadRequest = 0;

	OverState _over = OverNone;
	OverState _down = OverNone;
	QPoint _lastAction, _lastMouseMovePos;
	bool _ignoringDropdown = false;

	BasicAnimation _a_state;

	enum ControlsState {
		ControlsShowing,
		ControlsShown,
		ControlsHiding,
		ControlsHidden,
	};
	ControlsState _controlsState = ControlsShown;
	TimeMs _controlsAnimStarted = 0;
	QTimer _controlsHideTimer;
	anim::value a_cOpacity;
	bool _mousePressed = false;

	Ui::PopupMenu *_menu = nullptr;
	object_ptr<Ui::DropdownMenu> _dropdown;
	object_ptr<QTimer> _dropdownShowTimer;

	struct ActionData {
		QString text;
		const char *member;
	};
	QList<ActionData> _actions;

	bool _receiveMouse = true;

	bool _touchPress = false, _touchMove = false, _touchRightButton = false;
	QTimer _touchTimer;
	QPoint _touchStart;
	QPoint _accumScroll;

	QString _saveMsgFilename;
	TimeMs _saveMsgStarted = 0;
	anim::value _saveMsgOpacity;
	QRect _saveMsg;
	QTimer _saveMsgUpdater;
	Text _saveMsgText;

	typedef QMap<OverState, TimeMs> Showing;
	Showing _animations;
	typedef QMap<OverState, anim::value> ShowingOpacities;
	ShowingOpacities _animOpacities;

	int _verticalWheelDelta = 0;

	bool _themePreviewShown = false;
	uint64 _themePreviewId = 0;
	QRect _themePreviewRect;
	std::unique_ptr<Window::Theme::Preview> _themePreview;
	object_ptr<Ui::RoundButton> _themeApply = { nullptr };
	object_ptr<Ui::RoundButton> _themeCancel = { nullptr };

};