#include	<intuition/intuition.h>
#include	<exec/types.h>
#include	<devices/gameport.h>
#include	<devices/inputevent.h>
#include	"system.h"

#include	"atari.h"

#define GAMEPORT0 0
#define GAMEPORT1 1

static struct IntuitionBase	*IntuitionBase = NULL;
static struct GfxBase		*GfxBase = NULL;
static struct LayersBase	*LayersBase = NULL;
static struct Window		*WindowMain = NULL;

int old_stick_0;
int old_stick_1;

struct InputEvent gameport_data;
struct MsgPort *gameport_msg_port;
struct IOStdReq *gameport_io_msg;
BOOL gameport_error;

/*
extern struct MsgPort *CreatePort()
*/

static UBYTE	*image_data;
static UBYTE	*scanline_ptr;

static UWORD ScreenType;
static struct Screen *ScreenMain = NULL;
static struct NewScreen NewScreen;
static struct Image image_Screen;
static int ScreenID;
static int ScreenWidth;
static int ScreenHeight;
static int ScreenDepth;
static int tword;

static UWORD	*bitplane0;
static UWORD	*bitplane1;
static UWORD	*bitplane2;
static UWORD	*bitplane3;
static UWORD	*bitplane4;
static UWORD	*bitplane5;
static UWORD	*bitplane6;

extern UWORD	scanline[ATARI_WIDTH];
extern int	ypos;
extern int	colortable[128];

__chip UWORD data_Screen[40320]=
{
};

