/*~**********************************************************************
Title........ vdemo.c
Date......... Tue Mar 14 17:29:39  2000
Author....... Alan Austin
Description..
***********************************************************************~*/
/****	Include Files */

#include	"hardware.h"
#include	"peripherals.h"
#include	"interface.h"

#include	"fpga.h"
#include	"config.h"

#include	"images.h"
#include	"palettes.h"
#include	"fonts.h"

#include	"vdemo.h"
#include	"vdemo.sp"


/* 8-bit or 16-bit color version build? */
#ifdef COLOR_8

BYTE ColorMode = (BYTE)INDIRECT_COLOR;
enum {COLOR_BLACK=(WORD)BLACK, COLOR_WHITE=(WORD)WHITE, COLOR_RED=RED, COLOR_GREEN=GREEN, COLOR_BLUE=(WORD)BLUE};
#else

BYTE ColorMode = (BYTE)DIRECT_COLOR;
enum {COLOR_BLACK=(WORD)0x0000, COLOR_WHITE=(WORD)0xefff, COLOR_RED=0x4000, COLOR_GREEN=0x0200, COLOR_BLUE=(WORD)0x00010};

#endif

#define DISABLE	0
#define ENABLE	1

#define TEXT_WRAP_POSITION	600	/* pixels */

/* Unicode:
character codes 0x0000 - 0xffff supported,
divided into 512 code pages of size 0x80
*/
#define N_UNICODE_PAGES	512


/* layer sizes: width - pixels, height - lines

Required for Cremson:
width >= H_SIZE
width must be multiple of 64 pixels

*/
#define BASE_LAYER_WIDTH		((LONG) 1024)
#define BASE_LAYER_HEIGHT		((LONG) 768)
#define MIDDLE_LAYER_WIDTH		((LONG) 1024)
#define MIDDLE_LAYER_HEIGHT		((LONG) 768)
#define CONSOLE_LAYER_WIDTH		((LONG) 1024)
#define CONSOLE_LAYER_HEIGHT	((LONG) 768)

#define N_ANIMATIONS	8
#define N_SAVEBUFFERS	(2*N_ANIMATIONS)
#define N_DICE 			15

#define DOUBLE_BUFFERED	1

/* video memory addresses */
LONG display_start=0, base_start, middle_start, console_start;
LONG offscreen_start, bgnd_start, queen_start;
LONG dice_start[N_DICE];
LONG save_start[N_SAVEBUFFERS];
LONG load_buffer[2];
LONG free_start;
LONG dice_size_bytes;
LONG buffer_size_bytes;

ANIMATION anim[N_ANIMATIONS];
BYTE run_state[5];

BYTE str[64];


/************************************************************************
*****	Functions
*************************************************************************/


/*-----------------------------------------------------------------------
Author....... Alan Austin
Date......... 26/04/00
Description..
Limitations..
------------------------------------------------------------------------*/

void VDemo()
{

	TxString(&SerialDevice,1,"\r\nEnhanced Calypso Driver Test\r\n\r\n");

	sprintf(str, "%2d bit colour mode\r\n", 8<<ColorMode);
	TxString(&SerialDevice,1,str);

	/* run identical tests at different display resolutions */
	while(1) {

//		CalypsoInitDisplayMode(Calypso1024x768);

//		TxString(&SerialDevice,1,"Resolution 1024x768\r\n");
//		test();

		CalypsoInitDisplayMode(Calypso800x600);

		TxString(&SerialDevice,1,"Resolution 800x600\r\n");
		test();

		CalypsoInitDisplayMode(Calypso640x480);

		TxString(&SerialDevice,1,"Resolution 640x480\r\n");
		test();
	}

}

