/* $Revision Header * Header built automatically - do not edit! *************
 *
 *	(C) Copyright 1990 by Olaf 'Olsen' Barthel & MXM
 *
 *	Name .....: Console.c
 *	Created ..: Monday 21-Jan-91 20:12
 *	Revision .: 1
 *
 *	Date            Author          Comment
 *	=========       ========        ====================
 *	14-Apr-91       Olsen           Rewrote double-dead-key support.
 *	05-Feb-91       Olsen           Added double-dead-key support.
 *	21-Jan-91       Olsen           Created this file!
 *
 * $Revision Header ********************************************************/

#include "TermGlobal.h"

	/* Capture(APTR Buffer,LONG Size):
	 *
	 *	Send the buffer contents to the display/disk capture.
	 */

VOID
Capture(APTR Buffer,LONG Size)
{
	struct MenuItem *SomeItem;

		/* We are parsing a control sequence, don't buffer it! */

	if(!InSequence)
		StoreBuffer(Buffer,Size);

		/* Write to capture file. */

	if(FileCapture)
	{
		if(!FWrite(FileCapture,Buffer,Size,1))
		{
			BlockWindows();

				/* We had an error writing to the file. */

			switch(MyEasyRequest(NULL,"Error writing to capture\nfile %s!","Ignore Error|Close & Discard File|Close File",CaptureName))
			{
				case 0:	break;

				case 1:	Close(FileCapture);

					DeleteFile(CaptureName);

					if(SomeItem = FindThisItem(MEN_CAPTUREDISK))
						SomeItem -> Flags &= ~CHECKED;

					FileCapture = NULL;

					break;

				case 2:	Close(FileCapture);

					if(SomeItem = FindThisItem(MEN_CAPTUREDISK))
						SomeItem -> Flags &= ~CHECKED;

					FileCapture = NULL;

					if(!GetFileSize(CaptureName))
						DeleteFile(CaptureName);

					break;
			}

			ReleaseWindows();
		}
	}

		/* Send the buffer to the printer (ignore ANSI control
		 * sequences).
		 */

	if(PrinterCapture)
	{
		if(*((UBYTE *)Buffer) != '\033' && !InSequence)
		{
			if(!FWrite(FileCapture,Buffer,Size,1))
			{
				BlockWindows();

				if(!MyEasyRequest(NULL,"Error writing to printer!","Ignore Error|Close Printer"))
				{
					Close(PrinterCapture);

					if(SomeItem = FindThisItem(MEN_CAPTUREPRINTER))
						SomeItem -> Flags &= ~CHECKED;

					PrinterCapture = NULL;
				}

				ReleaseWindows();
			}
		}
	}
}

	/* HandleCursor(UBYTE Char):
	 *
	 *	This routine handles the somewhat strange behaviour of
	 *	an assorted set of keys in VT100 applications mode.
	 */