void Atari_Initialise ()
{
	struct NewWindow	NewWindow;

	char	*ptr;
	int 	i;

	IntuitionBase = (struct IntuitionBase*) OpenLibrary ("intuition.library", 0);
	if (!IntuitionBase)
	{
		printf ("Failed to open intuition.library\n");
		exit (1);
	}

	GfxBase = (struct GfxBase*) OpenLibrary ("graphics.library", 39);
	if (!GfxBase)
	{
		printf ("Failed to open graphics.library\n");
		exit (1);
	}

	LayersBase = (struct LayersBase*) OpenLibrary ("layers.library", 0);
	if (!LayersBase)
	{
		printf ("Failed to open layers.library\n");
		exit (1);
	}

	/*
	 * ==============
	 * Setup Joystick
	 * ==============
	 */

	gameport_msg_port = CreatePort (0, 0);
	if (!gameport_msg_port)
	{
		printf ("Failed to create gameport_msg_port\n");
		exit (1);
	}

	gameport_io_msg = CreateStdIO (gameport_msg_port);
	if (!gameport_io_msg)
	{
		printf ("Failed to create gameport_io_msg\n");
		exit (1);
	}

	gameport_error = OpenDevice ("gameport.device", GAMEPORT1, gameport_io_msg, 0xFFFF);
	if (gameport_error)
	{
		printf ("Failed to open the gameport.device\n");
		exit (1);
	}

	{
		BYTE type = 0;

		gameport_io_msg->io_Command = GPD_ASKCTYPE;
		gameport_io_msg->io_Length = 1;
		gameport_io_msg->io_Data = (APTR) &type;

		DoIO (gameport_io_msg);

		if (type)
		{
			printf ("gameport already in use\n");
			gameport_error = TRUE;
		}
	}

	{
		BYTE type = GPCT_ABSJOYSTICK;

		gameport_io_msg->io_Command = GPD_SETCTYPE;
		gameport_io_msg->io_Length = 1;
		gameport_io_msg->io_Data = (APTR) &type;

		DoIO (gameport_io_msg);

		if (gameport_io_msg->io_Error)
		{
			printf ("Failed to set controller type\n");
		}

	}

	{
		struct GamePortTrigger gpt;

		gpt.gpt_Keys = GPTF_DOWNKEYS | GPTF_UPKEYS;
		gpt.gpt_Timeout = 0;
		gpt.gpt_XDelta = 1;
		gpt.gpt_YDelta = 1;

		gameport_io_msg->io_Command = GPD_SETTRIGGER;
		gameport_io_msg->io_Length = sizeof (gpt);
		gameport_io_msg->io_Data = (APTR) &gpt;

		DoIO (gameport_io_msg);

		if (gameport_io_msg->io_Error)
		{
			printf ("Failed to set controller trigger\n");
		}
	}

	{
		struct InputEvent *Data;

		gameport_io_msg->io_Command = GPD_READEVENT;
		gameport_io_msg->io_Length = sizeof (struct InputEvent);
		gameport_io_msg->io_Data = (APTR) &gameport_data;
		gameport_io_msg->io_Flags = 0;
	}

	SendIO (gameport_io_msg);

	/*
	 * ===========
	 * Screen Pens
	 * ===========
	 */

	WORD ScreenPens[] =
	{
		-1
	};

	/*
	 * =============
	 * Create Screen
	 * =============
	 */

	ScreenWidth = 640;
	ScreenHeight = 400;
	ScreenDepth = 7;

	ScreenWidth = ATARI_WIDTH + 8;
	ScreenHeight = ATARI_HEIGHT + 13;
	ScreenDepth = 7;

	NewScreen.LeftEdge = 0;
	NewScreen.TopEdge = 0;
	NewScreen.Width = ScreenWidth;
	NewScreen.Height = ScreenHeight;
	NewScreen.Depth = ScreenDepth;
	NewScreen.DetailPen = 1;
	NewScreen.BlockPen = 2; /* 2 */
	NewScreen.ViewModes = HIRES | LACE;
	NewScreen.Type = CUSTOMSCREEN;
	NewScreen.Font = NULL;
	NewScreen.DefaultTitle = ATARI_TITLE;
	NewScreen.Gadgets = NULL;
	NewScreen.CustomBitMap = NULL;

	ScreenMain = (struct Screen *) OpenScreenTags
	(
		&NewScreen,
		SA_Left, 0,
		SA_Top, 0,
		SA_Width, ScreenWidth,
		SA_Height, ScreenHeight,
		SA_Depth, ScreenDepth,
		SA_DetailPen, 1,
		SA_BlockPen, 2, /* 2 */
		SA_Pens, ScreenPens,
		SA_Title, ATARI_TITLE,
		SA_Type, CUSTOMSCREEN,
/*
		SA_Overscan, OSCAN_STANDARD,
*/
/*
		SA_DisplayID, ScreenID,
*/
		SA_AutoScroll, TRUE,
/*
		SA_Interleaved, TRUE,
*/
		TAG_DONE
	);

	for (i=0;i<128;i++)
	{
		int rgb = colortable[i];
		int red;
		int green;
		int blue;

		red = (rgb & 0x00ff0000) >> 20;
		green = (rgb & 0x0000ff00) >> 12;
		blue = (rgb & 0x000000ff) >> 4;

		SetRGB4 (&ScreenMain->ViewPort, i, red, green, blue);
	}

	/*
	 * =============
	 * Create Window
	 * =============
	 */

	NewWindow.LeftEdge = 0;
	NewWindow.TopEdge = 0;
	NewWindow.Width = ATARI_WIDTH + 8;
	NewWindow.Height = ATARI_HEIGHT + 13;
	NewWindow.DetailPen = 0;
	NewWindow.BlockPen = 148;
	NewWindow.IDCMPFlags = MENUPICK | GADGETUP | RAWKEY | VANILLAKEY;
	NewWindow.Flags = GIMMEZEROZERO | WINDOWDRAG | WINDOWDEPTH | SMART_REFRESH | ACTIVATE;
	NewWindow.FirstGadget = NULL;
	NewWindow.CheckMark = NULL;
	NewWindow.Title = ATARI_TITLE;
	NewWindow.Screen = ScreenMain;
	NewWindow.Type = CUSTOMSCREEN;
	NewWindow.BitMap = NULL;
	NewWindow.MinWidth = 92;
	NewWindow.MinHeight = 65;
	NewWindow.MaxWidth = 1280;
	NewWindow.MaxHeight = 512;

	WindowMain = (struct Window*) OpenWindowTags
	(
		&NewWindow,
		WA_NewLookMenus, TRUE,
		WA_MenuHelp, TRUE,
		WA_ScreenTitle, ATARI_TITLE,
		TAG_DONE
	);

	if (!WindowMain)
	{
		printf ("Failed to create window\n");
		exit (1);
	}

	image_Screen.LeftEdge = 0;
	image_Screen.TopEdge = 0;
	image_Screen.Width = 384;
	image_Screen.Height = 240;
	image_Screen.Depth = 7;
	image_Screen.ImageData = data_Screen;
	image_Screen.PlanePick = 127;
	image_Screen.PlaneOnOff = NULL;
	image_Screen.NextImage = NULL;

/*
	============================
	Storage for Atari 800 Screen
	============================
*/
	image_data = (UBYTE*) malloc (ATARI_WIDTH * ATARI_HEIGHT);
	if (!image_data)
	{
		printf ("Failed to allocate space for image\n");
		exit (1);
	}

	for (ptr=image_data,i=0;i<ATARI_WIDTH*ATARI_HEIGHT;i++,ptr++) *ptr = 0;
}

