/*
**	termScroll.c
**
**	Support routines for optimized screen scrolling.
**
**	Copyright © 1990-1993 by Olaf `Olsen' Barthel & MXM
**		All Rights Reserved
*/

#include "termGlobal.h"

	/* ScrollLineRectFill():
	 *
	 *	Fill a rectangular portion of the window raster with the help
	 *	of the scrolling information.
	 */

VOID __regargs
ScrollLineRectFill(struct RastPort *RPort,WORD MinX,WORD MinY,WORD MaxX,WORD MaxY)
{
	if(RPort -> BitMap -> Depth > 1)
	{
		if(MinX < MaxX)
		{
				/* Is there anything on the screen at all? */

			if(ScrollLineFirst <= ScrollLineLast)
			{
				if(UseMasking)
				{
					WORD	ScrollLineMask	= 0,
						ScrollLineLeft	= 32767,
						ScrollLineRight	= 0,
						Temp,
						i;

						/* Determine screen colour mask. */

					for(i = MinY / TextFontHeight ; i <= MaxY / TextFontHeight ; i++)
					{
						if(ScrollLines[i] . Width)
						{
							if((Temp = ScrollLines[i] . Left * ScrollLines[i] . Width) < ScrollLineLeft)
								ScrollLineLeft = Temp;

							if((Temp = (ScrollLines[i] . Right * ScrollLines[i] . Width) - 1) > ScrollLineRight)
								ScrollLineRight = Temp;

							ScrollLineMask |= ScrollLines[i] . ColourMask;
						}
					}

						/* Wrap the bits. */

					ScrollLineMask &= DepthMask;

						/* Did we get a sensible colour? */

					if(ScrollLineMask && ScrollLineLeft <= ScrollLineRight)
					{
							/* Determine new left margin. */

						if(ScrollLineLeft > MinX)
							MinX = ScrollLineLeft;

							/* Determine new right margin. */

						if(ScrollLineRight < MaxX)
							MaxX = ScrollLineRight;

							/* Determine new top line margin. */

						if((Temp = MUL_Y(ScrollLineFirst)) > MinY)
							MinY = Temp;

							/* Determine new bottom line margin. */

						if((Temp = MUL_Y(ScrollLineLast + 1) - 1) < MaxY)
							MaxY = Temp;

							/* Set the colour mask. */

						SetWrMsk(RPort,ScrollLineMask);

							/* Add margin for italics or boldface. */

						if(MaxX == ScrollLineRight)
							MaxX += FontRightExtend;
					}
				}
				else
				{
					WORD	ScrollLineLeft	= 32767,
						ScrollLineRight	= 0,
						Temp,
						i;

						/* Determine screen colour mask. */

					for(i = MinY / TextFontHeight ; i <= MaxY / TextFontHeight ; i++)
					{
						if(ScrollLines[i] . Width)
						{
							if((Temp = ScrollLines[i] . Left * ScrollLines[i] . Width) < ScrollLineLeft)
								ScrollLineLeft = Temp;

							if((Temp = (ScrollLines[i] . Right * ScrollLines[i] . Width) - 1) > ScrollLineRight)
								ScrollLineRight = Temp;
						}
					}

						/* Did we get a sensible colour? */

					if(ScrollLineLeft <= ScrollLineRight)
					{
							/* Determine new left margin. */

						if(ScrollLineLeft > MinX)
							MinX = ScrollLineLeft;

							/* Determine new right margin. */

						if(ScrollLineRight < MaxX)
							MaxX = ScrollLineRight;

							/* Determine new top line margin. */

						if((Temp = MUL_Y(ScrollLineFirst)) > MinY)
							MinY = Temp;

							/* Determine new bottom line margin. */

						if((Temp = MUL_Y(ScrollLineLast + 1) - 1) < MaxY)
							MaxY = Temp;

							/* Add margin for italics or boldface. */

						if(MaxX == ScrollLineRight)
							MaxX += FontRightExtend;
					}
				}
			}
		}
	}

		/* And clear the raster. */

	if(MinX < MaxX && MinY < MaxY)
		RectFill(RPort,WindowLeft + MinX,WindowTop + MinY,WindowLeft + MaxX,WindowTop + MaxY);
}

	/* ScrollLineRaster():
	 *
	 *	Scroll the window raster with the help
	 *	of the scrolling information.
	 */

