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

#pragma once

#include <ox/std/point.hpp>

#include "consts.hpp"

namespace nostalgia::gfx {

[[nodiscard]]
constexpr std::size_t ptToIdx(int const x, int const y, int const c, int const scale = 1) noexcept {
	auto const tileWidth = TileWidth * scale;
	auto const tileHeight = TileHeight * scale;
	auto const pixelsPerTile = tileWidth * tileHeight;
	auto const colLength = static_cast<std::size_t>(pixelsPerTile);
	auto const rowLength = static_cast<std::size_t>(static_cast<std::size_t>(c / tileWidth) * colLength);
	auto const colStart = static_cast<std::size_t>(colLength * static_cast<std::size_t>(x / tileWidth));
	auto const rowStart = static_cast<std::size_t>(rowLength * static_cast<std::size_t>(y / tileHeight));
	auto const colOffset = static_cast<std::size_t>(x % tileWidth);
	auto const rowOffset = static_cast<std::size_t>((y % tileHeight) * tileHeight);
	return static_cast<std::size_t>(colStart + colOffset + rowStart + rowOffset);
}

[[nodiscard]]
constexpr std::size_t ptToIdx(ox::Point const &pt, int const c, int const scale = 1) noexcept {
	return ptToIdx(pt.x, pt.y, c * TileWidth, scale);
}

[[nodiscard]]
constexpr ox::Point idxToPt(int const i, int c, int const scale = 1) noexcept {
	auto const tileWidth = TileWidth * scale;
	auto const tileHeight = TileHeight * scale;
	auto const pixelsPerTile = tileWidth * tileHeight;
	// prevent divide by zeros
	if (!c) {
		++c;
	}
	auto const t = i / pixelsPerTile; // tile number
	auto const iti = i % pixelsPerTile; // in tile index
	auto const tc = t % c; // tile column
	auto const tr = t / c; // tile row
	auto const itx = iti % tileWidth; // in tile x
	auto const ity = iti / tileHeight; // in tile y
	return {
		itx + tc * tileWidth,
		ity + tr * tileHeight,
	};
}

static_assert(idxToPt(4, 1) == ox::Point{4, 0});
static_assert(idxToPt(8, 1) == ox::Point{0, 1});
static_assert(idxToPt(8, 2) == ox::Point{0, 1});
static_assert(idxToPt(64, 2) == ox::Point{8, 0});
static_assert(idxToPt(128, 2) == ox::Point{0, 8});
static_assert(idxToPt(129, 2) == ox::Point{1, 8});
static_assert(idxToPt(192, 2) == ox::Point{8, 8});
static_assert(idxToPt(384, 8) == ox::Point{48, 0});

}
