/*
 *  FreeLoader
 *  Copyright (C) 1998-2003  Brian Palmer  <brianp@sginet.com>
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include <freeldr.h>

PVOID	TextVideoBuffer = NULL;
extern BOOLEAN UiDrawTime;
extern BOOLEAN UiMinimal;

/*
 * printf() - prints formatted text to stdout
 * originally from GRUB
 */
int printf(const char *format, ... )
{
	va_list ap;
	va_start(ap,format);
	char c, *ptr, str[16];

	while ((c = *(format++)))
	{
		if (c != '%')
		{
			MachConsPutChar(c);
		}
		else
		{
			switch (c = *(format++))
			{
			case 'd': case 'u': case 'x':
                if (c == 'x')
                    *_itoa(va_arg(ap, unsigned long), str, 16) = 0;
                else
                    *_itoa(va_arg(ap, unsigned long), str, 10) = 0;

				ptr = str;

				while (*ptr)
				{
					MachConsPutChar(*(ptr++));
				}
				break;

			case 'c': MachConsPutChar((va_arg(ap,int))&0xff); break;

			case 's':
				ptr = va_arg(ap,char *);

				while ((c = *(ptr++)))
				{
					MachConsPutChar(c);
				}
				break;
			case '%':
				MachConsPutChar(c);
				break;
			default:
				printf("\nprintf() invalid format specifier - %%%c\n", c);
				break;
			}
		}
	}

	va_end(ap);

	return 0;
}

BOOLEAN TuiInitialize(VOID)
{
	MachVideoClearScreen(ATTR(COLOR_WHITE, COLOR_BLACK));
	MachVideoHideShowTextCursor(FALSE);

	TextVideoBuffer = VideoAllocateOffScreenBuffer();
	if (TextVideoBuffer == NULL)
	{
		return FALSE;
	}

	return TRUE;
}

VOID TuiUnInitialize(VOID)
{
	if (UiUseSpecialEffects)
	{
		TuiFadeOut();
	}
	else
	{
		MachVideoSetDisplayMode(NULL, FALSE);
	}

	//VideoClearScreen();
	MachVideoHideShowTextCursor(TRUE);
}

VOID TuiDrawBackdrop(VOID)
{
    if (UiMinimal)
    {
        //
        // Fill in a black background
        //
        TuiFillArea(0,
                    0,
                    UiScreenWidth - 1,
                    UiScreenHeight - 1,
                    0,
                    0);

        //
        // Update the screen buffer
        //
        VideoCopyOffScreenBufferToVRAM();
        return;
    }

	//
	// Fill in the background (excluding title box & status bar)
	//
	TuiFillArea(0,
			TUI_TITLE_BOX_CHAR_HEIGHT,
			UiScreenWidth - 1,
			UiScreenHeight - 2,
			UiBackdropFillStyle,
			ATTR(UiBackdropFgColor, UiBackdropBgColor));

	//
	// Draw the title box
	//
	TuiDrawBox(0,
			0,
			UiScreenWidth - 1,
			TUI_TITLE_BOX_CHAR_HEIGHT - 1,
			D_VERT,
			D_HORZ,
			TRUE,
			FALSE,
			ATTR(UiTitleBoxFgColor, UiTitleBoxBgColor));

	//
	// Draw version text
	//
	TuiDrawText(2,
			1,
			GetFreeLoaderVersionString(),
			ATTR(UiTitleBoxFgColor, UiTitleBoxBgColor));

	//
	// Draw copyright
	//
	TuiDrawText(2,
			2,
			BY_AUTHOR,
			ATTR(UiTitleBoxFgColor, UiTitleBoxBgColor));
	TuiDrawText(2,
			3,
			AUTHOR_EMAIL,
			ATTR(UiTitleBoxFgColor, UiTitleBoxBgColor));

	//
	// Draw help text
	//
	TuiDrawText(UiScreenWidth - 16, 3, /*"F1 for Help"*/"F8 for Options", ATTR(UiTitleBoxFgColor, UiTitleBoxBgColor));

	//
	// Draw title text
	//
	TuiDrawText( (UiScreenWidth / 2) - (strlen(UiTitleBoxTitleText) / 2),
			2,
			UiTitleBoxTitleText,
			ATTR(UiTitleBoxFgColor, UiTitleBoxBgColor));

	//
	// Draw status bar
	//
	TuiDrawStatusText("Welcome to FreeLoader!");

	//
	// Update the date & time
	//
	TuiUpdateDateTime();

	VideoCopyOffScreenBufferToVRAM();
}

