#include "ownincs/ASCIIconsole.h"

STATIC VOID		ASCII_cursorflip(struct ASCIIConsole *);
STATIC VOID		ASCII_textout(struct ASCIIConsole *);
STATIC VOID		ASCII_cursorup(struct ASCIIConsole *, UWORD);
STATIC VOID		ASCII_cursordown(struct ASCIIConsole *, UWORD);
STATIC VOID		ASCII_reset(struct ASCIIConsole *);
STATIC VOID		ASCII_internalsettings(struct ASCIIConsole *);


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



                           /* ASCII Zerhacker */


IMPORT struct	ExecBase			*SysBase;
IMPORT struct	DosLibrary		*DOSBase;
IMPORT struct	GfxBase			*GfxBase;
IMPORT struct	IntuitionBase	*IntuitionBase;

struct	Library	*KeymapBase;


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


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

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

	return(TRUE);
}


BOOL __saveds __asm XEmulatorSetup(register __a0 struct XEM_IO *io)
{
	struct ASCIIConsole *con;
	
	io->xem_console = NULL;

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

		ASCII_internalsettings(con);		/* just a preset */

		*con->io->xem_signal = 0;

		return(TRUE);
	}

	return(FALSE);
}


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

	if(con)
	{
		*con->io->xem_signal = 0;
		FreeMem(con, sizeof(struct ASCIIConsole));
		io->xem_console = NULL;
	}
}



LONG AtoL(UBYTE *str)
{
	LONG cnt=0, help;

	while(*str <= ' ')		/* skip white spaces.. */
	{
		if(*str == '\0')
			return(0);
		str++;
	}

	if(*str == '-')
	{
		help = -1;
		str++;
	}
	else
	{
		if(*str == '+')
			str++;
		help = 1;
	}
	
	for(;;)
	{
		if(*str >= '0' && *str <= '9')
		{
			cnt *= 10;
			cnt += *str - '0';
		}
		else
			break;

		if(!(*str))
			break;

		str++;
	}

	return(cnt * help);
}


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

	if(con == NULL)
		return(FALSE);

	con->rp = io->xem_window->RPort;		/* DO NOT forget this..!! */

	ASCII_reset(con);

	con->popup = TRUE;

	return(TRUE);
}


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

	con->popup = FALSE;
	return;
}


STATIC VOID ASCII_reset(struct ASCIIConsole *con)
{
	con->lines	= con->io->xem_window->Height	>> 3;
	con->columns= con->io->xem_window->Width	>> 3;
	con->width	= con->io->xem_window->Width;
	con->row		= 1;
	con->col		= 1;
	con->ordc	= 0;
	con->ordcol = 1;

	SetRast(con->rp, BACKGROUND_PEN);
	SetDrMd(con->rp, JAM2);
	SetFont(con->rp, con->io->xem_font);
	SetAPen(con->rp, con->foreground_pen);
	SetBPen(con->rp, BACKGROUND_PEN);
	SetSoftStyle(con->rp, 0, 0);

	ASCII_cursorflip(con);
}


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

	ULONG len;
	BOOL tty=FALSE;
	UBYTE c, *scp;

	ASCII_cursorflip(con);

	len = (((LONG)buflen) == -1) ? strlen(buff) : buflen;
	scp = buff;
	while(len--)
	{
		if(tty)
		{
			tty = FALSE;
			c = '[';
			goto PUTIT;
		}

		c = *scp++;

		if(con->ASCII)	/* else TTY */
		{
			if(c == CAN || c == SUB)
			{
				con->inESC = FALSE;
				con->inCSI = FALSE;

				continue;
			}


			if(con->inESC)
			{
				if(c == '[')	/* ist's ein verkappter CSI ? */
				{
					con->inESC = FALSE;
					con->inCSI = TRUE;
				}
				else
				{
					if(c >= '0')
						con->inESC = FALSE;
				}

				continue;
			}


			if(con->inCSI)		/* CSI - Befehl ? */
			{
				if(c >= '@')
					con->inCSI = FALSE;

				continue;
			}
		}


		switch(c)
		{
			case BS:
				if(con->ordc)
					ASCII_textout(con);
				if(con->col > 1)
					con->col--;
			break;

			case LF:
				if(con->ordc)
					ASCII_textout(con);
				ASCII_cursordown(con, 1);
			break;

			case VT:
				if(con->ordc)
					ASCII_textout(con);
				ASCII_cursorup(con, 1);
			break;

			case FF:
				con->row = 1;
				con->col = 1;
				if(con->ordc)
					ASCII_textout(con);
				SetRast(con->rp, BACKGROUND_PEN);
			break;

			case CR:
				if(con->ordc)
					ASCII_textout(con);
				con->col = 1;
			break;

			case ESC:
				if(con->ASCII)
					con->inESC = TRUE;
				else
				{
					c = '^';
					goto PUTIT;
				}
			break;

			case CSI:
				if(con->ASCII)
					con->inCSI = TRUE;
				else
				{
					tty = TRUE;
					len++;
					c = '^';
					goto PUTIT;
				}
			break;

			default:
			{
PUTIT:
				if((c >= 32 && c < 127)  ||  (c >= 160 && c < 255))
				{
					if(con->ordc == 0)
						con->ordcol = con->col;
					con->ordtext[con->ordc++] = c;
					con->col++;

					if(con->col == con->columns)
					{
						ASCII_textout(con);
						ASCII_cursordown(con, 1);
						con->col = 1;
					}
				}
			}
		}
	}

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

	ASCII_cursorflip(con);
}


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