void test()
{
	WORD i, x;
	BYTE blend, select;

	CalypsoVideoOn();
	CalypsoAlphaBlend(DISABLE);
	CalypsoDisableLayer(BASE_SELECT | MIDDLE_SELECT | CONSOLE_SELECT | WINDOW_SELECT);

	/*** initialise layers ***/
	base_start    = display_start;
	/* Initialise BASE layer */
	middle_start = CalypsoInitLayer(&CalypsoLayer[BL_LAYER], ColorMode, DOUBLEBUFFER, COLOR_BLACK, BASE_LAYER_WIDTH, BASE_LAYER_HEIGHT, base_start);
	/* Initialise MIDDLE layer */
	console_start  = CalypsoInitLayer(&CalypsoLayer[ML_LAYER], ColorMode, DOUBLEBUFFER, COLOR_BLACK, MIDDLE_LAYER_WIDTH, MIDDLE_LAYER_HEIGHT, middle_start);
	/* Initialise CONSOLE layer */
	offscreen_start  = CalypsoInitLayer(&CalypsoLayer[C_LAYER], ColorMode, SINGLEBUFFER, COLOR_BLACK, CONSOLE_LAYER_WIDTH, CONSOLE_LAYER_HEIGHT, console_start);

	/* load palettes */
	CalypsoLoadPalette(MIDDLE_BASE_LAYER_PALETTE, palette);
	CalypsoLoadPalette(CONSOLE_LAYER_PALETTE, palette);


	/* clear layers */
	CalypsoClearLayer(&CalypsoLayer[BL_LAYER], DOUBLEBUFFER_FRAME0);
	CalypsoClearLayer(&CalypsoLayer[BL_LAYER], DOUBLEBUFFER_FRAME1);
	CalypsoClearLayer(&CalypsoLayer[ML_LAYER], DOUBLEBUFFER_FRAME0);
	CalypsoClearLayer(&CalypsoLayer[ML_LAYER], DOUBLEBUFFER_FRAME1);
	CalypsoClearLayer(&CalypsoLayer[C_LAYER],  DOUBLEBUFFER_FRAME0);

	CalypsoSetFrameRate(FRAME_30HZ);

	/* Test text */
	test1();	Wait(300);


	CalypsoFillLayer(&CalypsoLayer[BL_LAYER],  DOUBLEBUFFER_FRAME0, COLOR_BLUE, BLT_COPY);
	CalypsoFillLayer(&CalypsoLayer[BL_LAYER],  DOUBLEBUFFER_FRAME1, COLOR_BLUE, BLT_COPY);

	/* ensure graphics commands finished before image loading */
	WaitNFrames(3);


	/* load background image to base layer

	16 bit color mode:
	==================

	To ensure code fits into 1 Mbyte RAM for development build, an 8-bit background
	is loaded and converted on the fly to 16 bit-color:
		CalypsoLoadImageLayerConvert8to16(&CalypsoLayer[BL_LAYER], DOUBLEBUFFER_FRAME0, &bgnd);

	If code space is not an issue, a 16 bit color image could be loaded:
		CalypsoLoadImageLayer(&CalypsoLayer[BL_LAYER], DOUBLEBUFFER_FRAME0, &bgnd);

	*/

	bgnd_start = offscreen_start;
	if(ColorMode == (BYTE)DIRECT_COLOR)
		queen_start = CalypsoLoadImageConvert8to16(bgnd_start, &bgnd);
	else
		queen_start = CalypsoLoadImage(bgnd_start, &bgnd);

	CalypsoPutSprite(&CalypsoLayer[BL_LAYER], DOUBLEBUFFER_FRAME0, 0, 0, bgnd_start, bgnd.width, bgnd.height, 0);
	CalypsoPutSprite(&CalypsoLayer[BL_LAYER], DOUBLEBUFFER_FRAME1, 0, 0, bgnd_start, bgnd.width, bgnd.height, 0);


	/* load sprites to undisplayed memory */
	dice_start[0] =	CalypsoLoadSprite(queen_start, &queen);
	dice_start[1] =	CalypsoLoadSprite(dice_start[0], &dice1);
	dice_start[2] =	CalypsoLoadSprite(dice_start[1], &dice2);
	dice_start[3] =	CalypsoLoadSprite(dice_start[2], &dice3);
	dice_start[4] =	CalypsoLoadSprite(dice_start[3], &dice4);
	dice_start[5] =	CalypsoLoadSprite(dice_start[4], &dice5);
	dice_start[6] =	CalypsoLoadSprite(dice_start[5], &dice6);
	dice_start[7] =	CalypsoLoadSprite(dice_start[6], &dice7);
	dice_start[8] =	CalypsoLoadSprite(dice_start[7], &dice8);
	dice_start[9] =	CalypsoLoadSprite(dice_start[8], &dice9);
	dice_start[10]=	CalypsoLoadSprite(dice_start[9],&dice10);
	dice_start[11]=	CalypsoLoadSprite(dice_start[10],&dice11);
	dice_start[12]=	CalypsoLoadSprite(dice_start[11],&dice12);
	dice_start[13]=	CalypsoLoadSprite(dice_start[12],&dice13);
	dice_start[14]=	CalypsoLoadSprite(dice_start[13],&dice14);
	save_start[0] =	CalypsoLoadSprite(dice_start[14],&dice15);

	dice_size_bytes = dice_start[1] - dice_start[0];

	for (i=1; i<N_SAVEBUFFERS; i++) {
		save_start[i] = save_start[i-1] + dice_size_bytes;
	}

	buffer_size_bytes = dice_size_bytes;

	load_buffer[0] = save_start[0] +  dice_size_bytes*N_SAVEBUFFERS;
	load_buffer[1] = load_buffer[0] + buffer_size_bytes;

	free_start    = load_buffer[1] + buffer_size_bytes;


	/***** Run tests in double buffer or single buffer mode *****/
	if(DOUBLE_BUFFERED)	{
		CalypsoEnableDoubleBuffer();
		TxString(&SerialDevice,1,"Double buffer mode\r\n");
	}
	else {
		CalypsoDisableDoubleBuffer();
		TxString(&SerialDevice,1,"Single buffer mode\r\n");
	}

	TxString(&SerialDevice,1,"Alpha blending...\r\n");
	test2();	Wait(200);

	TxString(&SerialDevice,1,"Animations...\r\n");
	test3();	Wait(200);


}

