#include "ownincs/IBMconsole.h"

               /* dies wird einmal ein GUTER Parser..(-: */

                            /* IBM Zerhacker */



/* allowed globals.. */

IMPORT struct	ExecBase			*SysBase;
IMPORT struct	DosLibrary		*DOSBase;
IMPORT struct	GfxBase			*GfxBase;
IMPORT struct	IntuitionBase	*IntuitionBase;
struct	Library			*DiskfontBase;
struct	Library			*KeymapBase;

/* utils.a */
VOID *GetSucc(VOID *);
VOID *GetHead(VOID *);
VOID *GetTail(VOID *);
VOID *GetPred(VOID *);
VOID BZero(VOID *, LONG);
VOID BSet(VOID *, LONG, LONG);



struct ConParm {
	VOID (*sigfunc)(struct IBMConsole *con, UBYTE *args, ULONG argc);
	UBYTE intermediate;	/* FIRST intermediate..!! */
	UBYTE sigchar;
};


struct ConParm CSI_cmds[] = {								/* Mnemonic */
	{ IBM_insertchars			,	NUL,	'@' },			/* ICH */
	{ IBM_cursorup				,	NUL,	'A' },			/* CUU */
	{ IBM_cursordown			,	NUL,	'B' },			/* CUD */
	{ IBM_cursorright			,	NUL,	'C' },			/* CUF */
	{ IBM_cursorleft			,	NUL,	'D' },			/* CUB */
	{ IBM_cursornextline		,	NUL,	'E' },			/* CNL */
	{ IBM_cursoraboveline	,	NUL,	'F' },			/* CPL */
	{ IBM_cursorposition		,	NUL,	'H' },			/* CUP */
	{ IBM_eraseindisplay		,	NUL,	'J' },			/* ED  */
	{ IBM_eraseinline			,	NUL,	'K' },			/* EL  */
	{ IBM_insertlines			,	NUL,	'L' },			/* IL  */
	{ IBM_deletelines			,	NUL,	'M' },			/* DL  */
	{ IBM_deletechars			,	NUL,	'P' },			/* DCH */
	{ IBM_ignore				,	NUL,	'R' },			/* CPR */
	{ IBM_scrollup				,	NUL,	'S' },			/* SU  */
	{ IBM_scrolldown			,	NUL,	'T' },			/* SD  */

	{ IBM_cursorposition		,	NUL,	'f' },			/* HVP */
	{ IBM_ANSIsetmodes		,	NUL,	'h' },			/* SM */
	{ IBM_ANSIresetmodes		,	NUL,	'l' },			/* RM */
	{ IBM_ANSIsetmodes		,	'>',	'h' },			/* SM */
	{ IBM_ANSIresetmodes		,	'>',	'l' },			/* RM */
	{ IBM_ANSIsetmodes		,	'?',	'h' },			/* SM */
	{ IBM_ANSIresetmodes		,	'?',	'l' },			/* RM */
	{ IBM_textmodes			,	NUL,	'm' },			/* SGR */
	{ IBM_devicestatreport	,	NUL,	'n' },			/* DSR */
	{ IBM_savecursor			,	NUL,	's' },			/* */
	{ IBM_restorecursor		,	NUL,	'u' },			/* */

	{ NULL, NUL, NUL },		/* der krönende Abschluss..(-: */
};




VOID __saveds XEmulatorExpu(VOID)
{
	if(DiskfontBase)
	{
		CloseLibrary(DiskfontBase);
		DiskfontBase = NULL;
	}


	if(KeymapBase)
	{
		CloseLibrary(KeymapBase);
		KeymapBase = NULL;
	}
}


BOOL __saveds XEmulatorInit(VOID)
{
	KeymapBase		= OpenLibrary("keymap.library", LIBRARY_MINIMUM);
	DiskfontBase	= OpenLibrary("diskfont.library", LIBRARY_MINIMUM);

	if(KeymapBase == NULL  ||  DiskfontBase == NULL)
	{
		XEmulatorExpu();
		return(FALSE);
	}

	return(TRUE);
}


