/*
**	$Id: termReview.c,v 1.7 92/08/15 20:14:56 olsen Sta Locker: olsen $
**	$Revision: 1.7 $
**	$Date: 92/08/15 20:14:56 $
**
**	Support routines for the review buffer (not the log book!)
**
**	Copyright © 1990-1992 by Olaf `Olsen' Barthel & MXM
**		All Rights Reserved
*/

#include "termGlobal.h"

	/* Scroller gadget flags. */

#define PROP_FLAGS	(AUTOKNOB | FREEVERT | PROPBORDERLESS | PROPNEWLOOK)

	/* Number of context lines to keep between pages. */

#define CONTEXT_LINES	1

	/* Local routines. */

STATIC VOID		ReviewUpdatePot(VOID);
STATIC VOID __stdargs	ReviewWrites(STRPTR String,...);
STATIC VOID __regargs	PrintReviewLine(STRPTR Buffer,LONG Line);
STATIC VOID __regargs	RefreshReview(LONG Top);
STATIC BYTE		ReviewQuery(VOID);
STATIC UBYTE __regargs	GetReviewChar(BYTE WaitForIt);

	/* Local variables. */

STATIC struct IBox	 ReviewBox;
STATIC struct IOStdReq	*ReviewWriteRequest,
			*ReviewReadRequest;

STATIC struct MsgPort	*ReviewWritePort;

STATIC LONG		 ReviewWidth = -1,ReviewHeight,
			 ReviewX,ReviewY;
STATIC UBYTE		 ReviewChar;
STATIC LONG		 ReviewColumns,ReviewLines,ReviewTop,ReviewGlobalLines;
STATIC LONG		 ReviewBody = -1,ReviewPot = -1;

STATIC struct Image	 ReviewPropImage;

STATIC struct PropInfo ReviewPropInfo =
{
	PROP_FLAGS,
	0,MAXPOT,
	0,MAXBODY,
	0,0,
	0,0,
	0,0
};

STATIC struct Gadget ReviewPropGadget =
{
	NULL,
	0,0,
	0,0,
	GFLG_GADGHCOMP | GFLG_RELRIGHT | GFLG_RELHEIGHT,
	GACT_IMMEDIATE | GACT_FOLLOWMOUSE,
	GTYP_PROPGADGET,
	&ReviewPropImage,
	NULL,
	NULL,
	0,
	(APTR)&ReviewPropInfo,
	0,
	NULL
};

	/* ReviewUpdatePot():
	 *
	 *	Update size and position of the scroller gadget.
	 */

STATIC VOID
ReviewUpdatePot()
{
	if(ReviewGlobalLines)
	{
		LONG	Body,
			Pot,
			Hidden;

		Hidden = MAX(ReviewGlobalLines - ReviewLines,0);

		if(ReviewTop > Hidden)
			ReviewTop = 0;

		if(Hidden > 0)
		{
			Body	= (UWORD)(((ULONG)(ReviewLines - CONTEXT_LINES) * MAXBODY) / (ReviewGlobalLines - CONTEXT_LINES));
			Pot	= (UWORD)(((ULONG)ReviewTop * MAXPOT) / Hidden);
		}
		else
		{
			Body	= MAXBODY;
			Pot	= 0;
		}

		if(Body != ReviewBody || Pot != ReviewPot)
		{
			NewModifyProp(&ReviewPropGadget,ReviewWindow,NULL,PROP_FLAGS,0,Pot,0,Body,1);

			ReviewBody	= Body;
			ReviewPot	= Pot;
		}
	}
}

	/* ReviewUp(LONG Count):
	 *
	 *	Move the contents of the review buffer up.
	 */

