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

#pragma once

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

#include <nostalgia/core/gfx.hpp>
#include <nostalgia/geo/bounds.hpp>
#include <nostalgia/geo/point.hpp>
#include <nostalgia/studio/studio.hpp>

namespace nostalgia::core {

class TileSheetEditorModel: public ox::SignalHandler {

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

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

	public:
		TileSheetEditorModel(Context *ctx, ox::String path);

		~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]]
		const ox::FileAddress &palPath() const noexcept;

		ox::Error setPalette(const ox::String &path) noexcept;

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

		void endDrawCommand() noexcept;

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

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

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

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

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

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

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

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

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

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

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

		void completeSelection() noexcept;

		void clearSelection() noexcept;

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

		ox::Error markUpdatedCmdId(const studio::UndoCommand *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;

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

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

		void setPalette();

		void saveState();

		void restoreState();

		[[nodiscard]]
		ox::String paletteName(const ox::String &palettePath) const;

		[[nodiscard]]
		ox::String palettePath(const ox::String &palettePath) const;

};

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;
}

}