/*
 * FillArea()
 * This function assumes coordinates are zero-based
 */
VOID TuiFillArea(ULONG Left, ULONG Top, ULONG Right, ULONG Bottom, CHAR FillChar, UCHAR Attr /* Color Attributes */)
{
	PUCHAR	ScreenMemory = (PUCHAR)TextVideoBuffer;
	ULONG		i, j;

	// Clip the area to the screen
	if ((Left >= UiScreenWidth) || (Top >= UiScreenHeight))
	{
		return;
	}
	if (Right >= UiScreenWidth)
	{
		Right = UiScreenWidth - 1;
	}
	if (Bottom >= UiScreenHeight)
	{
		Bottom = UiScreenHeight - 1;
	}

	// Loop through each line and fill it in
	for (i=Top; i<=Bottom; i++)
	{
		// Loop through each character (column) in the line and fill it in
		for (j=Left; j<=Right; j++)
		{
			ScreenMemory[((i*2)*UiScreenWidth)+(j*2)] = (UCHAR)FillChar;
			ScreenMemory[((i*2)*UiScreenWidth)+(j*2)+1] = Attr;
		}
	}
}

/*
 * DrawShadow()
 * This function assumes coordinates are zero-based
 */
VOID TuiDrawShadow(ULONG Left, ULONG Top, ULONG Right, ULONG Bottom)
{
	PUCHAR	ScreenMemory = (PUCHAR)TextVideoBuffer;
	ULONG		Idx;

	// Shade the bottom of the area
	if (Bottom < (UiScreenHeight - 1))
	{
		if (UiScreenHeight < 34)
		{
			Idx=Left + 2;
		}
		else
		{
			Idx=Left + 1;
		}

		for (; Idx<=Right; Idx++)
		{
			ScreenMemory[(((Bottom+1)*2)*UiScreenWidth)+(Idx*2)+1] = ATTR(COLOR_GRAY, COLOR_BLACK);
		}
	}

	// Shade the right of the area
	if (Right < (UiScreenWidth - 1))
	{
		for (Idx=Top+1; Idx<=Bottom; Idx++)
		{
			ScreenMemory[((Idx*2)*UiScreenWidth)+((Right+1)*2)+1] = ATTR(COLOR_GRAY, COLOR_BLACK);
		}
	}
	if (UiScreenHeight < 34)
	{
		if ((Right + 1) < (UiScreenWidth - 1))
		{
			for (Idx=Top+1; Idx<=Bottom; Idx++)
			{
				ScreenMemory[((Idx*2)*UiScreenWidth)+((Right+2)*2)+1] = ATTR(COLOR_GRAY, COLOR_BLACK);
			}
		}
	}

	// Shade the bottom right corner
	if ((Right < (UiScreenWidth - 1)) && (Bottom < (UiScreenHeight - 1)))
	{
		ScreenMemory[(((Bottom+1)*2)*UiScreenWidth)+((Right+1)*2)+1] = ATTR(COLOR_GRAY, COLOR_BLACK);
	}
	if (UiScreenHeight < 34)
	{
		if (((Right + 1) < (UiScreenWidth - 1)) && (Bottom < (UiScreenHeight - 1)))
		{
			ScreenMemory[(((Bottom+1)*2)*UiScreenWidth)+((Right+2)*2)+1] = ATTR(COLOR_GRAY, COLOR_BLACK);
		}
	}
}

