/*
 *		Commodore 64 Spoof Emulator (C) Eddy Carroll, 1st April 1988
 *
 * Module: SCREEN.C
 *
 * This module contains the various screen handling routines used by
 * the emulator.
 *
 */

#define  SCREEN

#include <exec/types.h>
#include <exec/io.h>
#include <intuition/intuition.h>
#include <proto/exec.h>
#include <proto/graphics.h>
#include <proto/intuition.h>
#include "screen.h"

struct RastPort *rp;
int openconsole = 0;

struct NewScreen NewScreen = {
	0,0,320,200,	/* Screen position & size 	*/
	2,				/* 2 bitplanes = 4 colours 	*/
	0,1,			/* Default text colours		*/
	0,				/* Lores, non-interlace		*/
	CUSTOMSCREEN,	/* Screen type				*/
	0,				/* Use default font for now	*/
	"C64 Emulator",	/* Screen title				*/
	0,				/* No gadgets 				*/
	0				/* No custom bitmap			*/
};

#define NORMALFLAGS WINDOWCLOSE | WINDOWSIZING | WINDOWDEPTH | WINDOWDRAG
struct NewWindow NewWindow = {
	0,0,320,200,  	/* Window position & Size	*/
	0,1,			/* Default text colours		*/
	MENUPICK      |	/* Ok, we want to know 		*/
	INTUITICKS,		/* about menus & timings 	*/
	BACKDROP      |	/* These four flags define	*/
	BORDERLESS    |	/* the type of window we	*/
	ACTIVATE      |	/* open. Here, we want an	*/
	0       ,		/* "invisible" window.		*/
	NULL,			/* No gadgets				*/
	NULL,			/* Default menus check mark	*/
	NULL,          	/* No window title			*/
	NULL,			/* Will point to our screen	*/
	NULL,			/* No special bitmap used	*/
	0,0,320,200,	/* No resizing so not used  */
	CUSTOMSCREEN 	/* Open this on OUR screen	*/
};

UWORD blackcol[4] = {	/* All black array for initial screen   */
	0,0,0,0};

UWORD screencol[4] = {	/* Default colours for custom screen	*/
	0x077E,				/* Colour 0 = Light blue				*/
	0x0EEE,				/* Colour 1 = White    					*/
	0x077E,				/* Colour 2 = Lighter blue				*/
	0x011C 				/* Colour 3 = Dark Blue					*/
};

struct IntuiText mytext[] = {
/*  Pens   Mode   Pos   Font    Text                       Next */
{   0,1,   JAM1,  0,0,  NULL,   (UBYTE *)" About...",       NULL},
{   0,1,   JAM1,  0,0,  NULL,   (UBYTE *)" Show Title",     NULL},
{   0,1,   JAM1,  0,0,  NULL,   (UBYTE *)" Quit",           NULL}};



#define MENUFLAGS ITEMTEXT | ITEMENABLED | HIGHCOMP
#define IDATA(off,str,key) 0,off,110,9,MENUFLAGS,0,str,NULL,key,NULL,NULL

struct MenuItem myitems[] = {
{ &myitems[1],  IDATA( 0, (APTR)&mytext[0],  M_ABOUT)  },
{ &myitems[2],  IDATA(10, (APTR)&mytext[1],  M_TITLE)  },
{ NULL,         IDATA(20, (APTR)&mytext[2],  M_QUIT )  }};

struct Menu mymenus = {NULL, 0,0,100,10, MENUENABLED, "Project", myitems};


/* 
 * Free all resources allocated in this module
 *
 */

void cleanup(err)
int err;
{
	if (openconsole)
		CloseDevice(ConReadReq);

	if (ConReadReq)
		DeleteStdIO(ConReadReq);

	if (ConReadPort)
		DeletePort(ConReadPort);

	if (mywin) {
		ClearMenuStrip(mywin);
		CloseWindow(mywin);
	}
	if (myscreen)
		CloseScreen(myscreen);

	if (IntuitionBase)
		CloseLibrary(IntuitionBase);

	if (GfxBase)
		CloseLibrary(GfxBase);

	exit(err);
}



/* 
 * Clears the screen
 *
 */

void clearscreen()
{
	register char *p;
	register int i;
	SetRast(rp,3);
	for (p = (char *)screen, i = 1000; i--; *p++ = ' ')
		;
	cursorx = 0;
	cursory = 0;
}


/* 
 * Tells console device we want to read a character from keyboard
 *
 */

void QueueRead(request,whereto)
struct IOStdReq *request;
char *whereto;
{
	request->io_Command	= CMD_READ;
	request->io_Data	= (APTR)whereto;
	request->io_Length	= 1;
	SendIO(request);
}

/*  
 * Initialises the screen, character set, colours etc. Exits program with
 * error code if an error occurs.
 *
 */

