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

	video.c

	PSP video control functions. (support 16bit color only)

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

#include "psp.h"


/***************************************************************************
	Global variables
 **************************************************************************/

u32 __attribute__((aligned(16))) gulist[262144];
int video_mode;
int show_frame;
int draw_frame;
int work_frame;
int tex_frame;


/***************************************************************************
	Global structures
 **************************************************************************/

RECT full_rect = { 0, 0, SCR_WIDTH, SCR_HEIGHT };


/*--------------------------------------------------------
	Initialize video
 -------------------------------------------------------*/

void video_init(void)
{
	video_mode = 16;
	show_frame = 0;
	draw_frame = 1;
	work_frame = 2;
	tex_frame  = 3;

	sceGuInit();
	sceGuDisplay(GU_FALSE);

	sceGuStart(GU_DIRECT, gulist);

	sceGuDispBuffer(SCR_WIDTH, SCR_HEIGHT, (void *)(FRAMESIZE * show_frame), BUF_WIDTH);
	sceGuDrawBuffer(GU_PSM_5551, (void *)(FRAMESIZE * draw_frame), BUF_WIDTH);
	sceGuOffset(2048 - (SCR_WIDTH / 2), 2048 - (SCR_HEIGHT / 2));
	sceGuViewport(2048, 2048, SCR_WIDTH, SCR_HEIGHT);
	sceGuEnable(GU_SCISSOR_TEST);
	sceGuAlphaFunc(GU_LEQUAL, 0, 0x01);
	sceGuEnable(GU_ALPHA_TEST);
	sceGuEnable(GU_TEXTURE_2D);
	sceGuClearColor(0);
	sceGuClear(GU_COLOR_BUFFER_BIT);

	sceGuTexMode(GU_PSM_5551, 0, 0, 0);
	sceGuTexScale(1.0f / BUF_WIDTH, 1.0f / BUF_WIDTH);
	sceGuTexOffset(0, 0);
	sceGuAmbientColor(0xffffffff);

	sceGuFinish();
	sceGuSync(0, 0);

	sceDisplayWaitVblankStart();
	sceGuDisplay(GU_TRUE);
}


/*--------------------------------------------------------
	Shutdown video
 -------------------------------------------------------*/

void video_exit(void)
{
	sceGuTerm();
}


/*--------------------------------------------------------
	Get VRAM address
 -------------------------------------------------------*/

u16 *video_frame_addr(int frame, int x, int y)
{
	return (u16 *)(((u8 *)0x44000000) + (x + (y + frame * SCR_HEIGHT) * BUF_WIDTH) * 2);
}


/*--------------------------------------------------------
	Clear show/draw frame
 -------------------------------------------------------*/

void video_clear_screen(void)
{
	video_clear_frame(show_frame);
	video_clear_frame(draw_frame);
}


/*--------------------------------------------------------
	Clear video frame
 -------------------------------------------------------*/

void video_clear_frame(int frame)
{
	sceGuStart(GU_DIRECT, gulist);
	sceGuDrawBufferList(GU_PSM_5551, (void *)(FRAMESIZE * frame), BUF_WIDTH);
	sceGuScissor(0, 0, SCR_WIDTH, SCR_HEIGHT);
	sceGuClearColor(0);
	sceGuClear(GU_COLOR_BUFFER_BIT);
	sceGuFinish();
	sceGuSync(0, 0);
}


/*--------------------------------------------------------
	Wait vsync
 -------------------------------------------------------*/

void video_wait_vsync(void)
{
	sceDisplayWaitVblankStart();
}


/*--------------------------------------------------------
	Flip screen
 -------------------------------------------------------*/

void video_flip_screen(int vsync)
{
	if (vsync) sceDisplayWaitVblankStart();
	sceGuSwapBuffers();
	show_frame ^= 1;
	draw_frame ^= 1;
}


/*--------------------------------------------------------
	Copy rectangle
 -------------------------------------------------------*/