/*
 * DrawBox()
 * This function assumes coordinates are zero-based
 */
VOID TuiDrawBox(ULONG Left, ULONG Top, ULONG Right, ULONG Bottom, UCHAR VertStyle, UCHAR HorzStyle, BOOLEAN Fill, BOOLEAN Shadow, UCHAR Attr)
{
	UCHAR	ULCorner, URCorner, LLCorner, LRCorner;

	// Calculate the corner values
	if (HorzStyle == HORZ)
	{
		if (VertStyle == VERT)
		{
			ULCorner = UL;
			URCorner = UR;
			LLCorner = LL;
			LRCorner = LR;
		}
		else // VertStyle == D_VERT
		{
			ULCorner = VD_UL;
			URCorner = VD_UR;
			LLCorner = VD_LL;
			LRCorner = VD_LR;
		}
	}
	else // HorzStyle == D_HORZ
	{
		if (VertStyle == VERT)
		{
			ULCorner = HD_UL;
			URCorner = HD_UR;
			LLCorner = HD_LL;
			LRCorner = HD_LR;
		}
		else // VertStyle == D_VERT
		{
			ULCorner = D_UL;
			URCorner = D_UR;
			LLCorner = D_LL;
			LRCorner = D_LR;
		}
	}

	// Fill in box background
	if (Fill)
	{
		TuiFillArea(Left, Top, Right, Bottom, ' ', Attr);
	}

	// Fill in corners
	TuiFillArea(Left, Top, Left, Top, ULCorner, Attr);
	TuiFillArea(Right, Top, Right, Top, URCorner, Attr);
	TuiFillArea(Left, Bottom, Left, Bottom, LLCorner, Attr);
	TuiFillArea(Right, Bottom, Right, Bottom, LRCorner, Attr);

	// Fill in left line
	TuiFillArea(Left, Top+1, Left, Bottom-1, VertStyle, Attr);
	// Fill in top line
	TuiFillArea(Left+1, Top, Right-1, Top, HorzStyle, Attr);
	// Fill in right line
	TuiFillArea(Right, Top+1, Right, Bottom-1, VertStyle, Attr);
	// Fill in bottom line
	TuiFillArea(Left+1, Bottom, Right-1, Bottom, HorzStyle, Attr);

	// Draw the shadow
	if (Shadow)
	{
		TuiDrawShadow(Left, Top, Right, Bottom);
	}
}

/*
 * DrawText()
 * This function assumes coordinates are zero-based
 */
VOID TuiDrawText(ULONG X, ULONG Y, PCSTR Text, UCHAR Attr)
{
	PUCHAR	ScreenMemory = (PUCHAR)TextVideoBuffer;
	ULONG		i, j;

	// Draw the text
	for (i=X, j=0; Text[j]  && i<UiScreenWidth; i++,j++)
	{
		ScreenMemory[((Y*2)*UiScreenWidth)+(i*2)] = (UCHAR)Text[j];
		ScreenMemory[((Y*2)*UiScreenWidth)+(i*2)+1] = Attr;
	}
}

VOID TuiDrawCenteredText(ULONG Left, ULONG Top, ULONG Right, ULONG Bottom, PCSTR TextString, UCHAR Attr)
{
	ULONG		TextLength;
	ULONG		BoxWidth;
	ULONG		BoxHeight;
	ULONG		LineBreakCount;
	ULONG		Index;
	ULONG		LastIndex;
	ULONG		RealLeft;
	ULONG		RealTop;
	ULONG		X;
	ULONG		Y;
	CHAR	Temp[2];

	TextLength = strlen(TextString);

	// Count the new lines and the box width
	LineBreakCount = 0;
	BoxWidth = 0;
	LastIndex = 0;
	for (Index=0; Index<TextLength; Index++)
	{
		if (TextString[Index] == '\n')
		{
			LastIndex = Index;
			LineBreakCount++;
		}
		else
		{
			if ((Index - LastIndex) > BoxWidth)
			{
				BoxWidth = (Index - LastIndex);
			}
		}
	}

	BoxHeight = LineBreakCount + 1;

	RealLeft = (((Right - Left) - BoxWidth) / 2) + Left;
	RealTop = (((Bottom - Top) - BoxHeight) / 2) + Top;

	LastIndex = 0;
	for (Index=0; Index<TextLength; Index++)
	{
		if (TextString[Index] == '\n')
		{
			RealTop++;
			LastIndex = 0;
		}
		else
		{
			X = RealLeft + LastIndex;
			Y = RealTop;
			LastIndex++;
			Temp[0] = TextString[Index];
			Temp[1] = 0;
			TuiDrawText(X, Y, Temp, Attr);
		}
	}
}

