#include <nitro.h>

#include "screen.h"
#include "font.c"


#define	GX_DMA_NUMBER	1


static void VBlankIntr(void);

#define	VSCREEN_ROWS_A	24
#define	VSCREEN_ROWS_B	24
#define	VSCREEN_ROWS	(VSCREEN_ROWS_A+VSCREEN_ROWS_B)
#define	VSCREEN_COLUMNS	32


static u16 vScreen[VSCREEN_ROWS*VSCREEN_COLUMNS];

static int vblank_needupdate;

static int cursor_x = 0;
static int cursor_y = 0;
static int cursor_color = COLOR_WHITE;


static void
VBlankIntr(void)
{
	if (vblank_needupdate) {
		DC_FlushRange(vScreen, sizeof(vScreen));
		GX_LoadBG0Scr(  &vScreen[VSCREEN_COLUMNS*0],              0, sizeof(u16)*VSCREEN_COLUMNS*VSCREEN_ROWS_A);
		GXS_LoadBG0Scr( &vScreen[VSCREEN_COLUMNS*VSCREEN_ROWS_A], 0, sizeof(u16)*VSCREEN_COLUMNS*VSCREEN_ROWS_B);
		vblank_needupdate = 0;
	}
	OS_SetIrqCheckFlag(OS_IE_V_BLANK);
}


void
screenLocate(int x, int y)
{
	if (x < 0)
		x = 0;

	if (y < 0)
		y = 0;

	if (x >= VSCREEN_COLUMNS)
		x = VSCREEN_COLUMNS-1;

	if (y >= VSCREEN_ROWS)
		y = VSCREEN_ROWS-1;

	cursor_x = x;
	cursor_y = y;
}

void
screenHome()
{
	screenLocate(0,0);
}

void
screenClear()
{
	MI_CpuClearFast((void *)vScreen, sizeof(vScreen));

	screenHome();
	vblank_needupdate = 1;
}

void
screenClearLine(int y)
{
	if ((y < 0) || (y >= VSCREEN_ROWS))
		y = cursor_y;

	MI_CpuClearFast((void*)&vScreen[VSCREEN_COLUMNS*y], sizeof(u16)*VSCREEN_COLUMNS);
}

static void
screenScrollUp()
{
	MI_CpuCopyFast((void*)&vScreen[VSCREEN_COLUMNS*1], (void*)&vScreen[VSCREEN_COLUMNS*0], sizeof(u16)*VSCREEN_COLUMNS*(VSCREEN_ROWS-1));
	screenClearLine(VSCREEN_ROWS-1);
}


void
screenPutc(char ch)
{
	switch (ch) {
	case '\0':
		break;

	case '\t':
		{
			int n = ((cursor_x + 8) & ~0x7) - cursor_x;
			for (;n>0;n--) {
				screenPutc(' ');
			}
		}
		break;

	case '\r':
		cursor_x = 0;
		break;
	case '\n':
		cursor_y++;
		cursor_x = 0;
		break;

	default:
		vScreen[cursor_y*VSCREEN_COLUMNS + cursor_x] = (u16)((cursor_color<<12) + ch);
		vblank_needupdate = 1;

		cursor_x++;
		break;
	}

	if (cursor_x >= VSCREEN_COLUMNS) {
		cursor_x = 0;
		cursor_y++;
	}

	if (cursor_y >= VSCREEN_ROWS) {
		screenScrollUp();
		cursor_y = VSCREEN_ROWS-1;
	}
}


void
screenPrintf(char *fmt, ...)
{
	va_list vlist;
	char tmpbuf[128];
	int i;

	va_start(vlist, fmt);
	(void)vsnprintf(tmpbuf, sizeof(tmpbuf), fmt, vlist);
	va_end(vlist);

	for (i=0; tmpbuf[i]; i++) {
		screenPutc(tmpbuf[i]);
	}
}