void video_copy_rect(int src, int dst, RECT *src_rect, RECT *dst_rect)
{
	int j, sw, dw, sh, dh;
	struct Vertex *vertices;

	sw = src_rect->right - src_rect->left;
	dw = dst_rect->right - dst_rect->left;
	sh = src_rect->bottom - src_rect->top;
	dh = dst_rect->bottom - dst_rect->top;

	sceGuStart(GU_DIRECT, gulist);

	sceGuDrawBufferList(GU_PSM_5551, (void *)(FRAMESIZE * dst), BUF_WIDTH);
	sceGuScissor(dst_rect->left, dst_rect->top, dst_rect->right, dst_rect->bottom);
	sceGuDisable(GU_ALPHA_TEST);

	sceGuTexImage(0, BUF_WIDTH, BUF_WIDTH, BUF_WIDTH, video_frame_addr(src, 0, 0));
	sceGuTexFunc(GU_TFX_REPLACE, GU_TCC_RGB);

	if (sw == dw && sh == dh)
		sceGuTexFilter(GU_NEAREST, GU_NEAREST);
	else
		sceGuTexFilter(GU_LINEAR, GU_LINEAR);

	for (j = 0; (j + SLICE_SIZE) < sw; j = j + SLICE_SIZE)
	{
    	vertices = (struct Vertex *)sceGuGetMemory(2 * sizeof(struct Vertex));

		vertices[0].u = src_rect->left + j;
		vertices[0].v = src_rect->top;
		vertices[0].x = dst_rect->left + j * dw / sw;
		vertices[0].y = dst_rect->top;
		vertices[0].z = 0;

		vertices[1].u = src_rect->left + j + SLICE_SIZE;
		vertices[1].v = src_rect->bottom;
		vertices[1].x = dst_rect->left + (j + SLICE_SIZE) * dw / sw;
		vertices[1].y = dst_rect->bottom;
		vertices[1].z = 0;

		sceGuDrawArray(GU_SPRITES, TEXTURE_FLAGS, 2, 0, vertices);
	}

	if (j < sw)
	{
		vertices = (struct Vertex *)sceGuGetMemory(2 * sizeof(struct Vertex));

		vertices[0].u = src_rect->left + j;
		vertices[0].v = src_rect->top;
		vertices[0].x = dst_rect->left + j * dw / sw;
		vertices[0].y = dst_rect->top;
		vertices[0].z = 0;

		vertices[1].u = src_rect->right;
		vertices[1].v = src_rect->bottom;
		vertices[1].x = dst_rect->right;
		vertices[1].y = dst_rect->bottom;
		vertices[1].z = 0;

		sceGuDrawArray(GU_SPRITES, TEXTURE_FLAGS, 2, 0, vertices);
	}

	sceGuFinish();
	sceGuSync(0, 0);
}



/*--------------------------------------------------------
	Initialize video
 -------------------------------------------------------*/

void video_init32(void)
{
	video_mode = 32;
	show_frame = 0;
	draw_frame = 1;
	work_frame = 2;
	tex_frame  = 3;

	sceGuInit();
	sceGuDisplay(GU_FALSE);

	sceGuStart(GU_DIRECT, gulist);

	sceGuDispBuffer(SCR_WIDTH, SCR_HEIGHT, (void *)(FRAMESIZE * 2 * show_frame), BUF_WIDTH);
	sceGuDrawBuffer(GU_PSM_8888, (void *)(FRAMESIZE * 2 * draw_frame), BUF_WIDTH);
	sceGuOffset(2048 - (SCR_WIDTH / 2), 2048 - (SCR_HEIGHT / 2));
	sceGuViewport(2048, 2048, SCR_WIDTH, SCR_HEIGHT);
	sceGuEnable(GU_SCISSOR_TEST);
//	sceGuAlphaFunc(GU_EQUAL, 0, 0);
	sceGuDisable(GU_ALPHA_TEST);
	sceGuEnable(GU_TEXTURE_2D);
	sceGuClearColor(0);
	sceGuClear(GU_COLOR_BUFFER_BIT);

	sceGuTexMode(GU_PSM_8888, 0, 0, 0);
	sceGuTexScale(1.0f / BUF_WIDTH, 1.0f / BUF_WIDTH);
	sceGuTexOffset(0, 0);
	sceGuAmbientColor(0xffffffff);

	sceGuFinish();
	sceGuSync(0, 0);

	video_clear_screen32();
	sceDisplayWaitVblankStart();
	sceGuDisplay(GU_TRUE);
}