STATIC BOOL HandleMacroKeys(struct ASCIIConsole *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 ASCIIConsole *con = (struct ASCIIConsole *)io->xem_console;
	struct InputEvent ie;
	ULONG length=0;
	UWORD code, qual;

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

	*retstr = '\0';

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

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

	retstr[length] = '\0';			/* Null terminate the value */

	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 - Command? */
			{
				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);
}




STATIC VOID ASCII_cursordown(struct ASCIIConsole *con, UWORD i)
{
	while(i--)
	{
		if(con->row == con->lines)
			ScrollRaster(con->rp,0,+8,0,0,con->width - 1,(con->lines << 3) - 1);
		else
		{
			if(con->row < con->lines)
				con->row++;
		}
	}
}


STATIC VOID ASCII_cursorup(struct ASCIIConsole *con, UWORD i)
{
	while(i--)
	{
		if(con->row == 1)
			ScrollRaster(con->rp,0,-8,0,0,con->width - 1,(con->lines << 3) - 1);
		else
		{
			if(con->row > 1)
				con->row--;
		}
	}
}


STATIC VOID ASCII_cursorflip(struct ASCIIConsole *con)
{
	if(con->col > con->columns)
		con->col = con->columns;

	SetDrMd(con->rp, COMPLEMENT);
	RectFill(con->rp, (con->col - 1) << 3, (con->row - 1) << 3, (con->col << 3) - 1, (con->row << 3) - 1);
	SetDrMd(con->rp, JAM2);
}


STATIC VOID ASCII_textout(struct ASCIIConsole *con)
{
	Move(con->rp, (con->ordcol - 1) << 3, ((con->row - 1) << 3) + BOTLINE);
	Text(con->rp, con->ordtext, con->ordc);

	con->ordc = 0;
}


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

	con->row = 1;
	con->col = 1;
	if(con->ordc)
		ASCII_textout(con);
	SetRast(con->rp, BACKGROUND_PEN);

	return(TRUE);
}


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

	ASCII_reset(con);
	return(TRUE);
}


BOOL __saveds __asm XEmulatorResetTextStyles(register __a0 struct XEM_IO *io)
{
	return(FALSE);
}


BOOL __saveds __asm XEmulatorResetCharset(register __a0 struct XEM_IO *io)
{
	return(FALSE);
}