STATIC VOID __regargs
ReviewUp(LONG Count)
{
	if(Count == 1)
	{
		if(ReviewTop)
		{
			ReviewTop--;

			ReviewWrites("\033[T");

			ObtainSemaphore(BufferSemaphore);

			PrintReviewLine(BufferLines[ReviewTop],1);

			ReviewUpdatePot();

			ReleaseSemaphore(BufferSemaphore);
		}
	}
	else
	{
		LONG NewTop = ReviewTop;

		if(NewTop >= Count)
			NewTop -= Count;
		else
			NewTop = 0;

		if(NewTop != ReviewTop)
		{
			ReviewTop = NewTop;

			RefreshReview(ReviewTop);

			ReviewUpdatePot();
		}
	}
}

	/* ReviewDown(LONG Count):
	 *
	 *	Move the contents of the review buffer down.
	 */

STATIC VOID __regargs
ReviewDown(LONG Count)
{
	if(Count == 1)
	{
		if(ReviewTop + ReviewLines < Lines)
		{
			LONG Last;

			ReviewTop++;

			ReviewWrites("\033[S");

			ObtainSemaphore(BufferSemaphore);

			if((Last = ReviewTop + ReviewLines) < Lines)
				PrintReviewLine(BufferLines[Last],ReviewLines + 1);

			ReviewUpdatePot();

			ReleaseSemaphore(BufferSemaphore);
		}
	}
	else
	{
		LONG NewTop = ReviewTop;

		if((NewTop + Count + ReviewLines) > Lines)
		{
			if((NewTop = Lines - ReviewLines) < 0)
				NewTop = 0;
		}
		else
			NewTop += Count;

		if(NewTop != ReviewTop)
		{
			ReviewTop = NewTop;

			RefreshReview(ReviewTop);

			ReviewUpdatePot();
		}
	}
}

	/* ReviewWrites(STRPTR String,...):
	 *
	 *	Write a string into the review buffer window.
	 */

STATIC VOID __stdargs
ReviewWrites(STRPTR String,...)
{
	va_list	VarArgs;

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

	ReviewWriteRequest -> io_Command	= CMD_WRITE;
	ReviewWriteRequest -> io_Data		= SharedBuffer;
	ReviewWriteRequest -> io_Length		= -1;

	DoIO(ReviewWriteRequest);
}

	/* PrintReviewLine(STRPTR Buffer,LONG Line):
	 *
	 *	Write the contents of a buffer line into the review buffer window.
	 */

STATIC VOID __regargs
PrintReviewLine(STRPTR Buffer,LONG Line)
{
	WORD Length = ((ULONG *)Buffer)[-1];

	if(Length > ReviewColumns)
		Length = ReviewColumns;

	ReviewWrites("\033[%ldH",Line);

	ReviewWriteRequest -> io_Command	= CMD_WRITE;
	ReviewWriteRequest -> io_Data		= Buffer;
	ReviewWriteRequest -> io_Length		= Length;

	DoIO(ReviewWriteRequest);

	ReviewWrites("\033[0K");
}

	/* RefreshReview(LONG Top):
	 *
	 *	Refresh the contents of the review buffer window.
	 */

STATIC VOID __regargs
RefreshReview(LONG Top)
{
	LONG i,Last,Line = 0;

	ReviewGlobalLines = Lines;

	ObtainSemaphore(BufferSemaphore);

	if((Last = Top + ReviewLines + 1) >= Lines)
		Last = Lines;

	for(i = Top ; i < Last ; i++)
		PrintReviewLine(BufferLines[i],++Line);

	if(Line <= ReviewLines)
		ReviewWrites("\033[0J");

	ReleaseSemaphore(BufferSemaphore);
}

	/* ReviewQuery():
	 *
	 *	Update the current review buffer window dimensions.
	 */

