#include "kernel.h"
#include "swis.h"
#include <math.h>

#ifndef __SCREEN_CC
#define __SCREEN_CC

/* Handy defines */
#define SCRN_1B 0
#define SCRN_2B 1
#define SCRN_4B 2
#define SCRN_8B 3
#define SCRN_16B 4
#define SCRN_24B 5
#define MODE_1B 1
#define MODE_2B 3
#define MODE_4B 15
#define MODE_8B 63
#define MODE_8BP 255
#define MODE_16B 65535
#define MODE_24B -1

/* ARM code links... */
#ifdef __cplusplus
extern "C"
{
#endif
void screen_qcls(int,int);
#ifdef __cplusplus
}
#endif

#define screen_cls() screen_qcls(screen.vdu,screen.length)

/* Globals */
_kernel_swi_regs screen_regs;
struct screen_struct
{
	int vdu;
	int display;
	int length;
	int width; /* number of pixels-1 */
	int height; /* number of rows-1 */
	int colours;
	int xeig;
	int yeig;
	int size;
	int osx;
	int osy;
	int bank;
	int num_banks;
	int spare1;
	int spare2;
} screen;

struct trucol
{
	/* Real format is &00BBGGRR */
	char r,g,b,x;
};

/*screen_struct screen; */

void screen_getdata(void)
{
	int vars[10];
	vars[0] = 148;
	vars[1] = 149;
	vars[2] = 7;
	vars[3] = 11;
	vars[4] = 12;
	vars[5] = 3;
	vars[6] = 4;
	vars[7] = 5;
	vars[8] = 150;
	vars[9] = -1;
	screen_regs.r[0] = (int)vars;
	screen_regs.r[1] = (int)&screen;
	_kernel_swi (OS_ReadVduVariables,&screen_regs,&screen_regs);
	if (screen.bank == 0)
		screen.bank = 1;
	screen.osx = screen.width * (int) pow(2,(double) screen.xeig);
	screen.osy = screen.height * (int) pow(2,(double) screen.yeig);
	/* Calculate how many screen banks are available at the moment */
	screen.num_banks = screen.size/screen.length;
	/* Now fill in the spare ones... */
	
}

void screen_write8(int x,int y,int c)
{

	char byte;
	byte = c;
	c = (screen.width * y) + x + y;
	/* Correct for width being width-1 by adding an extra y */
	*((char *) screen.vdu+c) = byte;
}

int screen_read8(int x,int y)
{
	char s;
	int c;
	c = (screen.width * y) + x + y;
	s = *((char *) screen.vdu+c);
	return (int) s;
}

struct trucol screen_reados(int x,int y) /* Must be in OS untis! */
{
	struct trucol pix;
	int tmp;
	/* Perform translation into a trucol */
	screen_regs.r[0] = x;
	screen_regs.r[1] = y;
	_kernel_swi (OS_ReadPoint,&screen_regs,&screen_regs);
	tmp = screen_regs.r[2] | screen_regs.r[3];
	if (screen.colours == MODE_16B)
	{
		/* Manual conversion */
		pix.r = (tmp & 31) * 8;
		pix.g = (tmp & (31*32)) / 4;
		pix.b = (tmp & (31*32*32)) / 128;
	}
	else if (screen.colours == MODE_24B)
	{
		pix.r = (tmp & 255);
		pix.g = (tmp & (255*256))/256;
		pix.b = (tmp & (255*65536))/65536;
	}
	else if (screen.colours == MODE_8B)
	{
		pix.r = (((tmp & 192)/64) + ((tmp & 3)*4)) * 17;
		pix.g = (((tmp & 192)/64) + (tmp & 12)) * 17;
		pix.b = (((tmp & 192)/64) + ((tmp & 48)/4)) * 17;
	}
	else
	{
		/* Use another SWI... */
		screen_regs.r[0] = tmp;
		screen_regs.r[1] = 16;
		_kernel_swi (OS_ReadPalette,&screen_regs,&screen_regs);
		tmp = screen_regs.r[2];
		pix.r = (tmp & (255*256))/256;
		pix.g = (tmp & (255*65536))/65536;
		pix.b = (tmp/(256*65536)) & 255; /* (tmp & (255*65536*256))/(256*65536); */
	}
	return pix;
}

