/*
 * Copyright 2016 - 2024 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 "context.hpp"

namespace nostalgia::core {

struct Sprite {
	static constexpr auto TypeName = "net.drinkingtea.nostalgia.core.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;
};

oxModelBegin(Sprite)
	oxModelField(idx)
	oxModelField(x)
	oxModelField(y)
	oxModelField(enabled)
	oxModelField(tileIdx)
	oxModelField(spriteShape)
	oxModelField(spriteSize)
	oxModelField(flipX)
	oxModelField(bpp)
	oxModelField(priority)
oxModelEnd()

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

oxModelBegin(BgTile)
	oxModelField(tileIdx)
	oxModelField(palBank)
	oxModelField(horizontalFlip)
	oxModelField(verticalFlip)
oxModelEnd()

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

oxModelBegin(TileSheetSetEntrySection)
	oxModelField(begin)
	oxModelField(size)
oxModelEnd()

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

oxModelBegin(TileSheetSetEntry)
	oxModelField(tilesheet)
	oxModelField(sections)
oxModelEnd()

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

oxModelBegin(TileSheetSet)
	oxModelField(bpp)
	oxModelField(entries)
oxModelEnd()

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

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

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

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

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

void hideSprite(Context &ctx, unsigned) noexcept;

void showSprite(Context &ctx, unsigned) noexcept;

void setSprite(Context &c, uint_t idx, Sprite const&s) noexcept;

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

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

void puts(Context &ctx, int column, int row, ox::CRStringView str) noexcept;

}

namespace nostalgia::core::gl {

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

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

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

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

}
