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

#include "rotatecommand.hpp"

namespace nostalgia::gfx {

static void rotateLeft(
		TileSheet::SubSheet &ss,
        ox::Point const &pt,
        ox::Point const &pt1,
        ox::Point const &pt2,
        int const depth = 0) noexcept {
	if (depth >= 4) {
		return;
	}
	auto const dstPt = ox::Point{pt1.x + pt.y, pt2.y - pt.x};
	auto const srcIdx = ptToIdx(pt + pt1, ss.columns);
	auto const dstIdx = ptToIdx(dstPt, ss.columns);
	auto const src = ss.pixels[srcIdx];
	auto &dst = ss.pixels[dstIdx];
	rotateLeft(ss, dstPt - pt1, pt1, pt2, depth + 1);
	dst = src;
}

static void rotateLeft(TileSheet::SubSheet &ss, ox::Point const &pt1, ox::Point const &pt2) noexcept {
	auto const w = pt2.x - pt1.x;
	auto const h = pt2.y - pt1.y;
	for (int x = 0; x <= w / 2; ++x) {
		for (int y = 0; y <= h / 2; ++y) {
			rotateLeft(ss, {x, y}, pt1, pt2);
		}
	}
}

static void rotateRight(
        TileSheet::SubSheet &ss,
        ox::Point const &pt,
        ox::Point const &pt1,
        ox::Point const &pt2,
        int const depth = 0) noexcept {
	if (depth >= 4) {
		return;
	}
	auto const dstPt = ox::Point{pt2.x - pt.y, pt1.y + pt.x};
	auto const srcIdx = ptToIdx(pt + pt1, ss.columns);
	auto const dstIdx = ptToIdx(dstPt, ss.columns);
	auto const src = ss.pixels[srcIdx];
	auto &dst = ss.pixels[dstIdx];
	rotateRight(ss, dstPt - pt1, pt1, pt2, depth + 1);
	dst = src;
}

static void rotateRight(TileSheet::SubSheet &ss, ox::Point const &pt1, ox::Point const &pt2) noexcept {
	auto const w = pt2.x - pt1.x;
	auto const h = pt2.y - pt1.y;
	for (int x = 0; x <= w / 2; ++x) {
		for (int y = 0; y <= h / 2; ++y) {
			rotateRight(ss, {x, y}, pt1, pt2);
		}
	}
}


RotateCommand::RotateCommand(
	TileSheet &img,
	TileSheet::SubSheetIdx idx,
	Direction const dir) noexcept:
		m_img(img),
		m_idx(std::move(idx)),
		m_pt2{[this] {
            auto &ss = getSubSheet(m_img, m_idx);
			return ox::Point{ss.columns * TileWidth - 1, ss.rows * TileHeight - 1};
		}()},
		m_dir{dir} {
}

RotateCommand::RotateCommand(
    TileSheet &img,
    TileSheet::SubSheetIdx idx,
    ox::Point const &pt1,
    ox::Point const &pt2,
	Direction const dir) noexcept:
		m_img(img),
		m_idx(std::move(idx)),
		m_pt1{pt1},
		m_pt2{pt2},
		m_dir{dir} {
}

ox::Error RotateCommand::redo() noexcept {
	auto &ss = getSubSheet(m_img, m_idx);
	switch (m_dir) {
		case Direction::Left:
			rotateLeft(ss, m_pt1, m_pt2);
			break;
		case Direction::Right:
			rotateRight(ss, m_pt1, m_pt2);
			break;
	}
	return {};
}

ox::Error RotateCommand::undo() noexcept {
	auto &ss = getSubSheet(m_img, m_idx);
	switch (m_dir) {
		case Direction::Left:
			rotateRight(ss, m_pt1, m_pt2);
			break;
		case Direction::Right:
			rotateLeft(ss, m_pt1, m_pt2);
			break;
	}
	return {};
}

int RotateCommand::commandId() const noexcept {
	return static_cast<int>(CommandId::Rotate);
}

TileSheet::SubSheetIdx const &RotateCommand::subsheetIdx() const noexcept {
	return m_idx;
}

}