void
screenVPrintf(char *fmt, va_list vlist)
{
	char tmpbuf[128];
	int i;

	(void)vsnprintf(tmpbuf, sizeof(tmpbuf), fmt, vlist);

	for (i=0; tmpbuf[i]; i++) {
		screenPutc(tmpbuf[i]);
	}
}

void
screenColor(u8 palette)
{
	cursor_color = palette;
}




void
screenInit(void)
{
	FX_Init();
	GX_InitEx(GX_DMA_NUMBER);
	GX_DispOff();
	GXS_DispOff();

	// 表示設定初期化
	GX_SetBankForLCDC(GX_VRAM_LCDC_ALL);

	MI_CpuClearFast((void *)HW_LCDC_VRAM, HW_LCDC_VRAM_SIZE);
	(void)GX_DisableBankForLCDC();
	MI_CpuFillFast((void *)HW_OAM, 192, HW_OAM_SIZE);
	MI_CpuClearFast((void *)HW_PLTT, HW_PLTT_SIZE);
	MI_CpuFillFast((void *)HW_DB_OAM, 192, HW_DB_OAM_SIZE);
	MI_CpuClearFast((void *)HW_DB_PLTT, HW_DB_PLTT_SIZE);

	// 文字列表示用に2D表示設定
	GX_SetBankForBG(GX_VRAM_BG_128_A);
	GX_SetBankForSubBG(GX_VRAM_SUB_BG_128_C);

	G2_SetBG0Control(GX_BG_SCRSIZE_TEXT_256x256, GX_BG_COLORMODE_16, GX_BG_SCRBASE_0xf000,
	                 GX_BG_CHARBASE_0x00000,    // CHR ベースブロック 0
	                 GX_BG_EXTPLTT_01);
	G2_SetBG0Priority(0);
	G2_BG0Mosaic(FALSE);

	GX_SetGraphicsMode(GX_DISPMODE_GRAPHICS, GX_BGMODE_0, GX_BG0_AS_2D);

	GX_SetVisiblePlane(GX_PLANEMASK_BG0);
	GX_LoadBG0Char(d_CharData, 0, sizeof(d_CharData));
	GX_LoadBGPltt(d_PaletteData, 0, sizeof(d_PaletteData));
	MI_CpuFillFast((void *)vScreen, 0, sizeof(vScreen));
	DC_FlushRange(vScreen, sizeof(vScreen));
	GX_LoadBG0Scr(vScreen, 0, sizeof(vScreen));


	// Sub
	G2S_SetBG0Control(GX_BG_SCRSIZE_TEXT_256x256, GX_BG_COLORMODE_16, GX_BG_SCRBASE_0xf800,
	                  GX_BG_CHARBASE_0x00000,
	                  GX_BG_EXTPLTT_01);
	G2S_SetBG0Priority(0);
	G2S_BG0Mosaic(FALSE);

	GXS_SetGraphicsMode(GX_BGMODE_0);
	GXS_SetVisiblePlane(GX_PLANEMASK_BG0);
	GXS_LoadBG0Char(d_CharData, 0, sizeof(d_CharData));
	GXS_LoadBGPltt(d_PaletteData, 0, sizeof(d_PaletteData));
	MI_CpuFillFast((void *)vScreen, 0, sizeof(vScreen));
	DC_FlushRange(vScreen, sizeof(vScreen));
	GXS_LoadBG0Scr(vScreen, 0, sizeof(vScreen));




	// 割込み設定
	(void)OS_DisableIrq();

	(void)OS_SetIrqFunction(OS_IE_V_BLANK, VBlankIntr);
	(void)OS_EnableIrqMask(OS_IE_V_BLANK);
	(void)OS_EnableIrqMask(OS_IE_FIFO_RECV);
	(void)GX_VBlankIntr(TRUE);
	(void)OS_EnableIrq();
	(void)OS_EnableInterrupts();

	// LCD表示開始
	GX_SetMasterBrightness(0);
	GXS_SetMasterBrightness(0);

	GX_DispOn();
	GXS_DispOn();

}