void Atari_Exit ()
{

	AbortIO (gameport_io_msg);

	while (GetMsg (gameport_msg_port))
	{
	}

	if (!gameport_error)
	{
		{
			BYTE type = GPCT_NOCONTROLLER;

			gameport_io_msg->io_Command = GPD_SETCTYPE;
			gameport_io_msg->io_Length = 1;
			gameport_io_msg->io_Data = (APTR) &type;

			DoIO (gameport_io_msg);

			if (gameport_io_msg->io_Error)
			{
				printf ("Failed to set controller type\n");
			}
		}

		CloseDevice (gameport_io_msg);
	}

	if (gameport_io_msg)
	{
		DeleteStdIO (gameport_io_msg);
	}

	if (gameport_msg_port)
	{
		DeletePort (gameport_msg_port);
	}

	if (WindowMain)
	{
		CloseWindow (WindowMain);
	}

	if (ScreenMain)
	{
		CloseScreen (ScreenMain);
	}

	if (LayersBase)
	{
		CloseLibrary (LayersBase);
	}

	if (GfxBase)
	{
		CloseLibrary (GfxBase);
	}

	if (IntuitionBase)
	{
		CloseLibrary (IntuitionBase);
	}
}

void Atari_PreUpdate ()
{
	tword = 0;
	bitplane0 = &data_Screen[0];
	bitplane1 = &data_Screen[5760]; 
	bitplane2 = &data_Screen[11520];
	bitplane3 = &data_Screen[17280];
	bitplane4 = &data_Screen[23040];
	bitplane5 = &data_Screen[28800];
	bitplane6 = &data_Screen[34560];

	scanline_ptr = image_data;
/*
	modified = FALSE;
*/
}

void Atari_PostUpdate ()
{
	DrawImage (WindowMain->RPort, &image_Screen, 0, 0);
}

