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

#pragma once

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

#include <studio/studio.hpp>

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

namespace nostalgia::core {

class TileSheetEditorModel: public ox::SignalHandler {

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

	private:
		static const Palette s_defaultPalette;
		turbine::Context &m_ctx;
		ox::String m_path;
		TileSheet m_img;
		TileSheet::SubSheetIdx m_activeSubsSheetIdx;
		keel::AssetRef<Palette> m_pal;
		studio::UndoStack &m_undoStack;
		class DrawCommand *m_ongoingDrawCommand = nullptr;
		bool m_updated = false;
		bool m_selectionOngoing = false;
		ox::Point m_selectionOrigin = {-1, -1};
		ox::Bounds m_selectionBounds = {{-1, -1}, {-1, -1}};

	public:
		TileSheetEditorModel(turbine::Context &ctx, ox::StringView path, studio::UndoStack &undoStack);

		~TileSheetEditorModel() override = default;

		void cut();

		void copy();

		void paste();

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

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

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

		[[nodiscard]]
		ox::StringView palPath() const noexcept;

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

		void drawCommand(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]]
		const TileSheet::SubSheet *activeSubSheet() const noexcept {
			auto &activeSubSheet = getSubSheet(m_img, m_activeSubsSheetIdx);
			return &activeSubSheet;
		}

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

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

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

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

		void completeSelection() noexcept;

		void clearSelection() 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;

	private:
		void getFillPixels(bool *pixels, ox::Point const&pt, int oldColor) const noexcept;

		void pushCommand(studio::UndoCommand *cmd) noexcept;

};

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

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

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

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

}