VOID TuiDrawStatusText(PCSTR StatusText)
{
	ULONG		i;

    //
    // Minimal UI doesn't have a status bar
    //
    if (UiMinimal) return;

	TuiDrawText(0, UiScreenHeight-1, " ", ATTR(UiStatusBarFgColor, UiStatusBarBgColor));
	TuiDrawText(1, UiScreenHeight-1, StatusText, ATTR(UiStatusBarFgColor, UiStatusBarBgColor));

	for (i=strlen(StatusText)+1; i<UiScreenWidth; i++)
	{
		TuiDrawText(i, UiScreenHeight-1, " ", ATTR(UiStatusBarFgColor, UiStatusBarBgColor));
	}

	VideoCopyOffScreenBufferToVRAM();
}

VOID TuiUpdateDateTime(VOID)
{
	ULONG	Year, Month, Day;
	ULONG	Hour, Minute, Second;
	CHAR	DateString[40];
	CHAR	TimeString[40];
	CHAR	TempString[20];
	BOOLEAN	PMHour = FALSE;

    /* Don't draw the time if this has been disabled */
    if (!UiDrawTime) return;

	MachRTCGetCurrentDateTime(&Year, &Month, &Day, &Hour, &Minute, &Second);
	if (Year < 1 || 9999 < Year || Month < 1 || 12 < Month || Day < 1 ||
            31 < Day || 23 < Hour || 59 < Minute || 59 < Second)
	{
		/* This happens on QEmu sometimes. We just skip updating */
		return;
	}
	// Get the month name
	strcpy(DateString, UiMonthNames[Month - 1]);
	// Get the day
	_itoa(Day, TempString, 10);
	// Get the day postfix
	if (1 == Day || 21 == Day || 31 == Day)
	{
		strcat(TempString, "st");
	}
	else if (2 == Day || 22 == Day)
	{
		strcat(TempString, "nd");
	}
	else if (3 == Day || 23 == Day)
	{
		strcat(TempString, "rd");
	}
	else
	{
		strcat(TempString, "th");
	}

	// Add the day to the date
	strcat(DateString, TempString);
	strcat(DateString, " ");

	// Get the year and add it to the date
	_itoa(Year, TempString, 10);
	strcat(DateString, TempString);

	// Draw the date
	TuiDrawText(UiScreenWidth-strlen(DateString)-2, 1, DateString, ATTR(UiTitleBoxFgColor, UiTitleBoxBgColor));

	// Get the hour and change from 24-hour mode to 12-hour
	if (Hour > 12)
	{
		Hour -= 12;
		PMHour = TRUE;
	}
	if (Hour == 0)
	{
		Hour = 12;
	}
	_itoa(Hour, TempString, 10);
	strcpy(TimeString, "    ");
	strcat(TimeString, TempString);
	strcat(TimeString, ":");
	_itoa(Minute, TempString, 10);
	if (Minute < 10)
	{
		strcat(TimeString, "0");
	}
	strcat(TimeString, TempString);
	strcat(TimeString, ":");
	_itoa(Second, TempString, 10);
	if (Second < 10)
	{
		strcat(TimeString, "0");
	}
	strcat(TimeString, TempString);
	if (PMHour)
	{
		strcat(TimeString, " PM");
	}
	else
	{
		strcat(TimeString, " AM");
	}

	// Draw the time
	TuiDrawText(UiScreenWidth-strlen(TimeString)-2, 2, TimeString, ATTR(UiTitleBoxFgColor, UiTitleBoxBgColor));
}