STATIC BYTE
ReviewQuery()
{
	struct ConUnit	*Unit = (struct ConUnit *)ReviewWriteRequest -> io_Unit;
	BYTE		 Refresh = FALSE;

	if(ReviewColumns != Unit -> cu_XMax)
	{
		Refresh = TRUE;

		ReviewColumns = Unit -> cu_XMax;
	}

	if(ReviewLines != Unit -> cu_YMax)
	{
		LONG Delta = ABS(Unit -> cu_YMax - ReviewLines);

		Refresh = TRUE;

		if(ReviewLines)
		{
			if(Unit -> cu_YMax > ReviewLines)
			{
				if((ReviewTop = ReviewTop - Delta) < 0)
					ReviewTop = 0;
			}
			else
				ReviewTop += Delta;
		}

		ReviewLines = Unit -> cu_YMax;
	}

	if(Refresh)
		ReviewUpdatePot();

	return(Refresh);
}

	/* GetReviewChar(BYTE WaitForIt):
	 *
	 *	Get the next character present at the console read port.
	 */

STATIC UBYTE __regargs
GetReviewChar(BYTE WaitForIt)
{
	UBYTE Char;

	if(!WaitForIt)
	{
		if(!CheckIO(ReviewReadRequest))
			return(0);
	}

	WaitIO(ReviewReadRequest);

	Char = ReviewChar;

	ReviewReadRequest -> io_Command	= CMD_READ;
	ReviewReadRequest -> io_Data	= &ReviewChar;
	ReviewReadRequest -> io_Length	= 1;

	SendIO(ReviewReadRequest);

	return(Char);
}

	/* DeleteReview():
	 *
	 *	Delete the review buffer.
	 */

VOID
DeleteReview()
{
	struct MenuItem *Item;

	if(Item = FindThisItem(MEN_REVIEW_WINDOW))
		Item -> Flags &= ~CHECKED;

	if(ReviewWriteRequest)
	{
		if(ReviewWriteRequest -> io_Device)
		{
			ReviewWriteRequest -> io_Command = CMD_CLEAR;

			DoIO(ReviewWriteRequest);

			CloseDevice(ReviewWriteRequest);
		}

		DeleteIORequest(ReviewWriteRequest);

		ReviewWriteRequest = NULL;
	}

	if(ReviewReadRequest)
	{
		if(ReviewReadRequest -> io_Device)
		{
			if(!CheckIO(ReviewReadRequest))
				AbortIO(ReviewReadRequest);

			WaitIO(ReviewReadRequest);
		}

		FreeVec(ReviewReadRequest);

		ReviewReadRequest = NULL;
	}

	ReviewPot = ReviewBody = -1;

	if(ReviewWindow)
	{
		ReviewWidth	= ReviewWindow -> Width;
		ReviewHeight	= ReviewWindow -> Height;
		ReviewX		= ReviewWindow -> LeftEdge;
		ReviewY		= ReviewWindow -> TopEdge;

		CloseWindow(ReviewWindow);

		ReviewWindow = NULL;
	}

	if(ReviewWritePort)
	{
		DeleteMsgPort(ReviewWritePort);

		ReviewWritePort = NULL;
	}

	if(ReviewPort)
	{
		DeleteMsgPort(ReviewPort);

		ReviewPort = NULL;
	}
}

	/* CreateReview():
	 *
	 *	Create the review buffer.
	 */

