/*
 *		Commodore 64 Spoof Emulator (C) Eddy Carroll, 1st April 1988
 *
 * Module: MAIN.C
 *
 * This module is the mainline - it handles the input loop and command
 * interpretation logic.
 *
 */

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

#define DEL '\177'
/* 
 *  Defining the following functions here stops them from being pulled into
 *  the program from the Lattice library when we link.
 */

int chkabort() {return(0);}
void MemCleanup() {}

/*
 *	Global references
 */

extern struct Menu mymenus;
extern struct IntuiText mytext[];

extern struct commandlist command[];	/* Commands & Responses */
extern char *errormsg[];				/* List of error msgs	*/
extern int  MAXCOMMANDS;				/* Number of commands	*/
extern int  MAXERRORS;					/* Number of messages	*/
extern char *STARTUP;					/* Startup message		*/
extern char *ABOUT;						/* "About..." message	*/
extern char *READY;						/* Ready prompt			*/

/*
 * Executes one of the menu commands
 *
 */

void domenu(opt)
int opt;
{
	CURSOROFF;

	switch (opt) {

	case M_ABOUT:
		clearscreen();
		if (titlebar) printchar('\r'); /* Skip past title bar if on */
		printmess(ABOUT);
		printmess(READY);
		break;

	case M_TITLE:
		titlebar = !titlebar;
		ClearMenuStrip(mywin);
		mytext[1].IText = titlebar ? " Hide Title" : " Show Title";
		SetMenuStrip(mywin,&mymenus);
		ShowTitle(myscreen, titlebar);
		break;

	case M_QUIT:
		cleanup(0);
		break;

	}
}

/*
 * Returns a random number in range 0..range-1.
 *
 */

int random(range)
int range;
{
	LONG seconds,micros;
	CurrentTime(&seconds,&micros);
	return (micros % range);
}


/*
 * Reads current line from screen into string, stripping off any extra
 * leading or trailing spaces.
 *
 */

void getcommand(s)
char *s;
{
	int i = 0, j = 39;
	char *p; 
	p = screen[cursory];
	while (p[i] == ' ' && i < 40)
		i++;
	if (i == 40) {
		*s = '\0';
		return;
	} 

	while (p[j] == ' ')
		j--;
	while (i <= j) {
		*s++ = toupper(p[i]);
		i++;
	}
	*s = '\0';
}


/*
 * Returns TRUE if the first n chars of the two strings are equal, else
 * returns FALSE
 *
 */

int match(s1,s2,n)
char *s1,*s2;
int n;
{
	while (*s1++ == *s2++ && n)
		n--;
	return (!n);
}


/*
 * Initialises error messages to be in random order.
 *
 */

void initerror()
{
	int i, x, y;
	char *p;

	for (i = 0; i < MAXERRORS; i++) {
		x = random(MAXERRORS);
		y = MAXERRORS - 1 - random(MAXERRORS);
		p = errormsg[x];
		errormsg[x] = errormsg[y];
		errormsg[y] = p;
	}
}


/*
 * Prints a suitable response for command in string s. If a match is found
 * in the command list, then a reponse appropriate to that command is
 * printed, else one of the standard error messages is printed.
 *
 */

void docommand(s)
char *s;
{
	static int curerr = 0;
	int i = 1, k = -1, x, y;
	char *p;
	struct commandlist *com;

	if (*s >= '0' && *s <= '9')
		k = 0;
	else {
		while (i < MAXCOMMANDS && k < 0) {
			if (match(command[i].asc,s,command[i].len))
				k = i; 
			i++;
		}
	}

	printchar('\r');
	if (k >= 0) {
		com = &command[k];
		do {
			com->num++;

			/* If we have printed all three messages once each, re-arrange	*/
			/* the order of them before we print them again to make it		*/
			/* seem random.													*/

			if (com->num >= MAXRESPONSE) {
				com->num = 0;
				x = random(MAXRESPONSE); y = random(MAXRESPONSE);
				p = com->response[x];
				com->response[x] = com->response[y];
				com->response[y] = p;
			}
		} while ((p = com->response[com->num]) == NULL);
		printmess(p);
	} else {
		printmess(errormsg[curerr]);
		curerr++;
		if (curerr >= MAXERRORS) {
			curerr = 0;
			initerror();
		}
	}

	printmess(READY);
}