void Atari_ScanLine ()
{
	UWORD	*pixel_ptr;
	int	xpos;
	int	tbit = 15;

	UBYTE	temp[16];

	temp[0] = COLBK;
	temp[1] = COLPF0;
	temp[2] = COLPF1;
	temp[3] = COLBK;
	temp[4] = COLPF2;
	temp[5] = COLBK;
	temp[6] = COLBK;
	temp[7] = COLBK;
	temp[8] = COLPF3;
	temp[9] = COLBK;
	temp[10] = COLBK;
	temp[11] = COLBK;
	temp[12] = COLBK;
	temp[13] = COLBK;
	temp[14] = COLBK;
	temp[15] = COLBK;

	pixel_ptr = scanline;
	for (xpos=0;xpos<ATARI_WIDTH;xpos++)
	{
		UWORD	pixel;
		UBYTE	playfield;
		UBYTE	colour;

		pixel = *pixel_ptr++;
		playfield = pixel & 0x000f;
		colour = temp[playfield];

		if (pixel & 0xff00)
		{
			UBYTE	player;

			player = (pixel >> 8) & 0x000f;

			if (pixel & 0xf000)
			{
				if (pixel & 0x8000)
				{
					M3PF |= playfield;
					M3PL |= player;
					colour = COLPM3;
				}

				if (pixel & 0x4000)
				{
					M2PF |= playfield;
					M2PL |= player;
					colour = COLPM2;
				}

				if (pixel & 0x2000)
				{
					M1PF |= playfield;
					M1PL |= player;
					colour = COLPM1;
				}

				if (pixel & 0x1000)
				{
					M0PF |= playfield;
					M0PL |= player;
					colour = COLPM0;
				}
/*
	==========================
	Handle fifth player enable
	==========================
*/
				if (PRIOR & 0x10)
				{
					colour = COLPF3;
				}
			}

			if (pixel & 0x0f00)
			{
				if (pixel & 0x0800)
				{
					P3PF |= playfield;
					P3PL |= player;
					colour = COLPM3;
				}

				if (pixel & 0x0400)
				{
					P2PF |= playfield;
					P2PL |= player;
					colour = COLPM2;
				}

				if (pixel & 0x0200)
				{
					P1PF |= playfield;
					P1PL |= player;
					colour = COLPM1;
				}

				if (pixel & 0x0100)
				{
					P0PF |= playfield;
					P0PL |= player;
					colour = COLPM0;
				}
			}
		}

		if (colour != *scanline_ptr)
		{
			UWORD mask;
/*
			SetAPen (WindowMain->RPort, colour >> 1);

			WritePixel (WindowMain->RPort, xpos, ypos);
*/
/*
			tbit = 15 - (xpos - (tword << 4));
*/
/*
			tbit = 15 - (xpos & 0x0f);
*/
			mask = ~(1 << tbit);

			*bitplane0 = (*bitplane0 & mask) | (((colour >> 1) & 1) << tbit);
			*bitplane1 = (*bitplane1 & mask) | (((colour >> 2) & 1) << tbit);
			*bitplane2 = (*bitplane2 & mask) | (((colour >> 3) & 1) << tbit);
			*bitplane3 = (*bitplane3 & mask) | (((colour >> 4) & 1) << tbit);
			*bitplane4 = (*bitplane4 & mask) | (((colour >> 5) & 1) << tbit);
			*bitplane5 = (*bitplane5 & mask) | (((colour >> 6) & 1) << tbit);
			*bitplane6 = (*bitplane6 & mask) | (((colour >> 7) & 1) << tbit);

/*
tword = (xpos >> 4);
*/
/*
tbit = 15 - (xpos - (tword << 4));

bitplane0[tword] = 
(bitplane0[tword] & (~(1 << tbit))) | 
(((colour >> 1) & 1) << tbit);

bitplane1[tword] = 
(bitplane1[tword] & (~(1 << tbit))) | 
(((colour >> 2) & 1) << tbit);

bitplane2[tword] = 
(bitplane2[tword] & (~(1 << tbit))) | 
(((colour >> 3) & 1) << tbit);

bitplane3[tword] = 
(bitplane3[tword] & (~(1 << tbit))) | 
(((colour >> 4) & 1) << tbit);

bitplane4[tword] = 
(bitplane4[tword] & (~(1 << tbit))) | 
(((colour >> 5) & 1) << tbit);

bitplane5[tword] = 
(bitplane5[tword] & (~(1 << tbit))) | 
(((colour >> 6) & 1) << tbit);

bitplane6[tword] = 
(bitplane6[tword] & (~(1 << tbit))) | 
(((colour >> 7) & 1) << tbit);
*/
/*
tword = (xpos >> 4);
tbit = 15 - (xpos - (tword << 4));

bitplane0[(ypos * 24) + tword] = 
(bitplane0[(ypos * 24) + tword] & (~(1 << tbit))) | 
(((colour >> 1) & 1) << tbit);

bitplane1[(ypos * 24) + tword] = 
(bitplane1[(ypos * 24) + tword] & (~(1 << tbit))) | 
(((colour >> 2) & 1) << tbit);

bitplane2[(ypos * 24) + tword] = 
(bitplane2[(ypos * 24) + tword] & (~(1 << tbit))) | 
(((colour >> 3) & 1) << tbit);

bitplane3[(ypos * 24) + tword] = 
(bitplane3[(ypos * 24) + tword] & (~(1 << tbit))) | 
(((colour >> 4) & 1) << tbit);

bitplane4[(ypos * 24) + tword] = 
(bitplane4[(ypos * 24) + tword] & (~(1 << tbit))) | 
(((colour >> 5) & 1) << tbit);

bitplane5[(ypos * 24) + tword] = 
(bitplane5[(ypos * 24) + tword] & (~(1 << tbit))) | 
(((colour >> 6) & 1) << tbit);

bitplane6[(ypos * 24) + tword] = 
(bitplane6[(ypos * 24) + tword] & (~(1 << tbit))) | 
(((colour >> 7) & 1) << tbit);
*/
/*
data_Screen[(ypos * 24) + tword] = 
(data_Screen[(ypos * 24) + tword] & (~(1 << tbit))) | 
(((colour >> 1) & 1) << tbit);

data_Screen[(ypos * 24) + tword + 5760] = 
(data_Screen[(ypos * 24) + tword + 5760] & (~(1 << tbit))) | 
(((colour >> 2) & 1) << tbit);

data_Screen[(ypos * 24) + tword + 11520] = 
(data_Screen[(ypos * 24) + tword + 11520] & (~(1 << tbit))) | 
(((colour >> 3) & 1) << tbit);

data_Screen[(ypos * 24) + tword + 17280] = 
(data_Screen[(ypos * 24) + tword + 17280] & (~(1 << tbit))) | 
(((colour >> 4) & 1) << tbit);

data_Screen[(ypos * 24) + tword + 23040] = 
(data_Screen[(ypos * 24) + tword + 23040] & (~(1 << tbit))) | 
(((colour >> 5) & 1) << tbit);

data_Screen[(ypos * 24) + tword + 28800] = 
(data_Screen[(ypos * 24) + tword + 28800] & (~(1 << tbit))) | 
(((colour >> 6) & 1) << tbit);

data_Screen[(ypos * 24) + tword + 34560] = 
(data_Screen[(ypos * 24) + tword + 34560] & (~(1 << tbit))) | 
(((colour >> 7) & 1) << tbit);
*/
			*scanline_ptr++ = colour;
		}
		else
		{
			scanline_ptr++;
		}

		if (--tbit == -1)
		{
			bitplane0++;
			bitplane1++;
			bitplane2++;
			bitplane3++;
			bitplane4++;
			bitplane5++;
			bitplane6++;
			tbit = 15;
		}
/*
		if ((xpos & 0x0f) == 0x0f)
		{
			bitplane0++;
			bitplane1++;
			bitplane2++;
			bitplane3++;
			bitplane4++;
			bitplane5++;
			bitplane6++;
			tword++;
		}
*/
	}
}