VOID __regargs
ScrollLineRaster(struct RastPort *RPort,WORD DeltaX,WORD DeltaY,WORD MinX,WORD MinY,WORD MaxX,WORD MaxY,BYTE Smooth)
{
	if(RPort -> BitMap -> Depth > 1)
	{
		if(MinX < MaxX)
		{
			if(UseMasking)
			{
				WORD ScrollLineMask;

					/* Are we to scroll a line in horizontal direction? If so, use the
					 * colour mask of the current line.
					 */

				if(DeltaX)
				{
						/* Set the colour mask. */

					if(ScrollLineMask = ScrollLines[CursorY] . ColourMask & DepthMask)
						SetWrMsk(RPort,ScrollLineMask);
				}
				else
				{
						/* Any data on screen worth scrolling? */

					if(ScrollLineFirst <= ScrollLineLast)
					{
						WORD Temp,First,Last,SaveMinY = MinY / TextFontHeight,SaveMaxY = MaxY / TextFontHeight,ScrollLineLeft = 32767,ScrollLineRight = 0,i;

							/* Reset colourmask. */

						ScrollLineMask = 0;

							/* Build both the colour mask and the margins. */

						for(i = MinY / TextFontHeight ; i <= MaxY / TextFontHeight ; i++)
						{
							if(ScrollLines[i] . Width)
							{
								if((Temp = ScrollLines[i] . Left * ScrollLines[i] . Width) < ScrollLineLeft)
									ScrollLineLeft = Temp;

								if((Temp = (ScrollLines[i] . Right * ScrollLines[i] . Width) - 1) > ScrollLineRight)
									ScrollLineRight = Temp;

								ScrollLineMask |= ScrollLines[i] . ColourMask;
							}
						}

							/* Wrap the bits. */

						ScrollLineMask &= DepthMask;

							/* Sensible results? */

						if(ScrollLineMask && ScrollLineLeft <= ScrollLineRight)
						{
								/* Determine new left margin. */

							if(ScrollLineLeft > MinX)
								MinX = ScrollLineLeft;

								/* Determine new right margin. */

							if(ScrollLineRight < MaxX)
								MaxX = ScrollLineRight;

								/* Scroll down or up? */

							if(DeltaY < 0)
							{
									/* So we are to scroll down, find the first
									 * blank line if any.
									 */

								if((Temp = MUL_Y(ScrollLineFirst)) > MinY)
									MinY = Temp;

									/* Find the last blank lines if any. */

								if((Temp = MUL_Y(ScrollLineLast + 1) - DeltaY - 1) < MaxY)
									MaxY = Temp;

									/* Determine margins and the like... */

								Last	= (MaxY + 1) / TextFontHeight;
								First	= Last - ((MaxY - MinY + TextFontHeight + DeltaY) / TextFontHeight);
								Temp	= (-DeltaY) / TextFontHeight;

									/* Move the scroll line info up. */

								for(i = Last - 1 ; i >= First ; i--)
									ScrollLines[i] = ScrollLines[i - Temp];

									/* Clear the remaining lines. */

								for(i = First - Temp ; i < First ; i++)
								{
									ScrollLines[i] . Left		= 32767;
									ScrollLines[i] . Right		= 0;
									ScrollLines[i] . ColourMask	= 0;
									ScrollLines[i] . Width		= 0;
								}

									/* Is the first line we were working
									 * on the first line of the whole display?
									 * If so, update the line marker.
									 */

								if(!SaveMinY)
									ScrollLineFirst += Temp;

									/* Now take a look at the last line.
									 * If the last line we were working
									 * on is in fact the last line of the
									 * display, update the line marker.
									 */

								if(SaveMaxY == LastLine)
								{
									ScrollLineLast += Temp;

									if(ScrollLineLast > LastLine)
										ScrollLineLast = LastLine;
								}
							}
							else
							{
									/* So we are to scroll up, find the last
									 * blank line if any.
									 */

								if((Temp = MUL_Y(ScrollLineLast + 1) - 1) < MaxY)
									MaxY = Temp;

									/* Find the first blank lines if any. */

								if((Temp = MUL_Y(ScrollLineFirst) - DeltaY) > MinY)
									MinY = Temp;

									/* Determine margins and the like... */

								First	= MinY / TextFontHeight;
								Last	= ((MaxY - MinY + TextFontHeight - DeltaY) / TextFontHeight);
								Temp	= DeltaY / TextFontHeight;

									/* Move the scroll line info down. */

								for(i = First ; i < First + Last ; i++)
									ScrollLines[i] = ScrollLines[i + Temp];

									/* Clear the remaining lines. */

								for(i = First + Last ; i < First + Last + Temp ; i++)
								{
									ScrollLines[i] . Left		= 32767;
									ScrollLines[i] . Right		= 0;
									ScrollLines[i] . ColourMask	= 0;
									ScrollLines[i] . Width		= 0;
								}

									/* Decrease number of last line. */

								if(SaveMaxY == LastLine)
								{
									if(ScrollLineLast > Temp)
										ScrollLineLast -= Temp;
									else
										ScrollLineLast = 0;
								}

									/* Decrease number of first line. */

								if(!SaveMinY)
								{
									if(ScrollLineFirst > Temp)
										ScrollLineFirst -= Temp;
									else
										ScrollLineFirst = 0;
								}
							}

								/* Adapt possible changes in the lines for first and last line. */

							while(ScrollLineFirst < RasterHeight)
							{
								if(ScrollLines[ScrollLineFirst] . Left > ScrollLines[ScrollLineFirst] . Right)
									ScrollLineFirst++;
								else
									break;
							}

							while(ScrollLineLast > 0)
							{
								if(ScrollLines[ScrollLineLast] . Left > ScrollLines[ScrollLineLast] . Right)
									ScrollLineLast--;
								else
									break;
							}

								/* Set the colour mask. */

							SetWrMsk(RPort,ScrollLineMask);

								/* Add margin for italics or boldface. */

							if(MaxX == ScrollLineRight)
								MaxX += FontRightExtend;
						}
					}
				}
			}
			else
			{
					/* Are we to scroll a line in horizontal direction? If so, use the
					 * colour mask of the current line.
					 */

				if(!DeltaX)
				{
						/* Any data on screen worth scrolling? */

					if(ScrollLineFirst <= ScrollLineLast)
					{
						WORD Temp,First,Last,SaveMinY = MinY / TextFontHeight,SaveMaxY = MaxY / TextFontHeight,ScrollLineLeft = 32767,ScrollLineRight = 0,i;

							/* Build both the colour mask and the margins. */

						for(i = MinY / TextFontHeight ; i <= MaxY / TextFontHeight ; i++)
						{
							if(ScrollLines[i] . Width)
							{
								if((Temp = ScrollLines[i] . Left * ScrollLines[i] . Width) < ScrollLineLeft)
									ScrollLineLeft = Temp;

								if((Temp = (ScrollLines[i] . Right * ScrollLines[i] . Width) - 1) > ScrollLineRight)
									ScrollLineRight = Temp;
							}
						}

							/* Sensible results? */

						if(ScrollLineLeft <= ScrollLineRight)
						{
								/* Determine new left margin. */

							if(ScrollLineLeft > MinX)
								MinX = ScrollLineLeft;

								/* Determine new right margin. */

							if(ScrollLineRight < MaxX)
								MaxX = ScrollLineRight;

								/* Scroll down or up? */

							if(DeltaY < 0)
							{
									/* So we are to scroll down, find the first
									 * blank line if any.
									 */

								if((Temp = MUL_Y(ScrollLineFirst)) > MinY)
									MinY = Temp;

									/* Find the last blank lines if any. */

								if((Temp = MUL_Y(ScrollLineLast + 1) - DeltaY - 1) < MaxY)
									MaxY = Temp;

									/* Determine margins and the like... */

								Last	= (MaxY + 1) / TextFontHeight;
								First	= Last - ((MaxY - MinY + TextFontHeight + DeltaY) / TextFontHeight);
								Temp	= (-DeltaY) / TextFontHeight;

									/* Move the scroll line info up. */

								for(i = Last - 1 ; i >= First ; i--)
									ScrollLines[i] = ScrollLines[i - Temp];

									/* Clear the remaining lines. */

								for(i = First - Temp ; i < First ; i++)
								{
									ScrollLines[i] . Left		= 32767;
									ScrollLines[i] . Right		= 0;
									ScrollLines[i] . Width		= 0;
								}

									/* Is the first line we were working
									 * on the first line of the whole display?
									 * If so, update the line marker.
									 */

								if(!SaveMinY)
									ScrollLineFirst += Temp;

									/* Now take a look at the last line.
									 * If the last line we were working
									 * on is in fact the last line of the
									 * display, update the line marker.
									 */

								if(SaveMaxY == LastLine)
								{
									ScrollLineLast += Temp;

									if(ScrollLineLast > LastLine)
										ScrollLineLast = LastLine;
								}
							}
							else
							{
									/* So we are to scroll up, find the last
									 * blank line if any.
									 */

								if((Temp = MUL_Y(ScrollLineLast + 1) - 1) < MaxY)
									MaxY = Temp;

									/* Find the first blank lines if any. */

								if((Temp = MUL_Y(ScrollLineFirst) - DeltaY) > MinY)
									MinY = Temp;

									/* Determine margins and the like... */

								First	= MinY / TextFontHeight;
								Last	= ((MaxY - MinY + TextFontHeight - DeltaY) / TextFontHeight);
								Temp	= DeltaY / TextFontHeight;

									/* Move the scroll line info down. */

								for(i = First ; i < First + Last ; i++)
									ScrollLines[i] = ScrollLines[i + Temp];

									/* Clear the remaining lines. */

								for(i = First + Last ; i < First + Last + Temp ; i++)
								{
									ScrollLines[i] . Left		= 32767;
									ScrollLines[i] . Right		= 0;
									ScrollLines[i] . Width		= 0;
								}

									/* Decrease number of last line. */

								if(SaveMaxY == LastLine)
								{
									if(ScrollLineLast > Temp)
										ScrollLineLast -= Temp;
									else
										ScrollLineLast = 0;
								}

									/* Decrease number of first line. */

								if(!SaveMinY)
								{
									if(ScrollLineFirst > Temp)
										ScrollLineFirst -= Temp;
									else
										ScrollLineFirst = 0;
								}
							}

								/* Adapt possible changes in the lines for first and last line. */

							while(ScrollLineFirst < RasterHeight)
							{
								if(ScrollLines[ScrollLineFirst] . Left > ScrollLines[ScrollLineFirst] . Right)
									ScrollLineFirst++;
								else
									break;
							}

							while(ScrollLineLast > 0)
							{
								if(ScrollLines[ScrollLineLast] . Left > ScrollLines[ScrollLineLast] . Right)
									ScrollLineLast--;
								else
									break;
							}

								/* Add margin for italics or boldface. */

							if(MaxX == ScrollLineRight)
								MaxX += FontRightExtend;
						}
					}
				}
			}
		}
	}

		/* And scroll the raster. */

	if(MinX < MaxX && MinY < MaxY)
	{
		LONG OldPen;

		MinX += WindowLeft;
		MaxX += WindowLeft;

		MinY += WindowTop;
		MaxY += WindowTop;

			/* Non-standard background colour? */

		if((OldPen = ReadBPen(RPort)) != MappedPens[0][0])
		{
			SetBPen(RPort,MappedPens[0][0]);

			if(Smooth && DeltaY)
			{
				WORD Lines = DeltaY / 2;

				if(Lines > 0)
				{
					while(Lines--)
					{
						WaitTOF();

						ScrollRaster(RPort,0,2,MinX,MinY,MaxX,MaxY);
					}
				}
				else
				{
					while(Lines++)
					{
						WaitTOF();

						ScrollRaster(RPort,0,-2,MinX,MinY,MaxX,MaxY);
					}
				}
			}
			else
				ScrollRaster(RPort,DeltaX,DeltaY,MinX,MinY,MaxX,MaxY);

			SetBPen(RPort,OldPen);
		}
		else
		{
			if(Smooth && DeltaY)
			{
				WORD Lines = DeltaY / 2;

				if(Lines > 0)
				{
					while(Lines--)
					{
						WaitTOF();

						ScrollRaster(RPort,0,2,MinX,MinY,MaxX,MaxY);
					}
				}
				else
				{
					while(Lines++)
					{
						WaitTOF();

						ScrollRaster(RPort,0,-2,MinX,MinY,MaxX,MaxY);
					}
				}
			}
			else
				ScrollRaster(RPort,DeltaX,DeltaY,MinX,MinY,MaxX,MaxY);
		}
	}
}

	/* ScrollLineEraseScreen(BYTE Mode):
	 *
	 *	Erase a part of the screen.
	 */