/*
 * Initialises the message headers to point to random messages
 *
 */

void initmess()
{
	int i;
	for (i = 0; i < MAXCOMMANDS; i++)
		command[i].num = random(3);
}


/*
 *  Returns code for function key n, where n = 0..20
 *  Returns 0 if no char equivalent. Also binds otherwise undefined
 *  function keys to common commands recognised.
 */

char fkey(n)
int n;
{
	register char ch = 0;
	switch (n) {

	case 9:		ch = C_HOME;
				break;
	case 19:	ch = C_CLEAR;
				break;
	case 20: 	printmess("HELP");
				break;
	default:	if (n >= 0 & n <= 19)
					printmess(command[n].asc);	/* Get command & print it */
				break;
	}
	return (ch);
}

/*
 * Converts code (an escape character) into one of the internal codes
 * (or 0 if no corresponding code)
 *
 */

char convert(ch)
char ch;
{
	switch (ch) {

	case 'A': ch = C_UP;		break;
	case 'B': ch = C_DOWN;		break;
	case 'C': ch = C_RIGHT;		break;
	case 'D': ch = C_LEFT;		break;

	default:  ch = 0;
	}

	return(ch);
}

/*
 * Mainline
 *
 */

void _main()
{
	LONG MenuNumber;
	struct MenuItem *item;
	register struct IntuiMessage *message;
	register struct Message *conmessage;
	int escape = 0;
	LONG class;
	USHORT code;
	int time = 3, curstate = 0, fkeynum;
	char s[50], ch;


	titlebar = 0;	/* Initially hidden */
	initscreen();
	initmess();
	initerror();
	printmess(STARTUP);
	printmess(READY);
	CURSORON;


	/* Main loop */

#define INTUIBITS	(1 << mywin->UserPort->mp_SigBit)
#define CONSOLEBITS	(1 << ConReadPort->mp_SigBit)

	while (1) {

		/* Reset message pointers, just in case 2nd part of conditional */
		/* && isn't executed. */
		message    = NULL;
		conmessage = NULL;

		while ((message = (struct IntuiMessage *)
					GetMsg(mywin->UserPort)) == NULL &&
				(conmessage = (struct Message *)
					GetMsg(ConReadPort)) == NULL)
			Wait ( INTUIBITS | CONSOLEBITS);


		if (message) {		/* Got an IntuiMessage */
			class = message->Class;
			code = message->Code;
			ReplyMsg((struct Message *)message);

			switch (class) {

			case MENUPICK:
				MenuNumber = code;
				while (MenuNumber != MENUNULL) {
					item = (struct MenuItem *)ItemAddress(&mymenus,MenuNumber);
					code = item->Command;
					MenuNumber = item->NextSelect;
				}
				domenu(code);
				break;

			case INTUITICKS:
				if (time == 0) {
					curstate = 1 - curstate;
					if (curstate)
						CURSORON;
					else
						CURSOROFF;
					time = 3;
				} else --time;
				break;

			}
		}

		if (conmessage) {		/* Character from console */
			ch = *constring;
			/* Retrieve char, then line up request for next char */
			QueueRead(ConReadReq,constring);
			if (ch == CSI)
				escape = 1, fkeynum = 0;
			else {
				if (escape) {
					if (isdigit(ch))
						fkeynum = fkeynum * 10 + ch - '0', ch = 0;
					else if (ch == '?')
						fkeynum = 20, ch = 0;
					else if (ch == ' ')
						escape = 2, ch = 0;
					else {
						escape = 0;
						if (ch == '~')
							ch = fkey(fkeynum);
						else if (escape != 2)
							ch = convert(ch);
					}
				}
				CURSOROFF;
				if (ch == '\r') {
					getcommand(s);	/* Read command from screen */
					printchar(ch);
					if (*s)
						docommand(s);
				} else {
					if (ch == DEL)		/* If delete key (Ascii #127)	*/
						ch = C_INSERT;	/* Make it act as insert		*/
					printchar(ch);
				}
				CURSORON;
				time = 3;
				curstate = 1;
			}
		}
	}
}