/*~----------------------------------------------------------------------
Author........ Alan Austin
Last Modified. 12 May 2004
Description... Display ASCII and Unicode character sets on console layer
..............
Parameters....
..............
Return Value..
..............
-----------------------------------------------------------------------~*/

void test1()
{
	UWORD ch;	/* Unicode character UTF16 */
	UWORD codepage;
	UWORD n;
	BYTE j=0;
	WORD textcolor[4] = {COLOR_BLACK, COLOR_RED, COLOR_GREEN, COLOR_BLUE};


	CalypsoDisableLayer(BASE_SELECT | MIDDLE_SELECT | CONSOLE_SELECT | WINDOW_SELECT);
	CalypsoEnableLayer(CONSOLE_SELECT);
	CalypsoFillLayer(&CalypsoLayer[C_LAYER],  DOUBLEBUFFER_FRAME0, COLOR_WHITE, BLT_COPY);
	CalypsoSetTextPosition(&CalypsoLayer[C_LAYER], 16, 32);



	/* Display Ascii Font*/

	CalypsoSetAsciiFont(&tempo16_0);

	CalypsoSetTextColor(textcolor[j++],TRANSPARENT);

	for(ch=0; ch<128; ch++) {

		CalypsoPutChar(&CalypsoLayer[C_LAYER], 0, ch);

		if(CalypsoGetTextPositionX(&CalypsoLayer[C_LAYER]) > TEXT_WRAP_POSITION)
			newline();
	}
	newline();


	/* Display Unicode Fonts */

	CalypsoSetUnicodeFont(&alphdemo16_0, 0x0);
	CalypsoSetUnicodeFont(&alphdemo16_80, 0x80);
	CalypsoSetUnicodeFont(&alphdemo16_100, 0x100);
	CalypsoSetUnicodeFont(&alphdemo16_180, 0x180);
	CalypsoSetUnicodeFont(&alphdemo16_200, 0x200);
	CalypsoSetUnicodeFont(&alphdemo16_380, 0x380);
	CalypsoSetUnicodeFont(&alphdemo16_400, 0x400);

	for(n=0, codepage=0; n < N_UNICODE_PAGES;  n++) {

		if(CalypsoGetUnicodeFont(codepage) != NULL) {	/* check if font has been assigned to this unicode page */

			CalypsoSetTextColor(textcolor[j++&0x03],TRANSPARENT);

			for(ch=0; ch<128; ch++) {

				CalypsoPutUnicodeChar(&CalypsoLayer[C_LAYER], 0, codepage + ch);

				if(CalypsoGetTextPositionX(&CalypsoLayer[C_LAYER]) > TEXT_WRAP_POSITION)
					newline();
			}


		}

		codepage += 0x80;

	}


}