BYTE
HandleCursor(UBYTE Char)
{
	STATIC struct
	{
		UBYTE	 Char;
		UBYTE	*VanillaString;
		UBYTE	*ApplicationString;
	} Table[18] =
	{
		CUP,	"\033[A",	"\033OA",
		CDN,	"\033[B",	"\033OB",
		CFW,	"\033[C",	"\033OC",
		CBK,	"\033[D",	"\033OD",

		'0',	"0",		"\033Op",
		'1',	"1",		"\033Oq",
		'2',	"2",		"\033Or",
		'3',	"3",		"\033Os",
		'4',	"4",		"\033Ot",
		'5',	"5",		"\033Ou",
		'6',	"6",		"\033Ov",
		'7',	"7",		"\033Ow",
		'8',	"8",		"\033Ox",
		'9',	"9",		"\033Oy",
		'-',	"-",		"\033Om",
		'*',	"*",		"\033Ol",
		'.',	".",		"\033On",
		'\r',	"\r",		"\033OM"
	};

	BYTE i;

		/* Look for the cursor keys first. */

	for(i = 0 ; i < 4 ; i++)
	{
		if(Table[i] . Char == Char)
		{
			if(Config . CursorApp)
				SerWrite(Table[i] . ApplicationString,strlen(Table[i] . ApplicationString));
			else
				SerWrite(Table[i] . VanillaString,strlen(Table[i] . VanillaString));

			return(TRUE);
		}
	}

		/* Then take a look at the numeric pad. */

	for(i = 4 ; i < 18 ; i++)
	{
		if(Table[i] . Char == Char)
		{
			if(Config . NumApp)
				SerWrite(Table[i] . ApplicationString,strlen(Table[i] . ApplicationString));
			else
			{
				if(i == 17)
				{
					switch(Config . SendCR)
					{
						case CR_IGNORE:	break;

						case CR_ASCR:	SerWrite("\r",1);
								break;

						case CR_ASCRLF:	SerWrite("\r\n",2);
								break;
					}
				}
				else
					SerWrite(Table[i] . VanillaString,strlen(Table[i] . VanillaString));
			}

			return(TRUE);
		}
	}

	return(FALSE);
}

	/* DoBackspace():
	 *
	 *	Special function: perform backspace.
	 */

VOID
DoBackspace()
{
	Capture("\b",1);

	if(CursorX)
	{
		ClearCursor();

		CursorX--;

			/* If destructive, shift the remaining line
			 * one character to the right.
			 */

		if(Config . DestructiveBackspace)
		{
			BackupRender();

			SetBPen(RPort,0);

			RasterEraseCharacters(1);

			ScrollRaster(RPort,8,0,CursorX * 8,CursorY * 8,Window -> Width - 1,(CursorY + 1) * 8 - 1);

			BackupRender();
		}

		SetCursor();
	}
}

	/* Raise(UWORD Colour):
	 *
	 *	Make an RGB value brighter.
	 */

STATIC UWORD
Raise(UWORD Colour)
{
	SHORT r,g,b;

	r = ((Colour >> 8) & 0xF) + 4;
	g = ((Colour >> 4) & 0xF) + 4;
	b = ((Colour     ) & 0xF) + 4;

	if(r > 15)
		r = 15;

	if(g > 15)
		g = 15;

	if(b > 15)
		b = 15;

	return(r << 8 | g << 4 | b);
}

	/* VisualBeep():
	 *
	 *	`Beep' the screen visually.
	 */

STATIC BYTE
VisualBeep()
{
	struct UCopList	*UserCopperList;

		/* Create a user copper list. */

	if(UserCopperList = (struct UCopList *)AllocMem(sizeof(struct UCopList),MEMF_PUBLIC|MEMF_CLEAR))
	{
		SHORT i;

			/* Initialize for 35 commands. */

		CINIT(UserCopperList,2 + 16 + 16 + 1);

			/* Wait until first line of window. */

		CWAIT(UserCopperList,Window -> TopEdge,0);

			/* Set the light colours. */

		for(i = 0 ; i < 16 ; i++)
			CMOVE(UserCopperList,custom . color[i],Raise(GetRGB4(Screen -> ViewPort . ColorMap,i)));

			/* Wait until bottom of window. */

		CWAIT(UserCopperList,Window -> TopEdge + Window -> Height - 1,0);

			/* Set the standard colours. */

		for(i = 0 ; i < 16 ; i++)
			CMOVE(UserCopperList,custom . color[i],GetRGB4(Screen -> ViewPort . ColorMap,i));

			/* Finish list. */

		CEND(UserCopperList);

			/* Lock the screen. */

		LockLayers(&Screen -> LayerInfo);

			/* Install user copper list... */

		Screen -> ViewPort . UCopIns = UserCopperList;

			/* ...and display it. */

		RethinkDisplay();

		return(TRUE);
	}
}

	/* DoSomeBeep():
	 *
	 *	Special function: beep the terminal.
	 */