BOOL __saveds __asm XEmulatorSetup(register __a0 struct XEM_IO *io)
{
	STATIC struct TextAttr font = { "ibm.font", 8, FS_NORMAL, FPF_DISKFONT | FPF_DESIGNED };
	struct IBMConsole *con;
	
	io->xem_console = NULL;

	if(con = AllocMem(sizeof(struct IBMConsole), MEMF_PUBLIC | MEMF_CLEAR))
	{
		con->io = io;			/* for easier passing..(-; */
		con->rp = io->xem_window->RPort;
		io->xem_console = con;
		*con->io->xem_signal = 0;

		IBM_internalsettings(con);		/* just to be on the save side.. */

		if((con->font = OpenFont(&font)) == NULL)
			con->font = OpenDiskFont(&font);

		return(con->font != NULL);
	}

	return(FALSE);
}


VOID __saveds __asm XEmulatorCleanup(register __a0 struct XEM_IO *io)
{
	struct IBMConsole *con = (struct IBMConsole *)io->xem_console;

	if(con)
	{
		if(con->font)
		{
			CloseFont(con->font);
		}

		SetFont(con->rp, con->io->xem_font);

		*con->io->xem_signal = 0;
		FreeMem(con, sizeof(struct IBMConsole));
	}
}


BOOL __saveds __asm XEmulatorOpenConsole(register __a0 struct XEM_IO *io)
{
	struct IBMConsole *con = (struct IBMConsole *)io->xem_console;

	con->rp = io->xem_window->RPort;

	switch(con->io->xem_screendepth)
	{
		default:
		case 1:	/* MONO */
			con->foreground_pen = 1;
			con->bold = FALSE;
			con->mono = TRUE;
		break;

		case 2:	/* MONO & BOLD */
			con->foreground_pen = 1;
			con->bold = TRUE;
			con->mono = TRUE;
		break;

		case 3:	/* COLOR */
			con->foreground_pen = 7;
			con->bold = FALSE;
			con->mono = FALSE;
		break;

		case 4:	/* COLOR & BOLD */
			con->foreground_pen = 7;
			con->bold = TRUE;
			con->mono = FALSE;
		break;
	}

	IBM_reset(con);

	IBM_cursorflip(con);		/* cursor sichtbar machen.. */

	return(TRUE);
}


VOID __saveds __asm XEmulatorCloseConsole(register __a0 struct XEM_IO *io)
{
	return;
}


VOID __saveds __asm XEmulatorWrite(register __a0 struct XEM_IO *io, register __a1 UBYTE *buff, register __d0 ULONG buflen)
{
	struct IBMConsole *con = (struct IBMConsole *)io->xem_console;

	if(con != NULL)
		IBM_writecon(con, buff, buflen);
}
VOID IBM_writecon(struct IBMConsole *con, UBYTE *buff, ULONG buflen)
{
	UBYTE c, *scp;

	IBM_cursorflip(con);

	if((LONG)buflen == -1)
		buflen = strlen(buff);

	scp = buff;
	while(buflen--)
	{
		c = *scp++;

		if(c == CAN || c == SUB)
		{
			IBM_cancel(con);
			continue;
		}

		if(con->inESC)
		{
			con->inESC = FALSE;

			if(c == '[')
				IBM_invokeCSI(con, NULL, NUL);

			continue;
		}

		if(con->inCSI)
		{
			UWORD i;

			if(c < '@')
			{
				if(c >= '0' && c <= '9')	/* Parameter.. */
				{
					con->args[con->argc] = con->args[con->argc] * 10;
					con->args[con->argc] += c - '0';
					continue;
				}

				if(c == ';')					/* Parameter-TrennZeichen */
				{
					con->args[++con->argc] = 0;
					if(con->argc > MAXARGS)
						con->argc = MAXARGS;
					continue;
				}

				if(con->icnt < MAXINTER)
				{
					con->inter[con->icnt++] = c;	/* something like '?', etc. */
					con->inter[con->icnt] = NUL;	/* clear next intermediate */
				}
				continue;
			}

			for(i=0; CSI_cmds[i].sigfunc; i++)
			{
				if(				c == CSI_cmds[i].sigchar		&&
					con->inter[0] == CSI_cmds[i].intermediate)
				{
					if(con->ordc)
						IBM_textout(con);

					con->inCSI = c;
					CSI_cmds[i].sigfunc(con, con->args, con->argc);
					break;
				}
			}
			con->inCSI = FALSE;
			continue;
		}

		if(c < ' '  &&  con->ordc)
			IBM_textout(con);

		switch(c)
		{
			case ESC:
				IBM_invokeESC(con, NULL, NUL);
			break;
/*
			case CSI:
				IBM_invokeCSI(con, NULL, NUL);
			break;
*/
			case BEL:
				con->io->xem_tbeep(1, 0);
			break;

			case BS:
				if(con->col > 1)
					con->col--;
			break;

			case HT:
				while(con->col < con->columns)
				{
					con->col++;
					if(con->tabs[con->col])
						break;
				}
			break;

			case LF:
				if(con->stat & NEWLINE)
					IBM_cursornextline(con, "\001", 0);
				else
					IBM_cursordown(con, "\001", 0);
			break;

			case VT:
				IBM_cursorup(con, "\001", 0);
			break;

			case FF:
				IBM_erasescreen(con);
			break;

			case CR:
				con->col = 1;
			break;

			case SO:
				con->shift = 128;
			break;

			case SI:
				con->shift = 0;
			break;

			default:
				if(!con->ordc)
					con->ordcol = con->col;

				con->ordtext[con->ordc] = c | con->shift;
				con->col++;
				con->ordc++;

				if(con->col > con->columns)
				{
					IBM_textout(con);

					if(con->stat & X_SCROLLCURSOR)
						IBM_cursornextline(con, "\001", 0);
					else
						con->col = con->columns;
				}
			break;
		}
	}

	if(con->ordc)
		IBM_textout(con);

	IBM_cursorflip(con);
}