/*~----------------------------------------------------------------------
Author........ Alan Austin
Last Modified. 12 May 2004
Description... CR LF when writing text to console layer
..............
Parameters....
..............
Return Value..
..............
-----------------------------------------------------------------------~*/

void newline()
{
	CalypsoPutStr(&CalypsoLayer[C_LAYER], 0, "\r\n");
}



/*~----------------------------------------------------------------------
Author........ Alan Austin
Last Modified. 12 May 2004
Description... Alpha blending demo
.............. NOTE: differences between 8-bit and 16-bit color
Parameters....
..............
Return Value..
..............
-----------------------------------------------------------------------~*/

void test2()
{
	WORD i, x;
	PALETTE_RGB rgb;

	CalypsoAlphaBlend(ENABLE);
	CalypsoSetBlendRatio(7,0);

	CalypsoClearLayer(&CalypsoLayer[C_LAYER],  DOUBLEBUFFER_FRAME0);

	/* enable layers */
	CalypsoDisableLayer(BASE_SELECT | MIDDLE_SELECT | CONSOLE_SELECT | WINDOW_SELECT);
	CalypsoEnableLayer(BASE_SELECT | CONSOLE_SELECT );

	/* BLIT 3 queen sprites to the Console Layer, then alpha blend */
	CalypsoPutSprite(&CalypsoLayer[C_LAYER], 0,  0, 0, queen_start, queen.width, queen.height,0);
	CalypsoPutSprite(&CalypsoLayer[C_LAYER], 0,  300, 200, queen_start, queen.width, queen.height,0);
	CalypsoPutSprite(&CalypsoLayer[C_LAYER], 0,  600, 400, queen_start, queen.width, queen.height,0);

	if(ColorMode == (BYTE)DIRECT_COLOR) {

		/* set alpha blend enable bit (bit 15 for 16 bit color) for defined regions of queen sprites */
		CalypsoFillSprite(&CalypsoLayer[C_LAYER], 0, 0, 0, queen.width, queen.height, 0x7fff, BLT_AND);
		CalypsoFillSprite(&CalypsoLayer[C_LAYER], 0, 0+23, 0+17, 84, 158, 0x8000, BLT_OR);

		CalypsoFillSprite(&CalypsoLayer[C_LAYER], 0, 300, 200, queen.width, queen.height, 0x7fff, BLT_AND);
		CalypsoFillSprite(&CalypsoLayer[C_LAYER], 0, 300+23, 200+17, 84, 158, 0x8000, BLT_OR);

		CalypsoFillSprite(&CalypsoLayer[C_LAYER], 0, 600, 400, queen.width, queen.height, 0x7fff, BLT_AND);
		CalypsoFillSprite(&CalypsoLayer[C_LAYER], 0, 600+23, 400+17, 84, 158, 0x8000, BLT_OR);

	} else {	/* 8-bit color: modify palette table to define WHITE as not alpha blended */
		CalypsoGetPaletteEntry(CONSOLE_LAYER_PALETTE, COLOR_WHITE, &rgb);
		rgb.alpha = 0;
		CalypsoSetPaletteEntry(CONSOLE_LAYER_PALETTE, COLOR_WHITE, &rgb);
	}

	for(i=0; i<1; i++) {
		/* scroll layers*/
		for(x=0; x < BASE_LAYER_WIDTH; x++) {
			CalypsoScrollLayerX(&CalypsoLayer[C_LAYER], CONSOLE_LAYER_WIDTH - x%CONSOLE_LAYER_WIDTH);
			Wait(1);
		}
	}


}