BYTE
CreateReview()
{
	if(ReviewWidth == -1)
	{
		ReviewWidth		= Screen -> Width;
		ReviewHeight		= Screen -> WBorTop + Screen -> WBorBottom + 1 + Screen -> Font -> ta_YSize + 5 * Screen -> Font -> ta_YSize;
		ReviewX			= 0;
		ReviewY			= Screen -> BarHeight + 1;

		ReviewBox . Left	= 0;
		ReviewBox . Top		= Screen -> BarHeight + 1;
		ReviewBox . Width	= Screen -> Width;
		ReviewBox . Height	= Screen -> Height - (Screen -> BarHeight + 1);
	}

	ReviewPropGadget . TopEdge	= Screen -> WBorTop + Screen -> Font -> ta_YSize + 2;
	ReviewPropGadget . LeftEdge	= -15;
	ReviewPropGadget . Width	= 14;
	ReviewPropGadget . Height	= -(ReviewPropGadget . TopEdge + Screen -> WBorBottom + 9);

	if(ReviewWindow = OpenWindowTags(NULL,
		WA_Left,		ReviewX,
		WA_Top,			ReviewY,
		WA_Width,		ReviewWidth,
		WA_Height,		ReviewHeight,
		WA_MinWidth,		Screen -> WBorLeft + Screen -> WBorRight + 15 * 8,
		WA_MinHeight,		Screen -> WBorTop + Screen -> WBorBottom + 1 + 2 * Screen -> Font -> ta_YSize,
		WA_MaxWidth,		Screen -> Width,
		WA_MaxHeight,		Screen -> Height,
		WA_DragBar,		TRUE,
		WA_CloseGadget,		TRUE,
		WA_DepthGadget,		TRUE,
		WA_SizeGadget,		TRUE,
		WA_IDCMP,		IDCMP_GADGETDOWN | IDCMP_MOUSEMOVE,
		WA_Title,		LocaleString(MSG_TERMREVIEW_REVIEWBUFFER_TXT),
		WA_CustomScreen,	Screen,
		WA_SimpleRefresh,	TRUE,
		WA_RMBTrap,		TRUE,
		WA_Activate,		TRUE,
		WA_Zoom,		&ReviewBox,
	TAG_DONE))
	{
		AddGadget(ReviewWindow,&ReviewPropGadget,-1);
		RefreshGadgets(&ReviewPropGadget,ReviewWindow,NULL);

		SetFont(ReviewWindow -> RPort,CurrentFont);

		if(ReviewWritePort = CreateMsgPort())
		{
			if(ReviewPort = CreateMsgPort())
			{
				if(ReviewWriteRequest = CreateIORequest(ReviewPort,sizeof(struct IOStdReq)))
				{
					if(ReviewReadRequest = (struct IOStdReq *)AllocVec(sizeof(struct IOStdReq),MEMF_PUBLIC | MEMF_CLEAR))
					{
						ReviewWriteRequest -> io_Data = ReviewWindow;

						if(!OpenDevice("console.device",CONU_SNIPMAP,ReviewWriteRequest,CONFLAG_NODRAW_ON_NEWSIZE))
						{
							struct MenuItem	*Item;
							BYTE		 Pen;

							if(Item = FindThisItem(MEN_REVIEW_WINDOW))
								Item -> Flags |= CHECKED;

							switch(Config . ColourMode)
							{
								case COLOUR_EIGHT:
								case COLOUR_SIXTEEN:	Pen = 7;
											break;

								default:		Pen = 1;
											break;
							}

							CopyMem(ReviewWriteRequest,ReviewReadRequest,sizeof(struct IOStdReq));

							ReviewReadRequest -> io_Message . mn_ReplyPort = ReviewPort;

							ReviewWrites("\033[0 p\033[11;12{\033[3%ldm",Pen);

							ReviewQuery();

							ObtainSemaphore(BufferSemaphore);

							if((ReviewTop = Lines - ReviewLines) < 0)
								ReviewTop = 0;

							ReleaseSemaphore(BufferSemaphore);

							RefreshReview(ReviewTop);

							ReviewUpdatePot();

							ReviewReadRequest -> io_Command	= CMD_READ;
							ReviewReadRequest -> io_Data	= &ReviewChar;
							ReviewReadRequest -> io_Length	= 1;

							SendIO(ReviewReadRequest);

							return(TRUE);
						}
					}
				}
			}
		}
	}

	DeleteReview();

	return(FALSE);
}

	/* UpdateReview():
	 *
	 *	Update the contents of the review buffer window.
	 */

VOID
UpdateReview(BYTE Force)
{
	if(ReviewPort)
	{
		if(Force || (Lines >= ReviewGlobalLines && ReviewGlobalLines <= ReviewLines))
			RefreshReview(ReviewTop);

		ReviewGlobalLines = Lines;

		ReviewUpdatePot();
	}
}

	/* HandleReview():
	 *
	 *	Process console and user input.
	 */