BOOL __saveds __asm XEmulatorSignal(register __a0 struct XEM_IO *io, register __d0 ULONG sig)
{
	return(TRUE);
}


STATIC BOOL HandleMacroKeys(struct IBMConsole *con, struct IntuiMessage *imsg, UBYTE chr)
{
	if(con->macrokeys  &&  con->io->xem_process_macrokeys)
	{
		struct XEmulatorMacroKey *key;
		BOOL shift, alt, ctrl;
		UWORD qual;

		shift	= imsg->Qualifier & (IEQUALIFIER_LSHIFT | IEQUALIFIER_RSHIFT);
		ctrl	= imsg->Qualifier & IEQUALIFIER_CONTROL;
		alt	= imsg->Qualifier & (IEQUALIFIER_LALT | IEQUALIFIER_RALT);

		if(shift)
			qual = XMKQ_SHIFT;
		else
		{
			if(alt)
				qual = XMKQ_ALTERNATE;
			else
			{
				if(ctrl)
					qual = XMKQ_CONTROL;
				else
					qual = XMKQ_NONE;
			}
		}

		if(key = GetHead(con->macrokeys))
		{
			do
			{
				if(key->xmk_Qualifier == qual)
				{
					BOOL match=FALSE;

					if(key->xmk_Type == XMKT_RAWKEY)
					{
						if(key->xmk_Code == imsg->Code)
							match = TRUE;
					}

					if(key->xmk_Type == XMKT_COOKED)
					{
						if(key->xmk_Code == chr)
							match = TRUE;
					}

					if(match != FALSE)
					{
						con->io->xem_process_macrokeys(key);
						return(TRUE);
					}
				}
			}
			while(key = GetSucc(key));
		}
	}
	return(FALSE);
}


ULONG __saveds __asm XEmulatorUserMon(register __a0 struct XEM_IO *io, register __a1 UBYTE *retstr, register __d0 ULONG maxlen, register __a2 struct IntuiMessage *imsg)
{
	struct IBMConsole *con = (struct IBMConsole *)io->xem_console;
	struct InputEvent ie;
	ULONG length=0;
	UWORD code;

	code = imsg->Code;
	if(code & IECODE_UP_PREFIX)
		return(0);

	retstr[0] = '\0';

   ie.ie_Class			= IECLASS_RAWKEY;
   ie.ie_SubClass		= 0;
	ie.ie_Code			= code;
	ie.ie_Qualifier	= imsg->Qualifier;
	ie.ie_position.ie_addr = *((APTR *)imsg->IAddress);

	if((length = MapRawKey(&ie, retstr, 20, NULL)) <= 0)
		return(0);

	if(retstr[0] == CR  &&  con->stat & NEWLINE  &&
	  (code == 0x0043  ||  code == 0x0044))
	{
		retstr[1] = LF;
		retstr[2] = '\0';
		length = 2;
	}
	else
	{
		if(retstr[0] == CSI)
		{
			auto UBYTE mybuf[40];

			if(length > (40-1))
				return(0);

			strcpy(&mybuf[1], retstr);
			mybuf[0] = ESC;
			mybuf[1] = '[';
			strcpy(retstr, mybuf);
			length++;
		}
	}

	retstr[length] = '\0';

	if(HandleMacroKeys(con, imsg, retstr[0]))
		length = 0;

	return(length);
}


