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

#pragma once

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

#include <studio/studio.hpp>

#include <nostalgia/gfx/gfx.hpp>
#include <nostalgia/gfx/tilesheet.hpp>

namespace nostalgia::gfx {

class TileSheetEditorModel final: public ox::SignalHandler {

	public:
		ox::Signal<ox::Error(const TileSheet::SubSheetIdx&)> activeSubsheetChanged;
		ox::Signal<ox::Error()> paletteChanged;

	private:
		static Palette const s_defaultPalette;
		studio::Context &m_sctx;
		turbine::Context &m_tctx;
		ox::String m_path;
		ox::String m_palPath;
		TileSheet m_img;
		TileSheet::SubSheetIdx m_activeSubsSheetIdx;
		keel::AssetRef<Palette> m_pal;
		size_t m_palettePage{};
		studio::UndoStack &m_undoStack;
		class DrawCommand *m_ongoingDrawCommand = nullptr;
		studio::SelectionTracker m_selTracker;
		ox::Optional<studio::Selection> m_selection;
		ox::Point m_lineStartPt;
		ox::Point m_lastDrawUpdatePt{-1, -1};
		bool m_updated = false;

	public:
		TileSheetEditorModel(studio::Context &sctx, ox::StringParam path, studio::UndoStack &undoStack);

		~TileSheetEditorModel() override = default;

		void cut();

		void copy();

		void paste();

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

		[[nodiscard]]
		constexpr TileSheet const &img() const noexcept;

		[[nodiscard]]
		constexpr TileSheet &img() noexcept;

		[[nodiscard]]
		constexpr Palette const &pal() const noexcept;

		[[nodiscard]]
		ox::String const &palPath() const & noexcept;

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

		void setPalettePage(size_t pg) noexcept;

		[[nodiscard]]
		size_t palettePage() const noexcept;

		void drawCommand(ox::Point const &pt, std::size_t palIdx) noexcept;

		void drawLineCommand(ox::Point const &pt, std::size_t palIdx) noexcept;

		void endDrawCommand() noexcept;

		void addSubsheet(TileSheet::SubSheetIdx const &parentIdx) noexcept;

		void rmSubsheet(TileSheet::SubSheetIdx const &idx) noexcept;

		void insertTiles(TileSheet::SubSheetIdx const &idx, std::size_t tileIdx, std::size_t tileCnt) noexcept;

		void deleteTiles(TileSheet::SubSheetIdx const &idx, std::size_t tileIdx, std::size_t tileCnt) noexcept;

		ox::Error updateSubsheet(TileSheet::SubSheetIdx const &idx, ox::StringView const &name, int cols, int rows) noexcept;

		void setActiveSubsheet(TileSheet::SubSheetIdx const &) noexcept;

		[[nodiscard]]
		TileSheet::SubSheet const &activeSubSheet() const noexcept {
			return getSubSheet(m_img, m_activeSubsSheetIdx);
		}

		[[nodiscard]]
		TileSheet::SubSheet &activeSubSheet() noexcept {
			return getSubSheet(m_img, m_activeSubsSheetIdx);
		}

		[[nodiscard]]
		constexpr TileSheet::SubSheetIdx const &activeSubSheetIdx() const noexcept {
			return m_activeSubsSheetIdx;
		}

		void fill(ox::Point const &pt, uint8_t palIdx) noexcept;

		ox::Error rotateLeft() noexcept;

		ox::Error rotateRight() noexcept;

		void setSelection(studio::Selection const &sel) noexcept;

		void select(ox::Point const &pt) noexcept;

		void completeSelection() noexcept;

		void clearSelection() noexcept;

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

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

		ox::Error markUpdatedCmdId(studio::UndoCommand const *cmd) noexcept;

		ox::Error markUpdated() noexcept;

		void ackUpdate() noexcept;

		ox::Error saveFile() noexcept;

		[[nodiscard]]
		constexpr studio::UndoStack *undoStack() noexcept;

		bool pixelSelected(std::size_t idx) const noexcept;

		ox::Error flipX() noexcept;

		ox::Error flipY() noexcept;

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

		ox::Error moveSubSheet(TileSheet::SubSheetIdx src, TileSheet::SubSheetIdx dst) noexcept;

	private:
		static void getFillPixels(
			TileSheet::SubSheet const &activeSubSheet,
			ox::Span<bool> pixels,
			ox::Point const &pt,
			uint8_t oldColor) noexcept;

		void setPalPath() noexcept;

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

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

};

constexpr TileSheet const &TileSheetEditorModel::img() const noexcept {
	return m_img;
}

constexpr TileSheet &TileSheetEditorModel::img() noexcept {
	return m_img;
}

constexpr Palette const &TileSheetEditorModel::pal() const noexcept {
	if (m_pal) {
		return *m_pal;
	}
	return s_defaultPalette;
}

constexpr studio::UndoStack *TileSheetEditorModel::undoStack() noexcept {
	return &m_undoStack;
}

}
