#pragma once

#include <QTimer>
#include <QLabel>
#include <QCheckBox>
#include <QListView>
#include <QStaticText>
#include <QAbstractListModel>
#include <QStyledItemDelegate>

#include <memory>
#include <deque>
#include <chrono>

class QLabel;
class QSpacerItem;
class QHBoxLayout;

// Only used to enable applying "SourceTreeSubItemCheckBox" stylesheet
// Can be removed once the minimum supported OBS version is greater than 30.1
class SourceTreeSubItemCheckBox : public QCheckBox {
	Q_OBJECT
};

namespace advss {

class Macro;
class MacroTree;

class MacroTreeItem : public QFrame {
	Q_OBJECT

public:
	explicit MacroTreeItem(MacroTree *tree, std::shared_ptr<Macro> macro,
			       bool highlight);

protected:
	bool event(QEvent *) override;

private slots:
	void ExpandClicked(bool checked);
	void EnableHighlight(bool enable);
	void RunningClicked(bool);
	void UpdateRunning();
	void HighlightIfExecuted();
	void MacroRenamed(const QString &, const QString &);

private:
	virtual void paintEvent(QPaintEvent *event) override;
	void mouseDoubleClickEvent(QMouseEvent *event) override;
	void Update(bool force);

	enum class Type {
		Unknown,
		Item,
		Group,
		SubItem,
	};

	Type _type = Type::Unknown;

	QSpacerItem *_spacer = nullptr;
	QCheckBox *_expand = nullptr;
	QLabel *_iconLabel = nullptr;
	QCheckBox *_running = nullptr;
	QHBoxLayout *_boxLayout = nullptr;
	QLabel *_label = nullptr;
	MacroTree *_tree;
	bool _highlight;
	std::chrono::high_resolution_clock::time_point _lastHighlightCheckTime{};
	QTimer _timer;
	std::shared_ptr<Macro> _macro;

	friend class MacroTree;
	friend class MacroTreeModel;
};

class MacroTreeModel : public QAbstractListModel {
	Q_OBJECT

public:
	explicit MacroTreeModel(MacroTree *st,
				std::deque<std::shared_ptr<Macro>> &macros);
	~MacroTreeModel() = default;
	virtual int rowCount(const QModelIndex &parent) const override;
	virtual QVariant data(const QModelIndex &index,
			      int role) const override;
	virtual Qt::ItemFlags flags(const QModelIndex &index) const override;
	virtual Qt::DropActions supportedDropActions() const override;

private:
	void Reset(std::deque<std::shared_ptr<Macro>> &);
	void MoveItemBefore(const std::shared_ptr<Macro> &item,
			    const std::shared_ptr<Macro> &before);
	void MoveItemAfter(const std::shared_ptr<Macro> &item,
			   const std::shared_ptr<Macro> &after);
	void Add(std::shared_ptr<Macro> item);
	void MoveToBeginningOfGroup(std::shared_ptr<Macro> item,
				    std::shared_ptr<Macro>);
	void Remove(std::shared_ptr<Macro> item);
	std::shared_ptr<Macro> Neighbor(const std::shared_ptr<Macro> &m,
					bool above) const;
	std::shared_ptr<Macro> FindEndOfGroup(const std::shared_ptr<Macro> &m,
					      bool above) const;
	std::shared_ptr<Macro> GetCurrentMacro() const;
	std::vector<std::shared_ptr<Macro>>
	GetCurrentMacros(const QModelIndexList &) const;
	QString GetNewGroupName();
	void GroupSelectedItems(QModelIndexList &indices);
	void UngroupSelectedGroups(QModelIndexList &indices);
	void ExpandGroup(std::shared_ptr<Macro> item);
	void CollapseGroup(std::shared_ptr<Macro> item);
	int GetItemMacroIndex(const std::shared_ptr<Macro> &item) const;
	int GetItemModelIndex(const std::shared_ptr<Macro> &item) const;
	bool IsLastItem(std::shared_ptr<Macro> item) const;
	bool IsInValidState();

	MacroTree *_mt;
	std::deque<std::shared_ptr<Macro>> &_macros;

	friend class MacroTree;
	friend class MacroTreeItem;
};

class MacroTree : public QListView {
	Q_OBJECT

public:
	explicit MacroTree(QWidget *parent = nullptr);
	void Reset(std::deque<std::shared_ptr<Macro>> &, bool highlight);
	void Add(std::shared_ptr<Macro> item,
		 std::shared_ptr<Macro> after = {}) const;
	void AddToGroup(std::shared_ptr<Macro> item,
			std::shared_ptr<Macro> group) const;
	void Remove(std::shared_ptr<Macro> item) const;
	void Up(std::shared_ptr<Macro> item) const;
	void Down(std::shared_ptr<Macro> item) const;
	std::shared_ptr<Macro> GetCurrentMacro() const;
	std::vector<std::shared_ptr<Macro>> GetCurrentMacros() const;
	bool GroupsSelected() const;
	bool GroupedItemsSelected() const;
	bool SingleItemSelected() const;
	bool SelectionEmpty() const;
	bool GroupsExist() const;
	void ExpandGroup(std::shared_ptr<Macro> item) const;
	void CollapseGroup(std::shared_ptr<Macro> item) const;
	void RefreshFilter();

public slots:
	void GroupSelectedItems();
	void UngroupSelectedGroups();
	void ExpandAll();
	void CollapseAll();
	void SelectionChangedHelper(const QItemSelection &,
				    const QItemSelection &);

signals:
	void MacroSelectionAboutToChange();
	void MacroSelectionChanged();

protected:
	virtual void dropEvent(QDropEvent *event) override;
	virtual void paintEvent(QPaintEvent *event) override;

private:
	MacroTreeItem *GetItemWidget(int idx) const;
	void ResetWidgets();
	void UpdateWidget(const QModelIndex &idx, std::shared_ptr<Macro> item);
	void UpdateWidgets(bool force = false);
	void MoveItemBefore(const std::shared_ptr<Macro> &item,
			    const std::shared_ptr<Macro> &after) const;
	void MoveItemAfter(const std::shared_ptr<Macro> &item,
			   const std::shared_ptr<Macro> &after) const;
	MacroTreeModel *GetModel() const;

	bool _highlight = false;

	friend class MacroTreeModel;
	friend class MacroTreeItem;
};

class MacroTreeDelegate : public QStyledItemDelegate {
	Q_OBJECT

public:
	MacroTreeDelegate(QObject *parent);
	virtual QSize sizeHint(const QStyleOptionViewItem &option,
			       const QModelIndex &index) const override;
};

} // namespace advss