/*--------------------------------------------------------
	Shutdown video
 -------------------------------------------------------*/

void video_exit32(void)
{
	sceGuTerm();
}


/*--------------------------------------------------------
	Get VRAM address
 -------------------------------------------------------*/

u32 *video_frame_addr32(int frame, int x, int y)
{
	return (u32 *)(((u8 *)0x44000000) + ((x + (y + frame * SCR_HEIGHT) * BUF_WIDTH) << 2));
}


/*--------------------------------------------------------
	Clear show/draw frame
 -------------------------------------------------------*/

void video_clear_screen32(void)
{
	video_clear_frame32(show_frame);
	video_clear_frame32(draw_frame);
}


/*--------------------------------------------------------
	Clear video frame
 -------------------------------------------------------*/

void video_clear_frame32(int frame)
{
	sceGuStart(GU_DIRECT, gulist);
	sceGuDrawBufferList(GU_PSM_8888, (void *)(FRAMESIZE * 2 * frame), BUF_WIDTH);
	sceGuScissor(0, 0, SCR_WIDTH, SCR_HEIGHT);
	sceGuClearColor(0);
	sceGuClear(GU_COLOR_BUFFER_BIT);
	sceGuFinish();
	sceGuSync(0, 0);
}


/*--------------------------------------------------------
	Copy rectangle
 -------------------------------------------------------*/

void video_copy_rect32(int src, int dst, RECT *src_rect, RECT *dst_rect)
{
	int j, sw, dw, sh, dh;
	struct Vertex *vertices;

	sw = src_rect->right - src_rect->left;
	dw = dst_rect->right - dst_rect->left;
	sh = src_rect->bottom - src_rect->top;
	dh = dst_rect->bottom - dst_rect->top;

	sceGuStart(GU_DIRECT, gulist);

	sceGuDrawBufferList(GU_PSM_8888, (void *)(FRAMESIZE * 2 * dst), BUF_WIDTH);
	sceGuScissor(dst_rect->left, dst_rect->top, dst_rect->right, dst_rect->bottom);

	sceGuTexImage(0, BUF_WIDTH, BUF_WIDTH, BUF_WIDTH, video_frame_addr32(src, 0, 0));
	sceGuTexFunc(GU_TFX_REPLACE, GU_TCC_RGB);

	if (sw == dw && sh == dh)
		sceGuTexFilter(GU_NEAREST, GU_NEAREST);
	else
		sceGuTexFilter(GU_LINEAR, GU_LINEAR);

	for (j = 0; (j + SLICE_SIZE) < sw; j = j + SLICE_SIZE)
	{
    	vertices = (struct Vertex *)sceGuGetMemory(2 * sizeof(struct Vertex));

		vertices[0].u = src_rect->left + j;
		vertices[0].v = src_rect->top;
		vertices[0].x = dst_rect->left + j * dw / sw;
		vertices[0].y = dst_rect->top;
		vertices[0].z = 0;

		vertices[1].u = src_rect->left + j + SLICE_SIZE;
		vertices[1].v = src_rect->bottom;
		vertices[1].x = dst_rect->left + (j + SLICE_SIZE) * dw / sw;
		vertices[1].y = dst_rect->bottom;
		vertices[1].z = 0;

		sceGuDrawArray(GU_SPRITES, TEXTURE_FLAGS, 2, 0, vertices);
	}

	if (j < sw)
	{
		vertices = (struct Vertex *)sceGuGetMemory(2 * sizeof(struct Vertex));

		vertices[0].u = src_rect->left + j;
		vertices[0].v = src_rect->top;
		vertices[0].x = dst_rect->left + j * dw / sw;
		vertices[0].y = dst_rect->top;
		vertices[0].z = 0;

		vertices[1].u = src_rect->right;
		vertices[1].v = src_rect->bottom;
		vertices[1].x = dst_rect->right;
		vertices[1].y = dst_rect->bottom;
		vertices[1].z = 0;

		sceGuDrawArray(GU_SPRITES, TEXTURE_FLAGS, 2, 0, vertices);
	}

	sceGuFinish();
	sceGuSync(0, 0);
}
