#pragma once

// The following helpers are used by all macro segments,
// so it makes sense to include them here:
#include "log-helper.hpp"
#include "obs-module-helper.hpp"
#include "sync-helper.hpp"
#include "temp-variable.hpp"

#include <QWidget>
#include <QFrame>
#include <QVBoxLayout>
#include <QTimer>
#include <obs.hpp>

class QLabel;

namespace advss {

class Macro;

class MacroSegment {
public:
	MacroSegment(Macro *m, bool supportsVariableValue);
	virtual ~MacroSegment() = default;
	Macro *GetMacro() const { return _macro; }
	void SetIndex(int idx) { _idx = idx; }
	int GetIndex() const { return _idx; }
	void SetCollapsed(bool collapsed) { _collapsed = collapsed; }
	bool GetCollapsed() const { return _collapsed; }
	virtual bool Save(obs_data_t *obj) const = 0;
	virtual bool Load(obs_data_t *obj) = 0;
	virtual bool PostLoad();
	virtual std::string GetShortDesc() const;
	virtual std::string GetId() const = 0;
	void SetHighlight();
	bool Highlight();
	virtual std::string GetVariableValue() const;

protected:
	friend bool SupportsVariableValue(MacroSegment *);
	friend void IncrementVariableRef(MacroSegment *);
	friend void DecrementVariableRef(MacroSegment *);
	void SetVariableValue(const std::string &value);
	bool IsReferencedInVars() { return _variableRefs != 0; }

	virtual void SetupTempVars();
	void AddTempvar(const std::string &id, const std::string &name,
			const std::string &description = "");
	void SetTempVarValue(const std::string &id, const std::string &value);

private:
	void ClearAvailableTempvars();
	std::optional<const TempVariable>
	GetTempVar(const std::string &id) const;
	void InvalidateTempVarValues();

	// Macro helpers
	Macro *_macro = nullptr;
	int _idx = 0;

	// UI helper
	bool _highlight = false;
	bool _collapsed = false;

	// Variable helpers
	const bool _supportsVariableValue = false;
	int _variableRefs = 0;
	std::string _variableValue;
	std::vector<TempVariable> _tempVariables;

	friend class Macro;
};

class Section;

class MacroSegmentEdit : public QWidget {
	Q_OBJECT

public:
	MacroSegmentEdit(bool highlight, QWidget *parent = nullptr);
	// Use this function to avoid accidental edits when scrolling through
	// list of actions and conditions
	void SetFocusPolicyOfWidgets();
	void SetCollapsed(bool collapsed);
	void SetSelected(bool);
	virtual std::shared_ptr<MacroSegment> Data() const = 0;

protected slots:
	void HeaderInfoChanged(const QString &);
	void Collapsed(bool);
	void Highlight();
	void EnableHighlight(bool);
signals:
	void MacroAdded(const QString &name);
	void MacroRemoved(const QString &name);
	void MacroRenamed(const QString &oldName, const QString &newName);
	void SceneGroupAdded(const QString &name);
	void SceneGroupRemoved(const QString &name);
	void SceneGroupRenamed(const QString &oldName, const QString newName);

protected:
	bool eventFilter(QObject *obj, QEvent *ev) override;

	Section *_section;
	QLabel *_headerInfo;
	QWidget *_frame;
	QVBoxLayout *_contentLayout;

private:
	enum class DropLineState {
		NONE,
		ABOVE,
		BELOW,
	};

	void ShowDropLine(DropLineState);

	// The reason for using two separate frame widget each with their own
	// stylesheet and changing their visibility vs. using a single frame
	// and changing the stylesheet at runtime is that the operation of
	// adjusting the stylesheet is very expensive and can take multiple
	// hundred milliseconds per widget.
	// This performance impact would hurt in areas like drag and drop or
	// emitting the "SelectionChanged" signal.
	QFrame *_noBorderframe;
	QFrame *_borderFrame;

	// In most cases the line above the widget will be used.
	// The lower one will only be used if the segment is the last one in
	// the list.
	QFrame *_dropLineAbove;
	QFrame *_dropLineBelow;

	bool _showHighlight;
	QTimer _timer;

	friend class MacroSegmentList;
};

} // namespace advss