VOID __regargs
ScrollLineEraseScreen(BYTE Mode)
{
	if(RPort -> BitMap -> Depth > 1)
	{
		WORD i;

		switch(Mode)
		{
				/* Erase from first line to current cursor line (inclusive). */

			case 1:	ScrollLineFirst = CursorY;

					/* Reset the lines. */

				for(i = 0 ; i < CursorY ; i++)
				{
					ScrollLines[i] . Left		= 32767;
					ScrollLines[i] . Right		= 0;
					ScrollLines[i] . ColourMask	= 0;
					ScrollLines[i] . Width		= 0;
				}

				ScrollLines[CursorY] . Left = CursorX + 1;

				if(ScrollLines[CursorY] . Right < ScrollLines[CursorY] . Left)
				{
					ScrollLines[CursorY] . Left	= 32767;
					ScrollLines[CursorY] . Right	= 0;
				}

				break;

				/* Erase entire screen. */

			case 2:	for(i = 0 ; i < RasterHeight ; i++)
				{
					ScrollLines[i] . Left		= 32767;
					ScrollLines[i] . Right		= 0;
					ScrollLines[i] . ColourMask	= 0;
					ScrollLines[i] . Width		= 0;
				}

				ScrollLineFirst	= 32767;
				ScrollLineLast	= 0;

				break;

				/* Erase from current cursor position to end of screen. */

			default:for(i = CursorY + 1 ; i < RasterHeight ; i++)
				{
					ScrollLines[i] . Left		= 32767;
					ScrollLines[i] . Right		= 0;
					ScrollLines[i] . ColourMask	= 0;
					ScrollLines[i] . Width		= 0;
				}

				if(CursorX)
				{
					ScrollLines[CursorY] . Right = CursorX;

					if(ScrollLines[CursorY] . Right < ScrollLines[CursorY] . Left)
					{
						ScrollLines[CursorY] . Left	= 32767;
						ScrollLines[CursorY] . Right	= 0;
					}
				}
				else
				{
					ScrollLines[CursorY] . Left	= 32767;
					ScrollLines[CursorY] . Right	= 0;
				}

					/* Cleared the entire screen? */

				if(CursorY)
					ScrollLineLast = CursorY;
				else
				{
					ScrollLineFirst	= 32767;
					ScrollLineLast	= 0;
				}

				break;
		}

			/* Adapt possible changes in the lines for first and last line. */

		while(ScrollLineFirst < RasterHeight)
		{
			if(ScrollLines[ScrollLineFirst] . Left > ScrollLines[ScrollLineFirst] . Right)
				ScrollLineFirst++;
			else
				break;
		}

		while(ScrollLineLast > 0)
		{
			if(ScrollLines[ScrollLineLast] . Left > ScrollLines[ScrollLineLast] . Right)
				ScrollLineLast--;
			else
				break;
		}
	}
}

	/* ScrollLineEraseLine(BYTE Mode):
	 *
	 *	Erase parts of the current cursor line.
	 */