VOID
DoSomeBeep()
{
		/* Handle the visual part. */

	if(Config . VisibleBell)
	{
		if(VisualBeep())
		{
			if(Config . AudibleBell)
				Beep();

			Delay(25);

				/* Remove the copper list. */

			FreeVPortCopLists(&Screen -> ViewPort);

				/* Really remove it. */

			RemakeDisplay();

				/* Unlock screen layers. */

			UnlockLayers(&Screen -> LayerInfo);
		}
		else
		{
			if(Config . AudibleBell)
				Beep();
		}
	}
	else
	{
			/* Let it beep. */

		if(Config . AudibleBell)
			Beep();

		Delay(25);
	}

	if(!Config . CaptureFilter)
		Capture("\a",1);
}

	/* DoLF():
	 *
	 *	Special function: perform line feed.
	 */

VOID
DoLF()
{
	ClearCursor();

	if(UseRegion)
	{
		if(CursorY == Bottom)
			ScrollRegion(8);
		else
		{
			CursorY++;

			SetCursor();
		}
	}
	else
	{
		if(CursorY == LastLine)
			ScrollRegion(8);
		else
		{
			CursorY++;

			SetCursor();
		}
	}
}

	/* DoIgnore():
	 *
	 *	Special function: don't do anything.
	 */

VOID
DoIgnore()
{
}

	/* DoCR_LF():
	 *
	 *	Special function: perform carriage return and line feed.
	 */

VOID
DoCR_LF()
{
	ClearCursor();

	CursorX = 0;

	if(UseRegion)
	{
		if(CursorY == Bottom)
			ScrollRegion(8);
		else
		{
			CursorY++;

			SetCursor();
		}
	}
	else
	{
		if(CursorY == LastLine)
			ScrollRegion(8);
		else
		{
			CursorY++;

			SetCursor();
		}
	}

	if(Config . CaptureFilter)
		Capture("\n",1);
	else
		Capture("\r\n",2);
}

	/* DoFF():
	 *
	 *	Special function: perform form feed.
	 */

VOID
DoFF()
{
	ClearCursor();

	if(Config . NewLine)
	{
		CursorX = 0;

		if(UseRegion)
		{
			if(CursorY == Bottom)
				ScrollRegion(8);
			else
			{
				CursorY++;

				SetCursor();
			}
		}
		else
		{
			if(CursorY == LastLine)
				ScrollRegion(8);
			else
			{
				CursorY++;

				SetCursor();
			}
		}

		if(Config . CaptureFilter)
			Capture("\n",1);
		else
			Capture("\r\n",2);
	}
	else
	{
		EraseScreen("2");

		CursorX = CursorY = 0;

		SetCursor();

		if(Config . CaptureFilter)
			Capture("\n",1);
		else
			Capture("\f",1);
	}
}

	/* DoLF_FF_VT():
	 *
	 *	Special function: handle line feed, form feed and vertical
	 *	tab.
	 */

VOID
DoLF_FF_VT()
{
	if(Config . NewLine)
		DoCR_LF();
	else
		DoLF();
}

	/* DoCR():
	 *
	 *	Special function: handle carriage return.
	 */

VOID
DoCR()
{
	if(Config . NewLine)
		DoCR_LF();
	else
	{
		ClearCursor();

		CursorX = 0;

		SetCursor();

		if(Config . CaptureFilter)
			Capture("\n",1);
		else
			Capture("\r",1);
	}
}

	/* DoTab():
	 *
	 *	Special function: handle tab, move cursor to next
	 *	tab stop.
	 */

VOID
DoTab()
{
	ClearCursor();

	if(CursorX < LastColumn)
	{
		SHORT i;

		for(i = CursorX + 1 ; i <= LastColumn ; i++)
		{
			if(TabStops[i])
			{
				CursorX = i;

				SetCursor();

				Capture("\t",1);

				return;
			}
		}
	}

	Capture("\t",1);

	CursorX = 0;

	if(UseRegion)
	{
		if(CursorY == Bottom)
			ScrollRegion(8);
		else
		{
			CursorY++;

			SetCursor();
		}
	}
	else
	{
		if(CursorY == LastLine)
			ScrollRegion(8);
		else
		{
			CursorY++;

			SetCursor();
		}
	}
}

	/* SpillTheBeans(UBYTE *Buffer,LONG Size):
	 *
	 *	Output a buffer of given size to the terminal
	 *	window.
	 */