void screen_swap(void)
{
	screen_regs.r[0] = 112;
	screen_regs.r[1] = screen.bank;
	_kernel_swi (OS_Byte,&screen_regs,&screen_regs);
	screen_regs.r[0] = 113;
	screen.bank++;
	if (screen.bank > screen.num_banks)
		screen.bank = 1;
	screen_regs.r[1] = screen.bank;
	_kernel_swi (OS_Byte,&screen_regs,&screen_regs);
}

void screen_setbank(int b)
{
	screen_regs.r[0] = 112;
	screen_regs.r[1] = b;
	_kernel_swi (OS_Byte,&screen_regs,&screen_regs);
	screen_regs.r[0] = 113;
	screen_regs.r[1] = b;
	_kernel_swi (OS_Byte,&screen_regs,&screen_regs);
	screen.bank = b;
}

void screen_vsync(void)
{
	screen_regs.r[0] = 19;
	_kernel_swi (OS_Byte,&screen_regs,&screen_regs);
}

void screen_setmode(int mode)
{
	screen_regs.r[0] = 0;
	screen_regs.r[1] = mode;
	_kernel_swi (OS_ScreenMode,&screen_regs,&screen_regs);
	screen.bank = 1;
}

void screen_oldmode(int mode)
{
	/* Use SWI &100 to set the mode */
	_kernel_swi(0x100 + 22,&screen_regs,&screen_regs);
	_kernel_swi(0x100 + mode,&screen_regs,&screen_regs);
}

void screen_rpcmode(int x,int y,int col)
{
	int mblock[6];
	mblock[0] = 1;
	mblock[1] = x;
	mblock[2] = y;
	mblock[3] = col; /* 0=1bpp,1=2bpp,2=4bpp,3=8bpp,4=16bpp,5=24bpp */
	mblock[4] = -1;
	mblock[5] = -1;
	screen_regs.r[0] = 0;
	screen_regs.r[1] = (int) mblock;
	_kernel_swi (OS_ScreenMode,&screen_regs,&screen_regs);
	screen.bank = 1;
}

struct trucol screen_s8_2_24(int col)
{
	/* Convert the screens 8bit format to 24bit union */
	struct trucol conv;
	conv.x = 0;
	conv.r = ((col & 7) + ((col & 16)/2)) * 17; /* *17 should duplicate in second nibble */
	conv.g = ((col & 3) + ((col & 96)/8)) * 17;
	conv.b = ((col & 3) + ((col & 8)/2) + ((col & 128)/16)) * 17;
	return conv;
}

int screen_24_2_s8(struct trucol col)
{
	int c;
	/* Convert 24bit union to screens 8bit format
	   RGB 8b format is   TTBBGGRR
	   Internal format is BGGRBRTT */
	c = ((col.r & 63) + (col.g & 63) + (col.b & 63))/48;
	c = c+(col.r & 64)/16 + (col.r & 128)/8;
	c = c+(col.g & 192)/2;
	c = c+(col.b & 64)/8 + (col.b & 128);
	return c;
}

int screen_24_2_s16(struct trucol col)
{
	/* Convert 24 bit into 16 bit */
	return (col.r & (31*8))/8 + (col.g & (31*8))*4 + (col.b & (31*8))*128;
}

void screen_createbanks(int nb)
{
	screen_getdata();
	/* Will try and claim screen mem based upon nb*screen.length */
	screen_regs.r[0]=2;
	screen_regs.r[1]=(nb*screen.length)-screen.size;
	_kernel_swi (OS_ChangeDynamicArea,&screen_regs,&screen_regs);
	screen_getdata();
}

#endif