int Atari_Keyboard (void)
{
	int keycode;

	struct IntuiMessage *IntuiMessage;

	ULONG Class;
	USHORT Code;
	APTR Address;

	keycode = -1;

	while (IntuiMessage = (struct IntuiMessage*) GetMsg (WindowMain->UserPort))
	{
		Class = IntuiMessage->Class;
		Code = IntuiMessage->Code;
		Address = IntuiMessage->IAddress;

		ReplyMsg (IntuiMessage);

		switch (Class)
		{
			case VANILLAKEY :
printf ("VANILLAKEY = %x\n", Code);
				keycode = Code;

				switch (keycode)
				{
					case 0x01 :
						keycode = AKEY_CTRL_A;
						break;
					case 0x02 :
						keycode = AKEY_CTRL_B;
						break;
					case 0x03 :
						keycode = AKEY_CTRL_C;
						break;
					case 0x04 :
						keycode = AKEY_CTRL_D;
						break;
					case 0x05 :
						keycode = AKEY_CTRL_E;
						break;
					case 0x06 :
						keycode = AKEY_CTRL_F;
						break;
					case 0x07 :
						keycode = AKEY_CTRL_G;
						break;
/*
					case 0x08 :
						keycode = AKEY_CTRL_H;
						break;
*/
					case 0x09 :
						keycode = AKEY_CTRL_I;
						break;
					case 0x0a :
						keycode = AKEY_CTRL_J;
						break;
					case 0x0b :
						keycode = AKEY_CTRL_K;
						break;
					case 0x0c :
						keycode = AKEY_CTRL_L;
						break;
/*
					case 0x0d :
						keycode = AKEY_CTRL_M;
						break;
*/
					case 0x0e :
						keycode = AKEY_CTRL_N;
						break;
					case 0x0f :
						keycode = AKEY_CTRL_O;
						break;
					case 0x10 :
						keycode = AKEY_CTRL_P;
						break;
					case 0x11 :
						keycode = AKEY_CTRL_Q;
						break;
					case 0x12 :
						keycode = AKEY_CTRL_R;
						break;
					case 0x13 :
						keycode = AKEY_CTRL_S;
						break;
					case 0x14 :
						keycode = AKEY_CTRL_T;
						break;
					case 0x15 :
						keycode = AKEY_CTRL_U;
						break;
					case 0x16 :
						keycode = AKEY_CTRL_V;
						break;
					case 0x17 :
						keycode = AKEY_CTRL_W;
						break;
					case 0x18 :
						keycode = AKEY_CTRL_X;
						break;
					case 0x19 :
						keycode = AKEY_CTRL_Y;
						break;
					case 0x1a :
						keycode = AKEY_CTRL_Z;
						break;
					case 8 :
						keycode = AKEY_BACKSPACE;
						break;
					case 13 :
						keycode = AKEY_RETURN;
						break;
					case 0x1b :
						keycode = AKEY_ESCAPE;
						break;
					case '£' :
						keycode = AKEY_EXIT;
						break;
					default :
						break;
				}
				break;
			case RAWKEY :
printf ("RAWKEY = %x\n", Code);
				switch (Code)
				{
					case 0x50 :
						keycode = AKEY_WARMSTART;
						break;
					case 0x51 :
						keycode = AKEY_OPTION;
						break;
					case 0x52 :
						keycode = AKEY_SELECT;
						break;
					case 0x53 :
						keycode = AKEY_START;
						break;
					case 0x54 :
						keycode = AKEY_COLDSTART;
						break;
					case 0x55 :
						keycode = AKEY_PIL;
						break;
					case 0x56 :
						keycode = AKEY_BREAK;
						break;
					case 0x57 :
						keycode = AKEY_DISKCHANGE;
						break;
					case 0x58 :
						keycode = AKEY_EXIT;
						break;
					case 0x59 :
						keycode = AKEY_NONE;
						break;
/*
					case :
						keycode = STICK_LL;
						break;
					case :
						keycode = STICK_BACK;
						break;
					case :
						keycode = STICK_LR;
						break;
					case :
						keycode = STICK_LEFT;
						break;
					case :
						keycode = STICK_CENTRE;
						break;
					case :
						keycode = STICK_RIGHT;
						break;
					case :
						keycode = STICK_UL;
						break;
					case :
						keycode = STICK_FORWARD;
						break;
					case :
						keycode = STICK_UR;
						break;
*/
					case 0x4f :
						keycode = AKEY_LEFT;
						break;
					case 0x4c :
						keycode = AKEY_UP;
						break;
					case 0x4e :
						keycode = AKEY_RIGHT;
						break;
					case 0x4d :
						keycode = AKEY_DOWN;
						break;
					default :
						break;
				}				
				break;
			default :
				break;
		}
	}

	return keycode;
}

