// Copyright (c) 2026 - 2026 kio@little-bat.de
// BSD-2-Clause license
// https://opensource.org/licenses/BSD-2-Clause

#include "CharacterFrameBuffer.h"
#include "ColorMap.h"


#define XRAM __attribute__((section(".scratch_x.CFBu" __XSTRING(__LINE__))))	 // the 4k page with the core1 stack
#define RAM	 __attribute__((section(".time_critical.CFBu" __XSTRING(__LINE__)))) // general ram


namespace kilipili::Video
{

// all permutations for mask for 4 1-char Colors in uint32:
static constexpr RAM uint32 mask16x1[16] = {
	0x00000000, 0x000000ff, 0x0000ff00, 0x0000ffff, //
	0x00ff0000, 0x00ff00ff, 0x00ffff00, 0x00ffffff, //
	0xff000000, 0xff0000ff, 0xff00ff00, 0xff00ffff, //
	0xffff0000, 0xffff00ff, 0xffffff00, 0xffffffff,
};

// all permutations for mask for 2 2-char Colors in uint32:
static constexpr XRAM uint32 mask4x2[4] = //
	{0x00000000, 0x0000ffff, 0xffff0000, 0xffffffff};


template<bool small>
CharacterFrameBuffer<small>::CharacterFrameBuffer(const CharMap& charmap, uchar* font, Color fg, Color bg) noexcept :
	screen(charmap.data),
	rows(charmap.rows),
	cols(charmap.cols),
	font(font),
	row_ptr(screen),
	raster_line(0),
	last_y(0)
{
	setColors(fg, bg);
}

template<bool small>
void CharacterFrameBuffer<small>::setColors(Color fg, Color bg) noexcept
{
	Color  colors[2] {bg, fg};
	Color* p = &colorstrips[0][0];
	for (int byte = 0; byte < (small ? 16 : 256); byte++)
	{
		for (int bit = 0; bit < (small ? 4 : 8); bit++) { *p++ = colors[(byte >> bit) & 1]; }
	}
}

template<bool small>
void RAM CharacterFrameBuffer<small>::vblank() noexcept
{
	row_ptr		= screen;
	raster_line = 0;
	last_y		= 0;
}


// clang-format off
template<uint sz> struct uint_with_size{using type=uint8;};
template<> struct uint_with_size<2>{using type=uint16;};
template<> struct uint_with_size<4>{using type=uint32;};
template<> struct uint_with_size<8>{using type=uint64;};
// clang-format on

using onecolor	= uint_with_size<sizeof(Color) * 1>::type;
using twocolors = uint_with_size<sizeof(Color) * 2>::type;

constexpr int __unused ssx = sizeof(Color) * CHAR_BIT; // shift distance for swapping colors


template<bool small>
void RAM CharacterFrameBuffer<small>::renderScanline(int y, int width, uint32* buffer) noexcept
{
	assert(char_width == 8);
	assert(char_height == 12);
	assert(y < rows * char_height);
	assert(width < cols * 8);
	assert(width % 8 == 0);

	while (last_y < y)
	{
		last_y++;
		if (++raster_line >= char_height)
		{
			raster_line = 0;
			row_ptr += cols;
		}
	}

	int		cols = width >> 3;
	uchar*	font = this->font + raster_line;
	uint32* z	 = buffer;
	uint32	bg	 = *reinterpret_cast<uint32*>(colorstrips[0]);

	for (cptr p = row_ptr, end = p + cols; p < end;)
	{
		int byte = uchar(*p++);
		byte	 = font[byte * char_height]; // TODO: reorganize fonts and save 10cc here!

		if (byte)
		{
			if constexpr (small) // ~ 16 + 32cc
			{
				uint32* colors = reinterpret_cast<uint32*>(colorstrips[byte & 0x0f]);

				*z++ = colors[0];
				if (sizeof(Color) >= 2) *z++ = colors[1];

				colors = reinterpret_cast<uint32*>(colorstrips[byte >> 4]);

				*z++ = colors[0];
				if (sizeof(Color) >= 2) *z++ = colors[1];
			}
			else // ~ 16 + 24cc
			{
				uint32* colors = reinterpret_cast<uint32*>(colorstrips[byte]);

				*z++ = colors[0];
				*z++ = colors[1];
				if (sizeof(Color) >= 2) *z++ = colors[2];
				if (sizeof(Color) >= 2) *z++ = colors[3];
			}
		}
		else // byte = 0x00		~ 16 + 12cc
		{
			*z++ = bg;
			*z++ = bg;
			if (sizeof(Color) >= 2) *z++ = bg;
			if (sizeof(Color) >= 2) *z++ = bg;
		}
	}
}

template class CharacterFrameBuffer<false>;
template class CharacterFrameBuffer<true>;


// ******************************************************************************

template<int fg_bits, int bg_bits, bool bold, bool underline>
CharacterFrameBuffer_wAttr<fg_bits, bg_bits, bold, underline>::CharacterFrameBuffer_wAttr(
	const CharMap_wAttr& charmap, uchar* font, const Color* fg, const Color* bg) noexcept :
	screen(charmap.data),
	rows(charmap.rows),
	cols(charmap.cols),
	font(font),
	row_ptr(screen),
	raster_line(0),
	last_y(0)
{
	fgcolors = fg ? fg : Graphics::system_colormap.colors;
	bgcolors = bg ? bg : Graphics::system_colormap.colors;
}

template<int fg_bits, int bg_bits, bool bold, bool underline>
void RAM CharacterFrameBuffer_wAttr<fg_bits, bg_bits, bold, underline>::vblank() noexcept
{
	row_ptr		= screen;
	raster_line = 0;
	last_y		= 0;
}

template<int fg_bits, int bg_bits, bool bold, bool underline>
void RAM CharacterFrameBuffer_wAttr<fg_bits, bg_bits, bold, underline>::renderScanline( //
	int y, int width, uint32* buffer) noexcept
{
	assert(char_width == 8);
	assert(char_height == 12);
	assert(y < rows * char_height);
	assert(width < cols * 8);
	assert(width % 8 == 0);

	while (last_y < y)
	{
		last_y++;
		if (++raster_line >= char_height)
		{
			raster_line = 0;
			row_ptr += cols;
		}
	}

	int	   cols = width >> 3;
	uchar* font = this->font + raster_line;
	int	   attr = row_ptr[1] ^ -1; // => force update of bg and fg in first char
	uint32 bg	= 0;
	uint32 fg	= 0; // (fg ^ bg) for sizeof(Color) <= 2

	for (cuptr p = row_ptr, end = p + cols; p < end;)
	{
		uchar byte	   = *p++;
		uchar new_attr = *p++;

		byte = font[byte * char_height]; // TODO: reorganize fonts and save 10cc here!

		if ((new_attr ^ attr) & (fg_mask | bg_mask))
		{
			if ((new_attr ^ attr) & bg_mask)
			{
				bg = bgcolors[(new_attr & bg_mask) >> bg_ss];
				if constexpr (sizeof(Color) == 1) bg |= bg << 8;
				if constexpr (sizeof(Color) <= 2) bg |= bg << 16;
			}
			if ((new_attr ^ attr) & fg_mask)
			{
				fg = fgcolors[(new_attr & fg_mask) >> fg_ss];
				if constexpr (sizeof(Color) == 1) fg |= fg << 8;
				if constexpr (sizeof(Color) <= 2) fg |= fg << 16;
				if constexpr (sizeof(Color) <= 2) fg = fg ^ bg; // xor instead of fg color
			}

			attr = new_attr;
		}

		if ((new_attr & underline_mask) && raster_line == 10) { byte = 0xff; }

		if (byte)
		{
			if (new_attr & bold_mask) byte |= byte << 1;

			if constexpr (sizeof(Color) == 1)
			{
				*buffer++ = bg ^ (fg & mask16x1[byte & 0x0f]);
				*buffer++ = bg ^ (fg & mask16x1[byte >> 4]);
			}
			else if constexpr (sizeof(Color) == 2)
			{
				*buffer++ = bg ^ (fg & mask4x2[byte & 3]);
				*buffer++ = bg ^ (fg & mask4x2[(byte >> 2) & 3]);
				*buffer++ = bg ^ (fg & mask4x2[(byte >> 4) & 3]);
				*buffer++ = bg ^ (fg & mask4x2[byte >> 6]);
			}
			else // Color = 4 bytes
			{
				*buffer++ = byte & 1 ? fg : bg;
				*buffer++ = byte & 2 ? fg : bg;
				*buffer++ = byte & 4 ? fg : bg;
				*buffer++ = byte & 8 ? fg : bg;
				*buffer++ = byte & 0x10 ? fg : bg;
				*buffer++ = byte & 0x20 ? fg : bg;
				*buffer++ = byte & 0x40 ? fg : bg;
				*buffer++ = byte & 0x80 ? fg : bg;
			}
		}
		else // byte = 0x00
		{
			*buffer++ = bg;
			*buffer++ = bg;

			if constexpr (sizeof(Color) >= 2) *buffer++ = bg;
			if constexpr (sizeof(Color) >= 2) *buffer++ = bg;

			if constexpr (sizeof(Color) == 4) *buffer++ = bg;
			if constexpr (sizeof(Color) == 4) *buffer++ = bg;
			if constexpr (sizeof(Color) == 4) *buffer++ = bg;
			if constexpr (sizeof(Color) == 4) *buffer++ = bg;
		}
	}
}

// the linker will know what we need:
// not all possible permutations, though.

template class CharacterFrameBuffer_wAttr<4, 4, false, false>;

template class CharacterFrameBuffer_wAttr<4, 3, false, true>;
template class CharacterFrameBuffer_wAttr<4, 3, true, false>;
template class CharacterFrameBuffer_wAttr<4, 3, false, false>;

template class CharacterFrameBuffer_wAttr<4, 2, true, false>;
template class CharacterFrameBuffer_wAttr<4, 2, false, true>;
template class CharacterFrameBuffer_wAttr<4, 2, true, true>;
template class CharacterFrameBuffer_wAttr<4, 2, false, false>;

template class CharacterFrameBuffer_wAttr<4, 1, true, false>;
template class CharacterFrameBuffer_wAttr<4, 1, false, true>;
template class CharacterFrameBuffer_wAttr<4, 1, true, true>;
template class CharacterFrameBuffer_wAttr<4, 1, false, false>;

template class CharacterFrameBuffer_wAttr<3, 3, true, false>;
template class CharacterFrameBuffer_wAttr<3, 3, false, true>;
template class CharacterFrameBuffer_wAttr<3, 3, true, true>;
template class CharacterFrameBuffer_wAttr<3, 3, false, false>;

template class CharacterFrameBuffer_wAttr<3, 2, true, false>;
template class CharacterFrameBuffer_wAttr<3, 2, false, true>;
template class CharacterFrameBuffer_wAttr<3, 2, true, true>;
template class CharacterFrameBuffer_wAttr<3, 2, false, false>;

template class CharacterFrameBuffer_wAttr<3, 1, true, false>;
template class CharacterFrameBuffer_wAttr<3, 1, false, true>;
template class CharacterFrameBuffer_wAttr<3, 1, true, true>;
template class CharacterFrameBuffer_wAttr<3, 1, false, false>;

template class CharacterFrameBuffer_wAttr<2, 2, true, false>;
template class CharacterFrameBuffer_wAttr<2, 2, false, true>;
template class CharacterFrameBuffer_wAttr<2, 2, true, true>;
template class CharacterFrameBuffer_wAttr<2, 2, false, false>;

template class CharacterFrameBuffer_wAttr<2, 1, true, false>;
template class CharacterFrameBuffer_wAttr<2, 1, false, true>;
template class CharacterFrameBuffer_wAttr<2, 1, true, true>;
template class CharacterFrameBuffer_wAttr<2, 1, false, false>;

template class CharacterFrameBuffer_wAttr<1, 1, false, true>;
template class CharacterFrameBuffer_wAttr<1, 1, true, false>;
template class CharacterFrameBuffer_wAttr<1, 1, true, true>;
template class CharacterFrameBuffer_wAttr<1, 1, false, false>;


} // namespace kilipili::Video


/*





































*/