VOID __regargs
ScrollLineEraseLine(BYTE Mode)
{
	if(RPort -> BitMap -> Depth > 1)
	{
		switch(Mode)
		{
				/* Erase from left margin to current cursor position (inclusive). */

			case 1:	ScrollLines[CursorY] . Left = CursorX + 1;
				break;

				/* Erase entire line. */

			case 2:	ScrollLines[CursorY] . Left	= 32767;
				ScrollLines[CursorY] . Right	= 0;

				break;

				/* Erase from current cursor position towards end of line. */

			default:if(CursorX)
					ScrollLines[CursorY] . Right = CursorX;
				else
				{
					ScrollLines[CursorY] . Left	= 32767;
					ScrollLines[CursorY] . Right	= 0;
				}

				break;
		}

			/* Adapt possible changes in the lines for first and last line. */

		while(ScrollLineFirst < RasterHeight)
		{
			if(ScrollLines[ScrollLineFirst] . Left > ScrollLines[ScrollLineFirst] . Right)
				ScrollLineFirst++;
			else
				break;
		}

		while(ScrollLineLast > 0)
		{
			if(ScrollLines[ScrollLineLast] . Left > ScrollLines[ScrollLineLast] . Right)
				ScrollLineLast--;
			else
				break;
		}
	}
}

	/* ScrollLineEraseCharacters(WORD Chars):
	 *
	 *	Erase a number of characters in the current cursor line.
	 */