void initscreen()
{
	if (!mywin) {
		if ((IntuitionBase = 
			(struct IntuitionBase *)OpenLibrary("intuition.library",0)) == 0)
			cleanup(1);

		if ((GfxBase =
			(struct GfxBase *)OpenLibrary("graphics.library",0)) == 0)
			cleanup(2);

		if ((myscreen = (struct Screen *)OpenScreen(&NewScreen)) == 0)
			cleanup(3);

		LoadRGB4(&(myscreen->ViewPort),blackcol,4); 
		ShowTitle(myscreen,0);
		NewWindow.Screen = myscreen;

		if ((mywin =  (struct Window *)OpenWindow(&NewWindow)) == 0)
			cleanup(4);

		SetMenuStrip(mywin, &mymenus);
		rp = mywin->RPort;

		/* Initialise communication ports for Console */

		if ((ConReadPort = CreatePort(0,0)) == NULL)
			cleanup(5);

		if ((ConReadReq = CreateStdIO(ConReadPort)) == NULL)
			cleanup(6);

		ConReadReq->io_Data = (APTR)mywin;
		ConReadReq->io_Length = sizeof(*mywin);

		/* Open console and tell it to turn off the cursor */
		if (OpenDevice("console.device",0,ConReadReq,0))
			cleanup(7);
		openconsole = 1;

		/* Tell console to get first character ready for us */
		QueueRead(ConReadReq,constring);

		SetAPen(rp,2);	/* Set primary colour */
		SetBPen(rp,3);	/* Set secondary colour */
		clearscreen();	/* Fill screen to raster colour */
		LoadRGB4(&(myscreen->ViewPort),screencol,4); 

	}
}


/*
 * Outputs a character to the screen at co-ordinates x,y. If mode is 0,
 * then output normal character, else output reverse character.
 *
 */

void writechar(x,y,ch,mode)
int x,y;
char ch;
int mode;
{
	char s[2];
	s[0] = ch;
	Move(rp,x * 8, y * 8 + 6); /* Quick multiply by 8 */
	if (mode) {
		SetDrMd(rp,INVERSVID|JAM2);
		Text(rp,s,1);
		SetDrMd(rp,          JAM2);
	} else
		Text(rp,s,1);
}

/* 
 * Scrolls the screen up one line. Both the visible rastport screen, 
 * and the internal screen are scrolled.
 *
 */

void scrollup()
{
	register char *p, *q;
	register int i;

	ScrollRaster(rp,0,8,0,0,319,199);
	for (p = (char *)screen,q = (char *)screen+40, i = 960; i--; *p++ = *q++)
		;
	for (i = 40; i--; *p++ = ' ')
		;
	cursory--;

}



/*
 * Handles special characters like CR, cursor keys etc.
 *
 */

void special(ch)
char ch;
{
	register char *p, *q;
	register int i,j;

	switch (ch) {

	case C_CR: /* Carriage Return */
		cursorx = 0;
		cursory++;
		if (cursory >= 25)
			scrollup();
		break;

	case C_UP: /* Cursor Up */
		if (cursory)
			cursory--;
		break;

	case C_DOWN: /* Cursor Down */
		cursory++;
		if (cursory >= 25)
			scrollup();
		break;

	case C_LEFT: /* Cursor Left */
		if (cursorx == 0) {
			if (cursory) {
				cursorx = 39;
				cursory--;
			}
		} else
			cursorx--;
		break;

	case C_RIGHT: /* Cursor Right */
		cursorx++;
		if (cursorx >= 40) {
			cursorx = 0;
			cursory++;
			if (cursory >= 25)
				scrollup();
		}
		break;

	case C_DEL: /* Delete */
		if (cursorx == 0) {
			if (cursory) {
				cursorx = 39;
				cursory--;
				screen[cursory][cursorx] = ' ';
				writechar(cursorx,cursory,' ',0);
			}
		} else {
			ScrollRaster(rp,8,0,cursorx*8,cursory*8,319,cursory*8+7);
			/* Move chars from here to end of line back one space */
			for (q = &(screen[cursory][cursorx]),
						p = q - 1, i = 40 - cursorx;
						i--;
						*p++ = *q++)
						;
			*p = ' ';
			cursorx--;
		}
		break;

	case C_INSERT: /* Insert */
		if (screen[cursory][39] == ' ' && cursorx < 39) {
			ScrollRaster(rp,-8,0,cursorx*8,cursory*8,319,cursory*8+7);
			for (p = &screen[cursory][39], q = p - 1, i = 39 - cursorx;
					i--; *p-- = *q--)
						;
			*p = ' ';
		}
		break;

	case C_CLEAR: /* Clear screen */
		clearscreen();
		break;

	case C_HOME: /* Home cursor */
		cursorx = 0;
		cursory = 0;
		break;

	case C_REDRAW: /* Redraw entire screen */
		for (i = 0; i < 25; i++)
			for (j = 0; j < 40; j++)
				writechar(j,i,screen[i][j],0);
		break;

	}
}



/*
 * Outputs character to screen at current cursor position, and advances
 * cursor to next position. Scrolls screen if necessary.
 *
 */

void printchar(ch)
char ch;
{
	if (ch < ' ')
		special(ch);
	else {
		writechar(cursorx,cursory,ch,0);
		screen[cursory][cursorx] = ch;
		cursorx++;
		if (cursorx >= 40) {
			cursorx = 0;
			cursory++;
			if (cursory >= 25)
				scrollup();
		}
	}
}


/* 
 * Outputs string to screen at current cursor position
 *
 */

void printmess(s)
char *s;
{
	while (*s)
		printchar(*s++);
}