VOID TuiSaveScreen(PUCHAR Buffer)
{
	PUCHAR	ScreenMemory = (PUCHAR)TextVideoBuffer;
	ULONG		i;

	for (i=0; i < (UiScreenWidth * UiScreenHeight * 2); i++)
	{
		Buffer[i] = ScreenMemory[i];
	}
}

VOID TuiRestoreScreen(PUCHAR Buffer)
{
	PUCHAR	ScreenMemory = (PUCHAR)TextVideoBuffer;
	ULONG		i;

	for (i=0; i < (UiScreenWidth * UiScreenHeight * 2); i++)
	{
		ScreenMemory[i] = Buffer[i];
	}
}

VOID TuiMessageBox(PCSTR MessageText)
{
	PVOID	ScreenBuffer;

	// Save the screen contents
	ScreenBuffer = MmAllocateMemory(UiScreenWidth * UiScreenHeight * 2);
	TuiSaveScreen(ScreenBuffer);

	// Display the message box
	TuiMessageBoxCritical(MessageText);

	// Restore the screen contents
	TuiRestoreScreen(ScreenBuffer);
	MmFreeMemory(ScreenBuffer);
}

VOID TuiMessageBoxCritical(PCSTR MessageText)
{
	int		width = 8;
	unsigned int	height = 1;
	int		curline = 0;
	int		k;
	size_t		i , j;
	int		x1, x2, y1, y2;
	char	temp[260];
	char	key;

	// Find the height
	for (i=0; i<strlen(MessageText); i++)
	{
		if (MessageText[i] == '\n')
			height++;
	}

	// Find the width
	for (i=0,j=0,k=0; i<height; i++)
	{
		while ((MessageText[j] != '\n') && (MessageText[j] != 0))
		{
			j++;
			k++;
		}

		if (k > width)
			width = k;

		k = 0;
		j++;
	}

	// Calculate box area
	x1 = (UiScreenWidth - (width+2))/2;
	x2 = x1 + width + 3;
	y1 = ((UiScreenHeight - height - 2)/2) + 1;
	y2 = y1 + height + 4;

	// Draw the box
	TuiDrawBox(x1, y1, x2, y2, D_VERT, D_HORZ, TRUE, TRUE, ATTR(UiMessageBoxFgColor, UiMessageBoxBgColor));

	// Draw the text
	for (i=0,j=0; i<strlen(MessageText)+1; i++)
	{
		if ((MessageText[i] == '\n') || (MessageText[i] == 0))
		{
			temp[j] = 0;
			j = 0;
			UiDrawText(x1+2, y1+1+curline, temp, ATTR(UiMessageBoxFgColor, UiMessageBoxBgColor));
			curline++;
		}
		else
			temp[j++] = MessageText[i];
	}

	// Draw OK button
	strcpy(temp, "   OK   ");
	UiDrawText(x1+((x2-x1)/2)-3, y2-2, temp, ATTR(COLOR_BLACK, COLOR_GRAY));

	// Draw status text
	UiDrawStatusText("Press ENTER to continue");

	VideoCopyOffScreenBufferToVRAM();

	for (;;)
	{
		if (MachConsKbHit())
		{
			key = MachConsGetCh();
			if(key == KEY_EXTENDED)
				key = MachConsGetCh();

			if(key == KEY_ENTER)
				break;
			else if(key == KEY_SPACE)
				break;
			else if(key == KEY_ESC)
				break;
		}

		TuiUpdateDateTime();

		VideoCopyOffScreenBufferToVRAM();
	}

}