VOID __regargs
ScrollLineEraseCharacters(WORD Chars)
{
	if(RPort -> BitMap -> Depth > 1)
	{
			/* Any characters to erase? */

		if(ScrollLines[CursorY] . Right)
			ScrollLines[CursorY] . Right -= Chars;
	}
}

	/* ScrollLineShiftChar(WORD Size):
	 *
	 *	Shift the characters following the current cursor position
	 *	Size characters to the right.
	 */

VOID __regargs
ScrollLineShiftChar(WORD Size)
{
	if(RPort -> BitMap -> Depth > 1)
	{
			/* Any characters to scroll? */

		if(ScrollLines[CursorY] . Right > 0)
			ScrollLines[CursorY] . Right += Size;
	}
}

	/* ScrollLinePutString(WORD Length):
	 *
	 *	Update the line info according to the length of a string
	 *	to be printed.
	 */

VOID __regargs
ScrollLinePutString(WORD Length)
{
	if(RPort -> BitMap -> Depth > 1)
	{
		LONG Width;

			/* Which scale is the font we are currently using? */

		if(RasterAttr[CursorY] >= SCALE_ATTR_TOP2X)
		{
				/* Valid length? */

			if(CursorX + Length >= RasterWidth / 2)
				Length = (RasterWidth / 2) - CursorX;

				/* Double width. */

			Width = TextFontWidth * 2;
		}
		else
		{
			if(Config -> EmulationConfig -> FontScale == SCALE_HALF)
			{
					/* Valid length? */

				if(CursorX + Length >= RasterWidth * 2)
					Length = (RasterWidth * 2) - CursorX;

					/* Half width. */

				Width = TextFontWidth / 2;
			}
			else
			{
					/* Valid length? */

				if(CursorX + Length >= RasterWidth)
					Length = RasterWidth - CursorX;

					/* Normal width. */

				Width = TextFontWidth;
			}
		}

			/* Sensible value? */

		if(Length > 0)
		{
			if(UseMasking)
			{
				struct ScrollLineInfo *Alias = &ScrollLines[CursorY];

					/* Update line colour mask. */

				Alias -> ColourMask |= ReadAPen(RPort) | ReadBPen(RPort);

					/* Update font scale. */

				Alias -> Width = Width;

					/* Set write mask (will affect Text() since it is called
					 * after this routine has finished.
					 */

				SetWrMsk(RPort,Alias -> ColourMask);

					/* Update right margin. */

				if(CursorX < Alias -> Left)
					Alias -> Left = CursorX;

					/* Update left margin. */

				if(CursorX + Length > Alias -> Right)
					Alias -> Right = CursorX + Length;

					/* Update topmost line. */

				if(CursorY < ScrollLineFirst)
					ScrollLineFirst = CursorY;

					/* Update bottommost line. */

				if(CursorY > ScrollLineLast)
					ScrollLineLast = CursorY;
			}
			else
			{
				struct ScrollLineInfo *Alias = &ScrollLines[CursorY];

					/* Update font scale. */

				Alias -> Width = Width;

					/* Update right margin. */

				if(CursorX < Alias -> Left)
					Alias -> Left = CursorX;

					/* Update left margin. */

				if(CursorX + Length > Alias -> Right)
					Alias -> Right = CursorX + Length;

					/* Update topmost line. */

				if(CursorY < ScrollLineFirst)
					ScrollLineFirst = CursorY;

					/* Update bottommost line. */

				if(CursorY > ScrollLineLast)
					ScrollLineLast = CursorY;
			}
		}
	}
}