VOID ASCII_configopt(struct xem_option *opt, UBYTE *title, UBYTE *buff, UWORD length, BOOL type, BOOL value)
{
	opt->xemo_description= title;
	opt->xemo_value		= buff;
	opt->xemo_type			= type;
	opt->xemo_length		= length;

	if(type == XEMO_BOOLEAN)
	{
		if(value != FALSE)
			strcpy(opt->xemo_value, "on");
		else
			strcpy(opt->xemo_value, "off");

		opt->xemo_length = 4;
	}
}

STATIC BOOL ASCII_options(struct ASCIIConsole *con);

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

	return(ASCII_options(con));
}
STATIC BOOL ASCII_options(struct ASCIIConsole *con)
{
	enum	{ OPT_HEADER,OPT_ASCII,OPT_FRONTPEN,
			  NUMOPTS };

	struct xem_option *opti[NUMOPTS];
	ULONG changed;
	UBYTE penbuff[5], asciibuff[5];

	struct xem_option opt0;
	struct xem_option opt1;
	struct xem_option opt2;

	opti[OPT_HEADER]	= &opt0;
	opti[OPT_ASCII]	= &opt1;
	opti[OPT_FRONTPEN]= &opt2;

	penbuff[0] = (con->foreground_pen / 10) + '0';
	penbuff[1] = (con->foreground_pen % 10) + '0';
	penbuff[2] = 0;

	ASCII_configopt(opti[OPT_HEADER],	"General options:", NULL,	0,		XEMO_HEADER,	FALSE);
	ASCII_configopt(opti[OPT_ASCII],		"ASCII Mode.:", asciibuff,	4, XEMO_BOOLEAN, con->ASCII);
	ASCII_configopt(opti[OPT_FRONTPEN],	"Front Pen..:", penbuff,	4, XEMO_LONG, FALSE);

	changed = con->io->xem_toptions(NUMOPTS, opti);

	if(changed & (1 << OPT_ASCII))
	{
		con->ASCII = (!stricmp(opti[OPT_ASCII]->xemo_value, "yes") || !stricmp(opti[OPT_ASCII]->xemo_value, "on"));
	}

	if(changed & (1 << OPT_FRONTPEN))
	{
		con->foreground_pen = AtoL(penbuff);
		SetAPen(con->rp, con->foreground_pen);
	}

	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 ASCIIConsole *con = (struct ASCIIConsole *)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 ASCIIConsole *con = (struct ASCIIConsole *)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);
}


STATIC VOID ASCII_internalsettings(struct ASCIIConsole *con)
{
	/* some good(?) settings */

	con->ASCII = TRUE;
	con->foreground_pen = 1;
}


UBYTE *PreferencesTemplate = "FGP=FOREGROUND_PEN/N,TTY=TTY_MODE/K";
enum { ARG_PEN=0,ARG_TTY,ARGS };

STATIC BOOL ASCII_parseoptions(struct ASCIIConsole *con, STRPTR optionsBuffer)
{
	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_PEN])
						con->foreground_pen = *((LONG *)ArgArray[ARG_PEN]);

					if(ArgArray[ARG_TTY])
						con->ASCII = (!strnicmp(ArgArray[ARG_TTY], "NO", 2));

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


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

	if(con != NULL)
	{
		switch(mode)
		{
			case XEM_PREFS_RESET:
				success = TRUE;
				ASCII_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(ASCII_parseoptions(con, buf) == FALSE)
							ASCII_internalsettings(con);
						else
							success = TRUE;
					}
					Close(fh);
				}
			}
			break;

			case XEM_PREFS_SAVE:
			{
				BPTR fh;

				if(fh = Open(filename, MODE_NEWFILE))
				{
					UBYTE buf[80], *mode;

					success = TRUE;

					mode = (con->ASCII != FALSE) ? "NO" : "YES";

					sprintf(buf, "FOREGROUND_PEN=%ld TTY_MODE=%s\n", con->foreground_pen, mode);

					fprintf(fh, buf);

					Close(fh);
				}
			}
			break;

		}
	}
	return(success);
}


/* end of source-code */