VOID
SpillTheBeans(UBYTE *Buffer,LONG Size)
{
		/* The slow method: insert characters at of the
		 * initial cursor position and shift the remaining
		 * screen characters to the right.
		 */

	if(Config . InsertChar)
	{
			/* Do we still have a character in the
			 * magnificient buffer?
			 */

		while(Size--)
		{
				/* Shift the character(s) over. */

			if(CursorX != LastColumn)
			{
				RasterShiftChar();

				ShiftChar();
			}

				/* The cursor hasn't hit the right
				 * margin, has it? So let's print a
				 * single character.
				 */

			ClearCursor();

			if(CursorX++ <= LastColumn)
			{
					/* Real-time font scaling
					 * enabled?
					 */

				if(Config . FontScale)
				{
					if(CursorX != LastColumn)
					{
						RasterShiftChar();

						ShiftChar();
					}

					CursorX--;

					PrintScaled(*Buffer++);
				}
				else
				{
					RasterPutString(Buffer,1);

					Text(RPort,Buffer,1);

					Buffer++;
				}
			}
			else
			{
					/* Cursor is positioned at
					 * the right hand side of the
					 * display. If auto-wrap is
					 * enabled, perform some
					 * kind of CR/LF, else leave
					 * the cursor where it is.
					 */

				if(Config . AutoWrap)
				{
					if(UseRegion)
					{
						if(CursorY == Bottom)
							ScrollRegion(8);
						else
							CursorY++;

						Capture("\n",1);
					}
					else
					{
						if(CursorY == LastLine)
							ScrollRegion(8);
						else
							CursorY++;

						Capture("\n",1);
					}

					CursorX = 0;
				}
				else
					CursorX = LastColumn;
			}

			SetCursor();
		}
	}
	else
	{
			/* Do we still have a character in the
			 * magnificient buffer?
			 */

		while(Size > 0)
		{
				/* Cursor is positioned at
				 * the right hand side of the
				 * display. If auto-wrap is
				 * enabled, perform some
				 * kind of CR/LF, else leave
				 * the cursor where it is and
				 * quit the show.
				 */

			ClearCursor();

			if(CursorX > LastColumn)
			{
				if(Config . AutoWrap)
				{
					CursorX = 0;

					if(UseRegion)
					{
						if(CursorY == Bottom)
							ScrollRegion(8);
						else
						{
							CursorY++;

							SetCursor();
						}

						Capture("\n",1);
					}
					else
					{
						if(CursorY == LastLine)
							ScrollRegion(8);
						else
						{
							CursorY++;

							SetCursor();
						}

						Capture("\n",1);
					}
				}
				else
				{
					CursorX = LastColumn;

					SetCursor();

					return;
				}
			}
			else
			{
					/* Real-time font scaling
					 * enabled?
					 */

				if(Config . FontScale)
				{
					while(CursorX <= LastColumn && Size)
					{
						PrintScaled(*Buffer++);

						Size--;
					}

					SetCursor();
				}
				else
				{
					SHORT Offset;

						/* We won't have to take
						 * care of characters to shift.
						 * We'll collect as many
						 * characters in the buffer as will
						 * fit into the current line
						 * and print them.
						 */

					if((Offset = LastColumn + 1 - CursorX) > Size)
						Offset = Size;

					RasterPutString(Buffer,Offset);

					Text(RPort,Buffer,Offset);

					Size -= Offset;

					CursorX += Offset;

					SetCursor();
				}
			}
		}
	}
}

	/* ConWrite(APTR Buffer,LONG Size):
	 *
	 *	Write a string to the console window.
	 */