/*~----------------------------------------------------------------------
Author........ Alan Austin
Last Modified. 12 May 2004
Description... Demo animation functions
..............
Parameters....
..............
Return Value..
..............
-----------------------------------------------------------------------~*/

void test3()
{
	BYTE ch;
	WORD i=0;

	CalypsoSetFrameRate(FRAME_60HZ);

	CalypsoDisableLayer(BASE_SELECT | MIDDLE_SELECT | CONSOLE_SELECT | WINDOW_SELECT);
	CalypsoEnableLayer(BASE_SELECT | MIDDLE_SELECT | CONSOLE_SELECT );

	/* clear layers */
	CalypsoClearLayer(&CalypsoLayer[ML_LAYER], DOUBLEBUFFER_FRAME0);
	CalypsoClearLayer(&CalypsoLayer[ML_LAYER], DOUBLEBUFFER_FRAME1);
	CalypsoClearLayer(&CalypsoLayer[C_LAYER],  DOUBLEBUFFER_FRAME0);
	CalypsoAlphaBlend(DISABLE);

	for(i=0; i<5; i++)
		run_state[i]=1;

	/*
	CalypsoDefineAnimation() Parameters:
	ANIMATION* anim, CALYPSOLAYER* layer, UWORD n_frames,
	WORD x, WORD y, WORD dx, WORD dy,
	LONG image_start, LONG image_offset, WORD n_images, WORD image_index,
	LONG width, LONG height,
	BYTE type, LONG mode,
	LONG buffer1, LONG buffer2,
	void (*update)(ANIMATION* anim));
	*/

	/* First, define animations... */

	CalypsoDefineAnimation(&anim[0],&CalypsoLayer[BL_LAYER], ANIMATION_RUN_FOREVER,
							0,0,12,12,
							dice_start[0], dice_size_bytes, 15, 0,
							dice1.width, dice1.height,
							ANIMATION_TYPE3, TRANSPARENT_MODE,
							save_start[i++], save_start[i++],
							edgebounce
							);

	CalypsoDefineAnimation(&anim[1], &CalypsoLayer[BL_LAYER], ANIMATION_RUN_FOREVER,
							100,0,6,6,
							dice_start[0], dice_size_bytes, 15, 0,
							dice1.width, dice1.height,
							ANIMATION_TYPE3, TRANSPARENT_MODE,
							save_start[i++], save_start[i++],
							edgebounce
							);

	CalypsoDefineAnimation(&anim[2], &CalypsoLayer[ML_LAYER], ANIMATION_RUN_FOREVER,
							200,0,4,4,
							dice_start[0], dice_size_bytes, 15, 0,
							dice1.width, dice1.height,
							ANIMATION_TYPE2, TRANSPARENT_MODE,
							0, 0,
							edgebounce
							);

	CalypsoDefineAnimation(&anim[3], &CalypsoLayer[ML_LAYER], ANIMATION_RUN_FOREVER,
							300,0,2,2,
							dice_start[0], dice_size_bytes, 15, 0,
							dice1.width, dice1.height,
							ANIMATION_TYPE2, TRANSPARENT_MODE,
							0, 0,
							edgebounce
							);

	CalypsoDefineAnimation(&anim[4], &CalypsoLayer[C_LAYER], ANIMATION_RUN_FOREVER,
							400,100,0,0,
							dice_start[0], dice_size_bytes, 15, 0,
							dice1.width, dice1.height,
							ANIMATION_TYPE1, NORMAL_MODE,
							0, 0,
							NULL
							);

	/* add animations to list... */

	CalypsoAddToAnimationList(&anim[0]);
	CalypsoAddToAnimationList(&anim[1]);
	CalypsoAddToAnimationList(&anim[2]);
	CalypsoAddToAnimationList(&anim[3]);
	CalypsoAddToAnimationList(&anim[4]);

	/* start the animations... */

	CalypsoStartAnimation(&anim[0]);
	CalypsoStartAnimation(&anim[1]);
	CalypsoStartAnimation(&anim[2]);
	CalypsoStartAnimation(&anim[3]);
	CalypsoStartAnimation(&anim[4]);

	CalypsoSetFrameNumber(0);
	do {

		if((ch=RxChar(&SerialDevice,1)) >= 0)

			control_animations(ch); /* control animations via terminal: pause, run... */

	} while (CalypsoGetFrameNumber() < 600);



	CalypsoStopAnimation(&anim[0]);
	CalypsoStopAnimation(&anim[1]);
	CalypsoStopAnimation(&anim[2]);
	CalypsoStopAnimation(&anim[3]);
	CalypsoStopAnimation(&anim[4]);

	/* ensure graphics commands finished */
	WaitNFrames(3);

	CalypsoClearAnimationList();

}