VOID TuiDrawProgressBarCenter(ULONG Position, ULONG Range, PCHAR ProgressText)
{
	ULONG		Left, Top, Right, Bottom;
	ULONG		Width = 50; // Allow for 50 "bars"
	ULONG		Height = 2;

	//
	// Is this the minimal UI?
	//
	if (UiMinimal)
	{
		//
		// Use alternate settings
		//
		Width = 80;
		Left = 0;
		Right = Left + Width;
		Top = UiScreenHeight - Height - 4;
		Bottom = Top + Height + 1;
	}
	else
	{
		Left = (UiScreenWidth - Width - 4) / 2;
		Right = Left + Width + 3;
		Top = (UiScreenHeight - Height - 2) / 2;
		Top += 2;
	}

	Bottom = Top + Height + 1;

	TuiDrawProgressBar(Left, Top, Right, Bottom, Position, Range, ProgressText);
}

VOID TuiDrawProgressBar(ULONG Left, ULONG Top, ULONG Right, ULONG Bottom, ULONG Position, ULONG Range, PCHAR ProgressText)
{
	ULONG		i;
	ULONG		ProgressBarWidth = (Right - Left) - 3;

	// First make sure the progress bar text fits
	UiTruncateStringEllipsis(ProgressText, ProgressBarWidth - 4);

	if (Position > Range)
	{
		Position = Range;
	}

    //
    // Minimal UI has no box, and only generic loading text
    //
    if (!UiMinimal)
    {
	    // Draw the box
	    TuiDrawBox(Left, Top, Right, Bottom, VERT, HORZ, TRUE, TRUE, ATTR(UiMenuFgColor, UiMenuBgColor));

    	//
        //  Draw the "Loading..." text
        //
	    TuiDrawCenteredText(Left + 2, Top + 2, Right - 2, Top + 2, ProgressText, ATTR(UiTextColor, UiMenuBgColor));
    }
    else
    {
    	//
        //  Draw the "Loading..." text
        //
        TuiDrawCenteredText(Left + 2, Top + 1, Right - 2, Top + 1, "ReactOS is loading files...", ATTR(7, 0));
    }

	// Draw the percent complete
	for (i=0; i<(Position*ProgressBarWidth)/Range; i++)
	{
		TuiDrawText(Left+2+i, Top+2, "\xDB", ATTR(UiTextColor, UiMenuBgColor));
	}

	// Draw the shadow for non-minimal UI
    if (!UiMinimal)
    {
	    for (; i<ProgressBarWidth; i++)
	    {
		    TuiDrawText(Left+2+i, Top+2, "\xB2", ATTR(UiTextColor, UiMenuBgColor));
	    }
    }

	TuiUpdateDateTime();
	VideoCopyOffScreenBufferToVRAM();
}

UCHAR TuiTextToColor(PCSTR ColorText)
{
	if (_stricmp(ColorText, "Black") == 0)
		return COLOR_BLACK;
	else if (_stricmp(ColorText, "Blue") == 0)
		return COLOR_BLUE;
	else if (_stricmp(ColorText, "Green") == 0)
		return COLOR_GREEN;
	else if (_stricmp(ColorText, "Cyan") == 0)
		return COLOR_CYAN;
	else if (_stricmp(ColorText, "Red") == 0)
		return COLOR_RED;
	else if (_stricmp(ColorText, "Magenta") == 0)
		return COLOR_MAGENTA;
	else if (_stricmp(ColorText, "Brown") == 0)
		return COLOR_BROWN;
	else if (_stricmp(ColorText, "Gray") == 0)
		return COLOR_GRAY;
	else if (_stricmp(ColorText, "DarkGray") == 0)
		return COLOR_DARKGRAY;
	else if (_stricmp(ColorText, "LightBlue") == 0)
		return COLOR_LIGHTBLUE;
	else if (_stricmp(ColorText, "LightGreen") == 0)
		return COLOR_LIGHTGREEN;
	else if (_stricmp(ColorText, "LightCyan") == 0)
		return COLOR_LIGHTCYAN;
	else if (_stricmp(ColorText, "LightRed") == 0)
		return COLOR_LIGHTRED;
	else if (_stricmp(ColorText, "LightMagenta") == 0)
		return COLOR_LIGHTMAGENTA;
	else if (_stricmp(ColorText, "Yellow") == 0)
		return COLOR_YELLOW;
	else if (_stricmp(ColorText, "White") == 0)
		return COLOR_WHITE;

	return COLOR_BLACK;
}