VOID
ConWrite(APTR Buffer,LONG Size)
{
	STATIC UBYTE	 StringBuffer[200],BufSize = 0;
	UBYTE		*Char = Buffer;

		/* Run down the buffer. */

	while(Size--)
	{
			/* Are we parsing a control sequence? */

		if(InSequence)
		{
			if(*Char == ESC)
			{
				DoCancel();
				InSequence = TRUE;

				if(!Config . CaptureFilter)
					Capture("\033",1);
			}
			else
			{
				if(!Config . CaptureFilter)
					Capture(Char,1);

					/* See if we are already done. */

				InSequence = ParseCode(*Char++);
			}
		}
		else
		{
				/* This looks like a control sequence
				 * introducing character.
				 */

			if(*Char == ESC || *Char == CSI)
			{
				if(BufSize)
				{
					Capture(&StringBuffer[0],BufSize);

					SpillTheBeans(&StringBuffer[0],BufSize);

					BufSize = 0;
				}

					/* So we're in TTY mode,
					 * escape the `escape' character
					 * and continue.
					 */

				if(Config . Emulation == EMULATION_TTY)
				{
					if(*Char == ESC)
					{
						StringBuffer[BufSize++] = '^';
						StringBuffer[BufSize++] = '[';

						if(!Config . CaptureFilter)
							Capture("\033",1);
					}
					else
					{
							/* Fake a CSI. */

						StringBuffer[BufSize++] = '^';
						StringBuffer[BufSize++] = '[';

						if(!Config . CaptureFilter)
							Capture("\033[",2);
					}
				}
				else
				{
					if(*Char == ESC)
					{
						InSequence = TRUE;
	
						if(!Config . CaptureFilter)
							Capture("\033",1);
					}
					else
						CSIFake();
				}

				Char++;
			}
			else
			{
					/* Stuff the character into
					 * the buffer.
					 */

				StringBuffer[BufSize++] = *Char++;

					/* If buffer is full, spill it. */

				if(BufSize == 200)
				{
					Capture(&StringBuffer[0],BufSize);

					SpillTheBeans(&StringBuffer[0],BufSize);

					BufSize = 0;
				}
			}
		}
	}

		/* Any characters in the buffer we didn't process yet? */

	if(!InSequence && BufSize)
	{
		Capture(&StringBuffer[0],BufSize);

		SpillTheBeans(&StringBuffer[0],BufSize);

		BufSize = 0;
	}
}

	/* ConProcess(UBYTE *String,LONG Size):
	 *
	 *	Process the contents of a string to be sent to the
	 *	console window.
	 */

VOID
ConProcess(UBYTE *String,LONG Size)
{
	LONG i,j;

		/* In quiet mode no characters are echoed to the
		 * console window, they are just passed through
		 * the data flow filter. This mode is enabled
		 * by the dial panel.
		 */

	if(Quiet)
	{
		for(i = 0 ; i < Size ; i++)
			FlowFilter(String[i]);
	}
	else
	{
			/* If the current font is Topaz, some bytes
			 * will need to be filtered since they are not
			 * defined as valid ASCII characters.
			 */

		if(Config . Font == FONT_TOPAZ)
		{
			for(i = 0, j = 0 ; i < Size ; i++)
			{
				FlowFilter(String[i]);

				if(IsPrintable(String[i]))
				{
						/* This character is
						 * associated with a
						 * special function
						 * (bell, xon, xoff, etc.).
						 */

					if(SpecialMap[String[i]] != -1)
					{
						if(j)
						{
							ConWrite(SharedBuffer,j);

							j = 0;
						}

						SpecialKeys[SpecialMap[String[i]]] . Routine();
					}
					else
					{
							/* Put the character
							 * into the buffer
							 * and empty it if
							 * necessary.
							 */

						SharedBuffer[j] = String[i];

						if(j++ == 512)
						{
							ConWrite(SharedBuffer,j);

							j = 0;
						}
					}
				}
			}
		}
		else
		{
				/* In IBM font mode, any character gives
				 * a sensible result.
				 */

			for(i = 0, j = 0 ; i < Size ; i++)
			{
				FlowFilter(String[i]);

				if(SpecialMap[String[i]] != -1)
				{
					if(j)
					{
						ConWrite(SharedBuffer,j);

						j = 0;
					}

					SpecialKeys[SpecialMap[String[i]]] . Routine();
				}
				else
				{
					SharedBuffer[j] = String[i];

					if(j++ == 512)
					{
						ConWrite(SharedBuffer,j);

						j = 0;
					}
				}
			}
		}

		if(j)
			ConWrite(SharedBuffer,j);
	}
}

	/* ConWrites(UBYTE *String,...):
	 *
	 *	Output a string to the console.
	 */