/*~----------------------------------------------------------------------
Author........ Alan Austin
Last Modified. 12 May 2004
Description... Pause/resume individual animations from terminal
..............
Parameters.... ch	- key pressed
..............
.............. 		  1,2,3,4,5		pause/resume animation 1,2,3,4,5
.............. 		  6,7,8,9,0		single step animation 1,2,3,4,5
..............
Return Value..
..............
-----------------------------------------------------------------------~*/

void control_animations(BYTE ch)
{
	WORD n;

	n = ch - '0' -1;
	if(n==-1)
		n=9;
	switch(n) {

		case 0:
		case 1:
		case 2:
		case 3:
		case 4:
				/* run/pause */
				run_state[n] ^= 1;
				if(run_state[n])
					CalypsoStartAnimation(&anim[n]);
				else
					CalypsoPauseAnimation(&anim[n]);
				break;

		case 5:
		case 6:
		case 7:
		case 8:
		case 9:
				/* single step */
				CalypsoStepAnimation(&anim[n-5]);
				break;

		default:
				break;
	}
}




/*~----------------------------------------------------------------------
Author........ Alan Austin
Last Modified. 12 May 2004
Description... Wait for next video frame (blocking)
..............
Parameters....
..............
Return Value..
..............
-----------------------------------------------------------------------~*/

BYTE WaitNextFrame()
{
	WORD frame_number;

	frame_number = CalypsoGetFrameNumber();
	while (frame_number==CalypsoGetFrameNumber())
		;

	return(CalypsoGetDoubleBufferFrame());

}

/*~----------------------------------------------------------------------
Author........ Alan Austin
Last Modified. 12 May 2004
Description... Wait for n video frames
..............
Parameters.... no of frames
..............
Return Value..
..............
-----------------------------------------------------------------------~*/

BYTE WaitNFrames(UBYTE nframes)
{
	UBYTE i;
	WORD frame_number;

	for(i=0; i<nframes; i++) {
		frame_number = CalypsoGetFrameNumber();
		while (frame_number==CalypsoGetFrameNumber())
			;
	}

	return(CalypsoGetDoubleBufferFrame());

}




/*~----------------------------------------------------------------------
Author........ Alan Austin
Last Modified. 12 May 2004
Description... animation update function
.............. computes next screen position
..............
.............. passed as parameter to CalypsoDefineAnimation()
..............
.............. Move around screen at constant speed, bouncing off screen sides
..............
.............. Uses animation structure members:
.............. anim->x		- current x position
.............. anim->y		- current y position
.............. anim->width	- image width
.............. anim->height	- image height
..............
..............
Parameters.... Pointer to animation
..............
Return Value..
..............
-----------------------------------------------------------------------~*/

static void edgebounce(ANIMATION *anim)
{
	WORD xlo, ylo, xhi, yhi;	/* screen limits */

	/* define screen limits */
	xlo = 0;
	ylo = 0;
	xhi = H_SIZE - anim->width;
	yhi = V_SIZE - anim->height;

	/* new position = old position + speed */
	anim->x += anim->dx;
	anim->y += anim->dy;

	/* reverse direction at screen limits */
	if(anim->x < xlo) {anim->x = xlo; anim->dx = -anim->dx;}
	if(anim->y < ylo) {anim->y = ylo; anim->dy = -anim->dy;}
	if(anim->x > xhi) {anim->x = xhi; anim->dx = -anim->dx;}
	if(anim->y > yhi) {anim->y = yhi; anim->dy = -anim->dy;}

}



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