ULONG __saveds __asm XEmulatorHostMon(register __a0 struct XEM_IO *io, register __a1 struct XEmulatorHostData *hd, register __d0 ULONG actual)
{
	if(hd->Destination != NULL)
	{
		ULONG cnt;
		UBYTE *read, *write;
		REGISTER UBYTE c;

		read	= hd->Source;
		write = hd->Destination;
		for(cnt=0; cnt < actual; cnt++)
		{
			c = *read++;

			if(c == '\x18'  ||  c == '\x1A')	/* CAN  ||  SUB */
			{
				hd->InESC = FALSE;
				hd->InCSI = FALSE;
				continue;
			}

			if(hd->InESC)		/* Escape - Befehl ? */
			{
				if(c == '[')	/* ist's ein verkappter CSI ? */
				{
					hd->InCSI = TRUE;
					hd->InESC = FALSE;
				}
				else
				{
					if(c >= '0')
						hd->InESC = FALSE;
				}
				continue;
			}

			if(hd->InCSI)		/* CSI - Befehl ? */
			{
				if(c >= '@')
					hd->InCSI = FALSE;
				continue;
			}


			if(c == ESC)
			{
				hd->InESC = TRUE;
				hd->InCSI = FALSE;
				continue;
			}

			if(c == CSI)
			{
				hd->InESC = FALSE;
				hd->InCSI = TRUE;
				continue;
			}

			*write++ = c;
		}

		return((ULONG)(write - hd->Destination));
	}
	else
		return(0);
}


BOOL __saveds __asm XEmulatorClearConsole(register __a0 struct XEM_IO *io)
{
	struct IBMConsole *con = (struct IBMConsole *)io->xem_console;

	if(con != NULL)
		IBM_writecon(con, "\014", 1);

	return(TRUE);
}


BOOL __saveds __asm XEmulatorResetConsole(register __a0 struct XEM_IO *io)
{
	struct IBMConsole *con = (struct IBMConsole *)io->xem_console;

	if(con != NULL)
	{
		IBM_reset(con);			/* RIS */
		IBM_cursorflip(con);		/* Cursor sichtbar machen.. */
	}

	return(TRUE);
}


BOOL __saveds __asm XEmulatorResetTextStyles(register __a0 struct XEM_IO *io)
{
	struct IBMConsole *con = (struct IBMConsole *)io->xem_console;

	if(con != NULL)
		IBM_writecon(con, "\033[m", -1);
	return(TRUE);
}


BOOL __saveds __asm XEmulatorResetCharset(register __a0 struct XEM_IO *io)
{
	struct IBMConsole *con = (struct IBMConsole *)io->xem_console;

	if(con != NULL)
		IBM_writecon(con, "\017", 1);
	return(TRUE);
}