UCHAR TuiTextToFillStyle(PCSTR FillStyleText)
{
	if (_stricmp(FillStyleText, "Light") == 0)
	{
		return LIGHT_FILL;
	}
	else if (_stricmp(FillStyleText, "Medium") == 0)
	{
		return MEDIUM_FILL;
	}
	else if (_stricmp(FillStyleText, "Dark") == 0)
	{
		return DARK_FILL;
	}

	return LIGHT_FILL;
}

VOID TuiFadeInBackdrop(VOID)
{
	PPALETTE_ENTRY TuiFadePalette = NULL;

	if (UiUseSpecialEffects && ! MachVideoIsPaletteFixed())
	{
		TuiFadePalette = (PPALETTE_ENTRY)MmAllocateMemory(sizeof(PALETTE_ENTRY) * 64);

		if (TuiFadePalette != NULL)
		{
			VideoSavePaletteState(TuiFadePalette, 64);
			VideoSetAllColorsToBlack(64);
		}
	}

	// Draw the backdrop and title box
	TuiDrawBackdrop();

	if (UiUseSpecialEffects && ! MachVideoIsPaletteFixed() && TuiFadePalette != NULL)
	{
		VideoFadeIn(TuiFadePalette, 64);
		MmFreeMemory(TuiFadePalette);
	}
}

VOID TuiFadeOut(VOID)
{
	PPALETTE_ENTRY TuiFadePalette = NULL;

	if (UiUseSpecialEffects && ! MachVideoIsPaletteFixed())
	{
		TuiFadePalette = (PPALETTE_ENTRY)MmAllocateMemory(sizeof(PALETTE_ENTRY) * 64);

		if (TuiFadePalette != NULL)
		{
			VideoSavePaletteState(TuiFadePalette, 64);
		}
	}

	if (UiUseSpecialEffects && ! MachVideoIsPaletteFixed() && TuiFadePalette != NULL)
	{
		VideoFadeOut(64);
	}

	MachVideoSetDisplayMode(NULL, FALSE);

	if (UiUseSpecialEffects && ! MachVideoIsPaletteFixed() && TuiFadePalette != NULL)
	{
		VideoRestorePaletteState(TuiFadePalette, 64);
		MmFreeMemory(TuiFadePalette);
	}

}

