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

#pragma once

#include <ox/std/array.hpp>
#include <ox/std/size.hpp>
#include <ox/std/types.hpp>
#include <ox/model/def.hpp>

#include "color.hpp"
#include "context.hpp"
#include "keelmodule.hpp"
#include "palette.hpp"
#include "ptidxconv.hpp"
#include "tilesheet.hpp"

namespace nostalgia::gfx {

struct Sprite {
	static constexpr auto TypeName = "net.drinkingtea.nostalgia.gfx.Sprite";
	static constexpr auto TypeVersion = 1;
	bool enabled = false;
	int x = 0;
	int y = 0;
	unsigned tileIdx = 0;
	unsigned spriteShape = 0;
	unsigned spriteSize = 0;
	unsigned flipX = 0;
	unsigned bpp = 0;
	/**
	 * Valid priorities: 0-3
	 */
	unsigned priority = 0;
};

OX_MODEL_BEGIN(Sprite)
	OX_MODEL_FIELD(idx)
	OX_MODEL_FIELD(x)
	OX_MODEL_FIELD(y)
	OX_MODEL_FIELD(enabled)
	OX_MODEL_FIELD(tileIdx)
	OX_MODEL_FIELD(spriteShape)
	OX_MODEL_FIELD(spriteSize)
	OX_MODEL_FIELD(flipX)
	OX_MODEL_FIELD(bpp)
	OX_MODEL_FIELD(priority)
OX_MODEL_END()

struct BgTile {
	static constexpr auto TypeName = "net.drinkingtea.nostalgia.gfx.BgTile";
	static constexpr auto TypeVersion = 1;
	unsigned tileIdx = 0;
	unsigned palBank = 0;
	unsigned flipX = false;
	unsigned flipY = false;
};

OX_MODEL_BEGIN(BgTile)
	OX_MODEL_FIELD(tileIdx)
	OX_MODEL_FIELD(palBank)
	OX_MODEL_FIELD(horizontalFlip)
	OX_MODEL_FIELD(verticalFlip)
OX_MODEL_END()

struct TileSheetSetEntrySection {
	static constexpr auto TypeName = "net.drinkingtea.nostalgia.gfx.TileSheetSetEntrySection";
	static constexpr auto TypeVersion = 1;
	int32_t begin = 0;
	int32_t tiles = 0;
	[[nodiscard]]
	constexpr auto end() const noexcept {
		return begin + tiles - 1;
	}
};

OX_MODEL_BEGIN(TileSheetSetEntrySection)
	OX_MODEL_FIELD(begin)
	OX_MODEL_FIELD(tiles)
OX_MODEL_END()

struct TileSheetSetEntry {
	static constexpr auto TypeName = "net.drinkingtea.nostalgia.gfx.TileSheetSetEntry";
	static constexpr auto TypeVersion = 1;
	ox::FileAddress tilesheet;
	ox::Vector<TileSheetSetEntrySection> sections;
};

OX_MODEL_BEGIN(TileSheetSetEntry)
	OX_MODEL_FIELD(tilesheet)
	OX_MODEL_FIELD(sections)
OX_MODEL_END()

struct TileSheetSet {
	static constexpr auto TypeName = "net.drinkingtea.nostalgia.gfx.TileSheetSet";
	static constexpr auto TypeVersion = 1;
	static constexpr auto Preloadable = true;
	int32_t bpp = 0;
	ox::Vector<TileSheetSetEntry> entries;
};

OX_MODEL_BEGIN(TileSheetSet)
	OX_MODEL_FIELD(bpp)
	OX_MODEL_FIELD(entries)
OX_MODEL_END()

struct InitParams {
	bool glInstallDrawer = true;
	uint_t glSpriteCount = 128;
	uint_t glBlocksPerSprite = 64;
};

ox::Result<ox::UPtr<Context>> init(turbine::Context &tctx, InitParams const &params = {}) noexcept;

[[nodiscard]]
int tileColumns(Context const&) noexcept;

[[nodiscard]]
int tileRows(Context const&) noexcept;

ox::Error loadBgPalette(
		Context &ctx,
		size_t palBank,
		CompactPalette const &palette,
		size_t page = 0) noexcept;

ox::Error loadSpritePalette(
		Context &ctx,
		CompactPalette const &palette,
		size_t page = 0) noexcept;

ox::Error loadBgPalette(
		Context &ctx,
		size_t palBank,
		ox::StringViewCR palettePath) noexcept;

ox::Error loadBgPalette(
		Context &ctx,
		size_t palBank,
		ox::FileAddress const &paletteAddr) noexcept;

ox::Error loadSpritePalette(
		Context &ctx,
		ox::StringViewCR palettePath) noexcept;

ox::Error loadSpritePalette(
		Context &ctx,
		ox::FileAddress const &paletteAddr) noexcept;

ox::Error loadBgTileSheet(
		Context &ctx,
		unsigned cbb,
		TileSheetSet const &set) noexcept;

void clearCbb(Context &ctx, unsigned cbb) noexcept;

void clearCbbs(Context &ctx) noexcept;

ox::Error loadBgTileSheet(
		Context &ctx,
		unsigned cbb,
		CompactTileSheet const &ts,
		size_t dstTileIdx,
		size_t srcTileIdx,
		size_t tileCnt) noexcept;

ox::Error loadBgTileSheet(
		Context &ctx,
		unsigned cbb,
		ox::StringViewCR tsPath,
		size_t dstTileIdx,
		size_t srcTileIdx,
		size_t tileCnt) noexcept;

ox::Error loadBgTileSheet(
		Context &ctx,
		unsigned cbb,
		ox::FileAddress const &tsAddr,
		size_t dstTileIdx,
		size_t srcTileIdx,
		size_t tileCnt) noexcept;

ox::Error loadBgTileSheet(
		Context &ctx,
		unsigned cbb,
		CompactTileSheet const &ts,
		ox::Optional<unsigned> const &paletteBank = {}) noexcept;

ox::Error loadBgTileSheet(
		Context &ctx,
		unsigned cbb,
		ox::StringViewCR tilesheetPath,
		ox::Optional<unsigned> const &paletteBank) noexcept;

ox::Error loadBgTileSheet(
		Context &ctx,
		unsigned cbb,
		ox::FileAddress const &tilesheetAddr,
		ox::Optional<unsigned> const &paletteBank = {}) noexcept;

ox::Error loadSpriteTileSheet(
		Context &ctx,
		CompactTileSheet const &ts,
		bool loadDefaultPalette) noexcept;

ox::Error loadSpriteTileSheet(
		Context &ctx,
		ox::StringViewCR tilesheetPath,
		bool loadDefaultPalette = false) noexcept;

ox::Error loadSpriteTileSheet(
		Context &ctx,
		ox::FileAddress const &tilesheetAddr,
		bool loadDefaultPalette = false) noexcept;

ox::Error loadSpriteTileSheet(
		Context &ctx,
		TileSheetSet const &set) noexcept;

void setBgTile(Context &ctx, uint_t bgIdx, int column, int row, unsigned tile, unsigned palBank = 0) noexcept;

void setBgTile(Context &ctx, uint_t bgIdx, int column, int row, BgTile const &tile) noexcept;

void clearBg(Context &ctx, uint_t bgIdx) noexcept;

[[nodiscard]]
uint8_t bgStatus(Context &ctx) noexcept;

void setBgStatus(Context &ctx, uint32_t status) noexcept;

[[nodiscard]]
bool bgStatus(Context &ctx, unsigned bg) noexcept;

void setBgStatus(Context &ctx, unsigned bg, bool status) noexcept;

void setBgCbb(Context &ctx, unsigned bgIdx, unsigned cbbIdx) noexcept;

void setBgPriority(Context &ctx, uint_t bgIdx, uint_t priority) noexcept;

void setBgOffset(Context &ctx, uint16_t bg, int16_t x, int16_t y) noexcept;

void scrollBgOffset(Context &ctx, uint16_t bg, int16_t x, int16_t y) noexcept;

void hideSprite(Context &ctx, unsigned) noexcept;

void showSprite(Context &ctx, unsigned) noexcept;

void setSprite(Context &ctx, uint_t idx, Sprite const &sprite) noexcept;

[[nodiscard]]
uint_t spriteCount(Context const &ctx) noexcept;

ox::Error initConsole(Context &ctx) noexcept;

void consoleWrite(Context &ctx, int column, int row, ox::StringViewCR str) noexcept;

}

namespace nostalgia::gfx::gl {

constexpr ox::CStringView GlslVersion = "#version 330";

[[nodiscard]]
ox::Size drawSize(int scale = 5) noexcept;

void draw(Context &ctx, ox::Size const &renderSz) noexcept;

void draw(Context&, int scale = 5) noexcept;

}