BOOL __saveds __asm XEmulatorOptions(register __a0 struct XEM_IO *io)
{
#define BUFLEN 4

	struct IBMConsole *con = (struct IBMConsole *)io->xem_console;
	UBYTE opt_header=0, opt_crlf, opt_horiz, opt_vert;
	ULONG numopts;

	struct xem_option *opti[6];
	UBYTE buff[6][BUFLEN];

	struct xem_option opt0;
	struct xem_option opt1;
	struct xem_option opt2;
	struct xem_option opt3;
	struct xem_option opt4;
	struct xem_option opt5;

	opti[0] = &opt0;
	opti[1] = &opt1;
	opti[2] = &opt2;
	opti[3] = &opt3;
	opti[4] = &opt4;
	opti[5] = &opt5;

	numopts = 0;

	opti[opt_header]->xemo_description = "IBM - ANSI options:";
	opti[opt_header]->xemo_type	= XEMO_HEADER;
	opti[opt_header]->xemo_value	= NULL;
	opti[opt_header]->xemo_length	= NULL;

	opt_crlf = ++numopts;
	opti[opt_crlf]->xemo_value = buff[opt_crlf];
	if(con->stat & NEWLINE)
		strcpy(opti[opt_crlf]->xemo_value, "on");
	else
		strcpy(opti[opt_crlf]->xemo_value, "off");
	opti[opt_crlf]->xemo_description = "EOL-tx and LF-rx => CR+LF";
	opti[opt_crlf]->xemo_type		= XEMO_BOOLEAN;
	opti[opt_crlf]->xemo_length	= BUFLEN;

	opt_horiz = ++numopts;
	opti[opt_horiz]->xemo_value = buff[opt_horiz];
	if(con->stat & X_SCROLLCURSOR)
		strcpy(opti[opt_horiz]->xemo_value, "on");
	else
		strcpy(opti[opt_horiz]->xemo_value, "off");
	opti[opt_horiz]->xemo_description = "Auto horizontal scrolling";
	opti[opt_horiz]->xemo_type		= XEMO_BOOLEAN;
	opti[opt_horiz]->xemo_length	= BUFLEN;

	opt_vert = ++numopts;
	opti[opt_vert]->xemo_value = buff[opt_vert];
	if(con->stat & Y_SCROLLCURSOR)
		strcpy(opti[opt_vert]->xemo_value, "on");
	else
		strcpy(opti[opt_vert]->xemo_value, "off");
	opti[opt_vert]->xemo_description = "Auto vertical scrolling";
	opti[opt_vert]->xemo_type		= XEMO_BOOLEAN;
	opti[opt_vert]->xemo_length	= BUFLEN;


	con->io->xem_toptions(++numopts, opti); 


	if(!stricmp(opti[opt_crlf]->xemo_value, "yes") || !stricmp(opti[opt_crlf]->xemo_value, "on"))
		con->stat |= NEWLINE;
	else
		con->stat &= ~NEWLINE;

	if(!stricmp(opti[opt_horiz]->xemo_value, "yes") || !stricmp(opti[opt_horiz]->xemo_value, "on"))
		con->stat |= X_SCROLLCURSOR;
	else
		con->stat &= ~X_SCROLLCURSOR;

	if(!stricmp(opti[opt_vert]->xemo_value, "yes") || !stricmp(opti[opt_vert]->xemo_value, "on"))
		con->stat |= Y_SCROLLCURSOR;
	else
		con->stat &= ~Y_SCROLLCURSOR;

	return(TRUE);
}


ULONG __saveds __asm XEmulatorGetFreeMacroKeys(register __a0 struct XEM_IO *io, register __d0 ULONG qualifier)
{
	return(512 + 256 + 128 + 64 + 32 + 16 + 8 + 4 + 2 + 1);
}


BOOL __saveds __asm XEmulatorMacroKeyFilter(register __a0 struct XEM_IO *io, register __a1 struct List *macrokeys)
{
	struct IBMConsole *con = (struct IBMConsole *)io->xem_console;

	if(con != NULL  &&  con->io->xem_process_macrokeys)
	{
		con->macrokeys = macrokeys;
		return(TRUE);
	}

	return(FALSE);
}


LONG __saveds __asm XEmulatorInfo(register __a0 struct XEM_IO *io, register __d0 ULONG type)
{
	struct IBMConsole *con = (struct IBMConsole *)io->xem_console;
	LONG result = -1;

	if(con != NULL)
	{
		switch(type)
		{
			case XEMI_CURSOR_POSITION:
				result = XEMI_CREATE_POSITION(con->row, con->col);
			break;

			case XEMI_CONSOLE_DIMENSIONS:
				result = XEMI_CREATE_DIMENSIONS(con->columns, con->lines);
			break;

		}
	}

	return(result);
}