int Atari_Joystick (int num)
{
	WORD x;
	WORD y;
	UWORD code;

	int stick = 0;

	if (GetMsg (gameport_msg_port))
	{
		x = gameport_data.ie_X;
		y = gameport_data.ie_Y;
		code = gameport_data.ie_Code;

		switch (x)
		{
			case -1 :
				switch (y)
				{
					case -1 :
						stick = 10;
						break;
					case 0 :
						stick = 11;
						break;
					case 1 :
						stick = 9;
						break;
					default :
						break;
				}
				break;
			case 0 :
				switch (y)
				{
					case -1 :
						stick = 14;
						break;
					case 0 :
						stick = 15;
						break;
					case 1 :
						stick = 13;
						break;
					default :
						break;
				}
				break;
			case 1 :
				switch (y)
				{
					case -1 :
						stick = 6;
						break;
					case 0 :
						stick = 7;
						break;
					case 1 :
						stick = 5;
						break;
					default :
						break;
				}
				break;
			default :
				break;
		}

		if (code == IECODE_LBUTTON)
		{
			if (stick == 0)
			{
				stick = old_stick_0 & 15;
			}
			else
			{
				stick = stick & 15;
			}
		}

		if (code == IECODE_LBUTTON + IECODE_UP_PREFIX)
		{
			if (stick == 0)
			{
				stick = old_stick_0 | 16;
			}
			else
			{
				stick = stick | 16;
			}
		}

		old_stick_0 = stick;
		SendIO (gameport_io_msg);
	}
	
	if (stick == 0)
	{
		stick = old_stick_0;
	}

	return stick;
}

int Atari_Paddle (int num)
{
	return 0x100;
}