BOOLEAN TuiEditBox(PCSTR MessageText, PCHAR EditTextBuffer, ULONG Length)
{
	int		width = 8;
	unsigned int	height = 1;
	int		curline = 0;
	int		k;
	size_t		i , j;
	int		x1, x2, y1, y2;
	char	temp[260];
	char	key;
	int		EditBoxLine;
	ULONG		EditBoxStartX, EditBoxEndX;
	int		EditBoxCursorX;
	unsigned int	EditBoxTextCount;
	int		EditBoxTextDisplayIndex;
	BOOLEAN	ReturnCode;
	PVOID	ScreenBuffer;

	// Save the screen contents
	ScreenBuffer = MmAllocateMemory(UiScreenWidth * UiScreenHeight * 2);
	TuiSaveScreen(ScreenBuffer);

	// Find the height
	for (i=0; i<strlen(MessageText); i++)
	{
		if (MessageText[i] == '\n')
			height++;
	}

	// Find the width
	for (i=0,j=0,k=0; i<height; i++)
	{
		while ((MessageText[j] != '\n') && (MessageText[j] != 0))
		{
			j++;
			k++;
		}

		if (k > width)
			width = k;

		k = 0;
		j++;
	}

	// Calculate box area
	x1 = (UiScreenWidth - (width+2))/2;
	x2 = x1 + width + 3;
	y1 = ((UiScreenHeight - height - 2)/2) + 1;
	y2 = y1 + height + 4;

	// Draw the box
	TuiDrawBox(x1, y1, x2, y2, D_VERT, D_HORZ, TRUE, TRUE, ATTR(UiMessageBoxFgColor, UiMessageBoxBgColor));

	// Draw the text
	for (i=0,j=0; i<strlen(MessageText)+1; i++)
	{
		if ((MessageText[i] == '\n') || (MessageText[i] == 0))
		{
			temp[j] = 0;
			j = 0;
			UiDrawText(x1+2, y1+1+curline, temp, ATTR(UiMessageBoxFgColor, UiMessageBoxBgColor));
			curline++;
		}
		else
			temp[j++] = MessageText[i];
	}

	EditBoxTextCount = 0;
	EditBoxLine = y2 - 2;
	EditBoxStartX = x1 + 3;
	EditBoxEndX = x2 - 3;
	UiFillArea(EditBoxStartX, EditBoxLine, EditBoxEndX, EditBoxLine, ' ', ATTR(UiEditBoxTextColor, UiEditBoxBgColor));

	// Show the cursor
	EditBoxCursorX = EditBoxStartX;
	MachVideoSetTextCursorPosition(EditBoxCursorX, EditBoxLine);
	MachVideoHideShowTextCursor(TRUE);

	// Draw status text
	UiDrawStatusText("Press ENTER to continue, or ESC to cancel");

	VideoCopyOffScreenBufferToVRAM();

	for (;;)
	{
		if (MachConsKbHit())
		{
			key = MachConsGetCh();
			if(key == KEY_EXTENDED)
			{
				key = MachConsGetCh();
			}

			if(key == KEY_ENTER)
			{
				ReturnCode = TRUE;
				break;
			}
			else if(key == KEY_ESC)
			{
				ReturnCode = FALSE;
				break;
			}
			else if (key == KEY_BACKSPACE) // Remove a character
			{
				if (EditBoxTextCount)
				{
					EditBoxTextCount--;
					EditTextBuffer[EditBoxTextCount] = 0;
				}
				else
				{
					beep();
				}
			}
			else // Add this key to the buffer
			{
				if (EditBoxTextCount < Length - 1)
				{
					EditTextBuffer[EditBoxTextCount] = key;
					EditBoxTextCount++;
					EditTextBuffer[EditBoxTextCount] = 0;
				}
				else
				{
					beep();
				}
			}
		}

		// Draw the edit box background
		UiFillArea(EditBoxStartX, EditBoxLine, EditBoxEndX, EditBoxLine, ' ', ATTR(UiEditBoxTextColor, UiEditBoxBgColor));

		// Fill the text in
		if (EditBoxTextCount > (EditBoxEndX - EditBoxStartX))
		{
			EditBoxTextDisplayIndex = EditBoxTextCount - (EditBoxEndX - EditBoxStartX);
			EditBoxCursorX = EditBoxEndX;
		}
		else
		{
			EditBoxTextDisplayIndex = 0;
			EditBoxCursorX = EditBoxStartX + EditBoxTextCount;
		}
		UiDrawText(EditBoxStartX, EditBoxLine, &EditTextBuffer[EditBoxTextDisplayIndex], ATTR(UiEditBoxTextColor, UiEditBoxBgColor));

		// Move the cursor
		MachVideoSetTextCursorPosition(EditBoxCursorX, EditBoxLine);

		TuiUpdateDateTime();

		VideoCopyOffScreenBufferToVRAM();
	}

	// Hide the cursor again
	MachVideoHideShowTextCursor(FALSE);

	// Restore the screen contents
	TuiRestoreScreen(ScreenBuffer);
	MmFreeMemory(ScreenBuffer);

	return ReturnCode;
}