BOOL __saveds __asm XEmulatorPreferences(register __a0 struct XEM_IO *io, register __a1 STRPTR filename, register __d0 ULONG mode)
{
	struct IBMConsole *con = (struct IBMConsole *)io->xem_console;
	BOOL success=FALSE;

	if(con != NULL)
	{
		switch(mode)
		{
			case XEM_PREFS_RESET:
				success = TRUE;
				IBM_internalsettings(con);
			break;

			case XEM_PREFS_LOAD:
			{
				BPTR fh;
				UBYTE buf[80];

				if(fh = Open(filename, MODE_OLDFILE))
				{
					if(Read(fh, buf, 80) > 0)
					{
						if(IBM_parseoptions(con, buf) == FALSE)
							IBM_internalsettings(con);
						else
							success = TRUE;
					}
					Close(fh);
				}
			}
			break;

			case XEM_PREFS_SAVE:
			{
				BPTR fh;

				if(fh = Open(filename, MODE_NEWFILE))
				{
					UBYTE *mode;

					success = TRUE;

					mode = (con->stat & X_SCROLLCURSOR) ? "YES" : "NO";
					fprintf(fh, "HSCROLL=%s", mode);

					mode = (con->stat & Y_SCROLLCURSOR) ? "YES" : "NO";
					fprintf(fh, " VSCROLL=%s", mode);

					mode = (con->stat & NEWLINE) ? "YES" : "NO";
					fprintf(fh, " NEWLINE=%s\n", mode);

					con->r_stat = con->stat;

					Close(fh);
				}
				else
					success = FALSE;
			}
			break;

		}
	}
	return(success);
}


STATIC VOID IBM_internalsettings(struct IBMConsole *con)
{
	/* some good(?) settings */

	con->r_stat &= ~NEWLINE;
	con->r_stat |= Y_SCROLLCURSOR;
	con->r_stat |= X_SCROLLCURSOR;
}


enum { ARG_HS=0,ARG_VS,ARG_NL,ARGS };

STATIC BOOL IBM_parseoptions(struct IBMConsole *con, STRPTR optionsBuffer)
{
	STATIC UBYTE *PreferencesTemplate = "HS=HSCROLL/K,VS=VSCROLL/K,NL=NEWLINE/K";
	UBYTE **ArgArray;
	UBYTE *ArgBuffer;
	BOOL success=FALSE;

	if(SysBase->LibNode.lib_Version < 37)	/* sorry, wrong number..<-:< */
		return(FALSE);

	if(ArgBuffer = (UBYTE *)AllocVec(1024, MEMF_ANY | MEMF_CLEAR))
	{
		strcpy(ArgBuffer, optionsBuffer);
		strcat(ArgBuffer, "\n");

		if(ArgArray = (UBYTE **)AllocVec(sizeof(UBYTE *) * ARGS, MEMF_ANY | MEMF_CLEAR))
		{
			struct RDArgs *ArgsPtr;

			if(ArgsPtr = (struct RDArgs *)AllocDosObject(DOS_RDARGS, TAG_DONE))
			{
				ArgsPtr->RDA_Source.CS_Buffer = ArgBuffer;
				ArgsPtr->RDA_Source.CS_Length = strlen(ArgBuffer);
				ArgsPtr->RDA_Source.CS_CurChr = 0;
				ArgsPtr->RDA_DAList	= NULL;
				ArgsPtr->RDA_Buffer	= NULL;
				ArgsPtr->RDA_BufSiz	= 0;
				ArgsPtr->RDA_ExtHelp	= NULL;
				ArgsPtr->RDA_Flags	= RDAF_NOPROMPT;

				if(ReadArgs(PreferencesTemplate, (LONG *)ArgArray, ArgsPtr))
				{
/*					VOID KPrintF(UBYTE *, ...); */
					success = TRUE;

					if(ArgArray[ARG_HS])
					{
						if(!strnicmp(ArgArray[ARG_HS], "NO", 2))
							con->r_stat &= ~X_SCROLLCURSOR;
						else
							con->r_stat |= X_SCROLLCURSOR;
					}

					if(ArgArray[ARG_VS])
					{
						if(!strnicmp(ArgArray[ARG_VS], "NO", 2))
							con->r_stat &= ~Y_SCROLLCURSOR;
						else
							con->r_stat |= Y_SCROLLCURSOR;
					}

					if(ArgArray[ARG_NL])
					{
						if(!strnicmp(ArgArray[ARG_NL], "NO", 2))
							con->r_stat &= ~NEWLINE;
						else
							con->r_stat |= NEWLINE;
					}

					FreeArgs(ArgsPtr);
				}
				FreeDosObject(DOS_RDARGS, ArgsPtr);
			}
			FreeVec(ArgArray);
		}
		FreeVec(ArgBuffer);
	}
	return(success);
}


/* end of source-code */
