/*
 * Copyright 2016 - 2025 Gary Talent (gary@drinkingtea.net). All rights reserved.
 */

#pragma once

#include <ox/event/signal.hpp>

#include <turbine/turbine.hpp>

#include "undostack.hpp"
#include "widget.hpp"

namespace studio {

class StudioUI;

class BaseEditor: public Widget {

	friend StudioUI;

	private:
		bool m_unsavedChanges = false;
		bool m_exportable = false;
		bool m_cutEnabled = false;
		bool m_copyEnabled = false;
		bool m_pasteEnabled = false;

	public:
		~BaseEditor() override = default;

		/**
		 * Returns the name of item being edited.
		 */
		[[nodiscard]]
		virtual ox::CStringView itemPath() const noexcept = 0;

		[[nodiscard]]
		virtual ox::CStringView itemDisplayName() const noexcept;

		virtual void cut();

		virtual void copy();

		virtual void paste();

		[[nodiscard]]
		virtual bool acceptsClipboardPayload() const noexcept;

		virtual void exportFile();

		virtual void keyStateChanged(turbine::Key key, bool down);

		virtual void onActivated() noexcept;

		void close() const;

		/**
		 * Save changes to item being edited.
		 */
		void save() noexcept;

		/**
		 * Sets indication of item being edited has unsaved changes. Also emits
		 * unsavedChangesChanged signal.
		 */
		void setUnsavedChanges(bool);

		[[nodiscard]]
		bool unsavedChanges() const noexcept;

		void setExportable(bool) noexcept;

		[[nodiscard]]
		bool exportable() const noexcept;

		void setCutEnabled(bool);

		[[nodiscard]]
		bool cutEnabled() const noexcept;

		void setCopyEnabled(bool);

		[[nodiscard]]
		bool copyEnabled() const noexcept;

		void setPasteEnabled(bool);

		[[nodiscard]]
		bool pasteEnabled() const noexcept;

	protected:
		/**
		 * Save changes to item being edited.
		 */
		virtual ox::Error saveItem() noexcept;

		/**
		 * Returns the undo stack holding changes to the item being edited.
		 */
		[[nodiscard]]
		virtual UndoStack *undoStack() noexcept;

		virtual void navigateTo(ox::StringViewCR arg) noexcept;

		void setRequiresConstantRefresh(bool value) noexcept;

	// signals
	public:
		ox::Signal<ox::Error(bool)> unsavedChangesChanged;
		ox::Signal<ox::Error(bool)> exportableChanged;
		ox::Signal<ox::Error(bool)> cutEnabledChanged;
		ox::Signal<ox::Error(bool)> copyEnabledChanged;
		ox::Signal<ox::Error(bool)> pasteEnabledChanged;
		ox::Signal<ox::Error(ox::StringView)> closed;

};

class Editor: public studio::BaseEditor {
	private:
		studio::UndoStack m_undoStack;
		ox::String m_itemPath;
		ox::String m_itemName;

	public:
		Editor(Context &ctx, ox::StringParam itemPath) noexcept;

		[[nodiscard]]
		ox::CStringView itemPath() const noexcept final;

		[[nodiscard]]
		ox::CStringView itemDisplayName() const noexcept final;

		[[nodiscard]]
		UndoStack *undoStack() noexcept final;

		ox::Error pushCommand(ox::UPtr<UndoCommand> &&cmd) noexcept;

		template<typename UC, typename ...Args>
		ox::Error pushCommand(Args&&... args) noexcept {
			try {
				return m_undoStack.push(ox::make_unique<UC>(ox::forward<Args>(args)...));
			} catch (ox::Exception const &ex) {
				return ex.toError();
			}
		}

	private:
		ox::Error markUnsavedChanges(UndoCommand const*) noexcept;

		ox::Error handleRename(ox::StringViewCR oldPath, ox::StringViewCR newPath, ox::UUID const &id) noexcept;

};


}