BYTE
HandleReview()
{
	BYTE Result = FALSE;

	if(ReviewWindow)
	{
		struct IntuiMessage *Message;

		if(Message = (struct IntuiMessage *)GetMsg(ReviewWindow -> UserPort))
		{
			ReplyMsg((struct Message *)Message);

			if(ReviewGlobalLines > ReviewLines)
			{
				UWORD Hidden;

				Hidden		= MAX(ReviewGlobalLines - ReviewLines,0);
				ReviewTop	= (((ULONG)Hidden * ReviewPropInfo . VertPot) + (MAXPOT / 2)) >> 16;

				RefreshReview(ReviewTop);
			}

			Result = TRUE;
		}
	}

	if(ReviewPort)
	{
		UBYTE Char;

		if(Char = GetReviewChar(FALSE))
		{
			if(Char == CSI)
			{
				WORD Count = 0;

				Char = GetReviewChar(TRUE);

				switch(Char)
				{
					case 'A':	ReviewUp(1);
							break;

					case 'B':	ReviewDown(1);
							break;

					case 'T':	ReviewUp(ReviewLines);
							break;

					case 'S':	ReviewDown(ReviewLines);
							break;

					default:	SharedBuffer[Count++] = Char;

							while(Char = GetReviewChar(FALSE))
							{
								SharedBuffer[Count++] = Char;

								if(Char == 'v' || Char == '|')
									break;
							}

							SharedBuffer[Count] = 0;

							if(!strcmp(SharedBuffer,"0 v"))
							{
								if(!ClipInput)
								{
									if(!OpenClip())
										ClipInput = ClipXerox = TRUE;
									else
										ClipInput = ClipXerox = FALSE;
								}
							}
							else
							{
								if(!strncmp(SharedBuffer,"11",2))
								{
									DeleteReview();

									return(Result);
								}

								if(!strncmp(SharedBuffer,"12",2))
								{
									if(ReviewQuery())
										RefreshReview(ReviewTop);
								}
							}

							break;
				}
			}
			else
			{
				if(Config . StripBit8)
					Char &= 0x7F;

				if(Status == STATUS_HOLDING)
				{
					if(Char == XOF)
					{
						SerWrite(&Char,1);

						Status = STATUS_READY;
					}
					else
						DoBeep();
				}
				else
				{
						/* Convert chars
						 * as approriate.
						 */

					if(Char == '\n')
					{
						switch(Config . SendLF)
						{
							case LF_IGNORE:	break;

							case LF_ASLF:	goto SendIt;

							case LF_ASLFCR:	SerWrite("\n\r",2);
									break;
						}

						return(TRUE);
					}

					if(Char == '\r')
					{
						switch(Config . SendCR)
						{
							case CR_IGNORE:	break;

							case CR_ASCR:	goto SendIt;

							case CR_ASCRLF:	SerWrite("\r\n",2);
									break;
						}

						return(TRUE);
					}

						/* Stop in/output. */

					if(Char == XON)
					{
						if(Config . PassThrough)
						{
							SerWrite(&Char,1);

							return(TRUE);
						}
						else
						{
							if(Config . xONxOFF)
								Status = STATUS_HOLDING;
						}
					}

						/* Restart in/output. */

					if(Char == XOF)
					{
						if(Config . PassThrough)
						{
							SerWrite(&Char,1);

							return(TRUE);
						}
					}

						/* Convert special
						 * Amiga characters into
						 * alien IBM dialect.
						 */

SendIt:					if(Config . Font == FONT_IBM)
					{
						if(IBMConversion[Char])
							SerWrite(&IBMConversion[Char],1);
						else
							SerWrite(&Char,1);
					}
					else
						SerWrite(&Char,1);
				}
			}

			return(TRUE);
		}
	}

	return(Result);
}