VOID __stdargs
ConWrites(UBYTE *String,...)
{
	va_list	VarArgs;

	va_start(VarArgs,String);
	VSPrintf(SharedBuffer,String,VarArgs);
	va_end(VarArgs);

	ConProcess(SharedBuffer,strlen(SharedBuffer));
}

	/* KeyConvert(struct IntuiMessage *Massage,UBYTE *Buffer):
	 *
	 *	Convert a raw key information according to the
	 *	current keymap settings.
	 */

UBYTE
KeyConvert(struct IntuiMessage *Massage,UBYTE *Buffer)
{
	if(Buffer)
		Buffer[0] = 0;

	if(Massage -> Class == IDCMP_RAWKEY)
	{
			/* These are the sequences mapped to special
			 * control keys (cursor keys, function keys,
			 * the help key).
			 */

		STATIC struct
		{
			UBYTE *RawCode;
			UBYTE Result;
		} ConversionTable[16] =
		{
			(UBYTE *)"A",	CUP,
			(UBYTE *)"B",	CDN,
			(UBYTE *)"C",	CFW,
			(UBYTE *)"D",	CBK,

			(UBYTE *)"?~",	HLP,

			(UBYTE *)"0~",	FN1,
			(UBYTE *)"1~",	FN2,
			(UBYTE *)"2~",	FN3,
			(UBYTE *)"3~",	FN4,
			(UBYTE *)"4~",	FN5,
			(UBYTE *)"5~",	FN6,
			(UBYTE *)"6~",	FN7,
			(UBYTE *)"7~",	FN8,
			(UBYTE *)"8~",	FN9,
			(UBYTE *)"9~",	F10
		};

			/* Key was pressed, not released. */

		if(!(Massage -> Code & IECODE_UP_PREFIX))
		{
			UBYTE	ConvertBuffer[257],i;
			ULONG	Qualifier = Massage -> Qualifier;

				/* If it's a function key clear the qualifier. */

			if(Massage -> Code >= 80 && Massage -> Code <= 89)
				Qualifier = 0;

				/* Clear the buffer. */

			memset(&ConvertBuffer[0],0,257);

				/* Convert the key. */

			FakeInputEvent -> ie_Code		= Massage -> Code;
			FakeInputEvent -> ie_Qualifier		= Qualifier;
			FakeInputEvent -> ie_position . ie_addr	= *((APTR *)Massage -> IAddress);

			if(RawKeyConvert(FakeInputEvent,(UBYTE *)ConvertBuffer,257,NULL) > 0)
			{
				if(ConvertBuffer[0])
				{
					if(Buffer)
						strcpy(Buffer,ConvertBuffer);

						/* Translated sequence starts
						 * with a CSI, let's have a look
						 * at the associated control
						 * key.
						 */

					if(ConvertBuffer[0] == CSI)
					{
						for(i = 0 ; i < 16 ; i++)
						{
							if(!StrCmp(&ConvertBuffer[1],ConversionTable[i] . RawCode))
							{
								ConvertBuffer[0] = ConversionTable[i] . Result;

								if(Buffer)
								{
									Buffer[0] = ConversionTable[i] . Result;
									Buffer[1] = 0;
								}

								break;
							}
						}
					}

					return(ConvertBuffer[0]);
				}
			}
		}
	}

	return(0);
}
