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

#pragma once

#include <algorithm>

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

#include <turbine/context.hpp>

#include "widget.hpp"

namespace studio {

constexpr void safeDelete(class FileTreeModel const *m) noexcept;

class FileExplorer: public ox::SignalHandler {

	friend class FileTreeModel;

	private:
		keel::Context &m_kctx;
		class FileTreeModel const *m_selected{};
		bool const m_fileDraggable{};
		ox::UPtr<FileTreeModel> m_treeModel;

	public:
		explicit FileExplorer(keel::Context &kctx, bool const fileDraggable = false) noexcept:
			m_kctx{kctx},
			m_fileDraggable{fileDraggable} {}

		void draw(Context &ctx, ImVec2 const &sz) const noexcept;

		void setModel(ox::UPtr<FileTreeModel> &&model, bool selectRoot = false) noexcept;

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

		[[nodiscard]]
		ox::Optional<ox::String> selectedPath() const;

		virtual void fileOpened(ox::StringViewCR path) const noexcept;

		virtual void fileDeleted(ox::StringViewCR path) const noexcept;

		virtual void fileMoved(ox::StringViewCR src, ox::StringViewCR dst) const noexcept;

		virtual void dirMoved(ox::StringViewCR src, ox::StringViewCR dst) const noexcept;

		void drawFileContextMenu(ox::CStringViewCR path) const noexcept;

		void drawDirContextMenu(ox::CStringViewCR path) const noexcept;

		[[nodiscard]]
		ox::FileSystem &romFs() const noexcept {
			return *m_kctx.rom;
		}

	protected:
		virtual void fileContextMenu(ox::StringViewCR path) const noexcept;

		virtual void dirContextMenu(ox::StringViewCR path) const noexcept;

		void setSelectedNode(FileTreeModel const *const node) noexcept {
			m_selected = node;
		}

		[[nodiscard]]
		bool selected(FileTreeModel const *const node) const noexcept {
			return m_selected == node;
		}

		[[nodiscard]]
		bool fileDraggable() const noexcept {
			return m_fileDraggable;
		}

	private:
		ox::Result<bool> setSelectedPath(ox::StringViewCR path, FileTreeModel const &node) noexcept;

};

class FileTreeModel {
	private:
		FileExplorer &m_explorer;
		ox::String m_name;
		ox::String m_fullPath;
		ox::Vector<ox::UPtr<FileTreeModel>> m_children;
		ox::FileType const m_fileType{};

	public:
		explicit FileTreeModel(
			FileExplorer &explorer,
			ox::StringParam name,
			ox::FileType fileType,
			FileTreeModel const *parent = nullptr) noexcept;

		virtual ~FileTreeModel() = default;

		FileTreeModel(FileTreeModel &&other) noexcept = default;

		void draw(turbine::Context &tctx) const noexcept;

		void setChildren(ox::Vector<ox::UPtr<FileTreeModel>> children) noexcept;

		[[nodiscard]]
		ox::Vector<ox::UPtr<FileTreeModel>> const &children() const noexcept {
			return m_children;
		}

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

		[[nodiscard]]
		ox::String const &path() const noexcept {
			return m_fullPath;
		}

};

constexpr void safeDelete(FileTreeModel const *m) noexcept {
	delete m;
}

ox::Result<ox::UPtr<FileTreeModel>> buildFileTreeModel(
		FileExplorer &explorer,
		ox::StringParam name,
		ox::StringViewCR path,
		FileTreeModel *parent = nullptr,
		std::function<bool(ox::StringViewCR, ox::FileStat const&)> const &filter =
			[](ox::StringViewCR, ox::FileStat const&) {return true;},
		bool showEmptyDirs = true) noexcept;

ox::Result<ox::UPtr<FileTreeModel>> buildFileTreeModel(
		FileExplorer &explorer,
		std::function<bool(ox::StringViewCR, ox::FileStat const&)> const &filter =
			[](ox::StringViewCR, ox::FileStat const&) {return true;},
		bool showEmptyDirs = true) noexcept;

}
