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

#pragma once

#include <ox/event/signal.hpp>
#include <ox/std/memory.hpp>
#include <ox/std/string.hpp>

#include <studio/editor.hpp>
#include <studio/imguiutil.hpp>
#include <studio/module.hpp>
#include <studio/project.hpp>
#include <studio/task.hpp>

#include "popups/about.hpp"
#include "popups/deleteconfirmation.hpp"
#include "popups/makecopy.hpp"
#include "popups/newdir.hpp"
#include "popups/newmenu.hpp"
#include "popups/newproject.hpp"
#include "projectexplorer.hpp"
#include "popups/fileinfo.hpp"
#include "popups/renamefile.hpp"

namespace studio {

class StudioUI final: public ox::SignalHandler {
	friend class StudioUIDrawer;

	private:
		Context m_sctx;
		turbine::Context &m_tctx{m_sctx.tctx};
		ox::String m_projectDataDir;
		ox::UPtr<Project> m_project;
		TaskRunner m_taskRunner;
		ox::Vector<ox::UPtr<BaseEditor>> m_editors;
		ox::HashMap<ox::String, EditorMaker::Func> m_editorMakers;
		ProjectExplorer m_projectExplorer{keelCtx(m_tctx)};
		ox::Vector<ox::String> m_openFiles;
		BaseEditor *m_activeEditorOnLastDraw = nullptr;
		BaseEditor *m_activeEditor = nullptr;
		BaseEditor *m_activeEditorUpdatePending = nullptr;
		bool m_closeActiveTab{};
		ox::Vector<ox::Pair<ox::String>> m_queuedMoves;
		ox::Vector<ox::Pair<ox::String>> m_queuedDirMoves;
		ox::Vector<ox::String> m_recentProjects;
		NewMenu m_newMenu{keelCtx(m_tctx)};
		AboutPopup m_aboutPopup{m_tctx};
		DeleteConfirmation m_deleteConfirmation;
		FileInfo m_fileInfo{m_sctx};
		NewDir m_newDirDialog;
		ig::QuestionPopup m_closeFileConfirm{"Close File?", "This file has unsaved changes. Close?"};
		ig::QuestionPopup m_closeAppConfirm{
			"Close Application?",
			"There are files with unsaved changes. Close?"
		};
		ig::MessagePopup m_messagePopup{"Message", ""};
		MakeCopyPopup m_copyFilePopup;
		RenameFile m_renameFile;
		NewProject m_newProject{m_projectDataDir};
		struct {
			ig::QuestionPopup dlg{"Remove From Recents?", "Unable to load project. Remove from recent projects?"};
			size_t idx{};
		} m_removeRecentProject;
		ox::Array<Widget*, 12> const m_widgets {
			&m_closeFileConfirm,
			&m_closeAppConfirm,
			&m_copyFilePopup,
			&m_fileInfo,
			&m_newMenu,
			&m_newProject,
			&m_aboutPopup,
			&m_deleteConfirmation,
			&m_newDirDialog,
			&m_renameFile,
			&m_messagePopup,
			&m_removeRecentProject.dlg,
		};
		bool m_showProjectExplorer = true;
		struct NavAction {
			ox::String path;
			ox::String args;
		};
		ox::Optional<NavAction> m_navAction;
		float m_fontScale{};

	public:
		explicit StudioUI(turbine::Context &tctx, ox::StringParam projectDataDir) noexcept;

		void handleKeyEvent(turbine::Key, bool down) noexcept;

		void handleMouseButtonEvent(int btn, bool down) noexcept;

		void handleNavigationChange(ox::StringParam path, ox::StringParam navArgs) noexcept;

		[[nodiscard]]
		constexpr Project *project() noexcept {
			return m_project.get();
		}

		bool handleShutdown() noexcept;

	protected:
		void preDraw() noexcept;

		void draw() noexcept;

	private:
		void loadFont() noexcept;

		void drawMenu() noexcept;

		void drawTabBar() noexcept;

		void drawTabs() noexcept;

		void loadEditorMaker(EditorMaker const &editorMaker) noexcept;

		void loadModule(Module const &mod) noexcept;

		void loadModules() noexcept;

		void toggleProjectExplorer() noexcept;

		void redo() noexcept;

		void undo() noexcept;

		void save() noexcept;

		void handleKeyInput() noexcept;

		ox::Error addDir(ox::StringViewCR path) noexcept;

		ox::Error addFile(ox::StringViewCR path) noexcept;

		ox::Error deleteFile(ox::StringViewCR path) noexcept;

		ox::Error getFileInfo(ox::StringViewCR path) noexcept;

		ox::Error renameFile(ox::StringViewCR path) noexcept;

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

		ox::Error handleDeleteDir(ox::StringViewCR path) noexcept;

		ox::Error handleDeleteFile(ox::StringViewCR path) noexcept;

		ox::Error createOpenProject(ox::StringViewCR path) noexcept;

		ox::Error openProjectPath(ox::StringParam path) noexcept;

		ox::Error openFile(ox::StringViewCR path) noexcept;

		ox::Error openFileActiveTab(ox::StringViewCR path, bool makeActiveTab) noexcept;

		ox::Error makeCopyDlg(ox::StringViewCR path) noexcept;

		ox::Error handleCloseAppResponse(ig::PopupResponse response) noexcept;

		ox::Error handleCloseFileResponse(ig::PopupResponse response) noexcept;

		ox::Error handleRemoveRecentProjectResponse(ig::PopupResponse response) noexcept;

		ox::Error closeCurrentFile() noexcept;

		ox::Error closeFile(ox::StringViewCR path) noexcept;

		ox::Error queueDirMove(ox::StringParam src, ox::StringParam dst) noexcept;

		ox::Error queueFileMove(ox::StringParam src, ox::StringParam dst) noexcept;

		void procFileMoves() noexcept;

};

}
