//=============================================================================
// Console - A quick little command line console that accepts most commands.
// Copyright 1998-2013 Epic Games, Inc. All Rights Reserved.
//=============================================================================
class Console extends Interaction
	within GameViewportClient
	transient
	config(Input)
	native(UserInterface);

// Constants.
const MaxHistory=16;		// # of command history to remember.

/** The player which the next console command should be executed in the context of.  If NULL, execute in the viewport. */
var LocalPlayer ConsoleTargetPlayer;

var Texture2D		DefaultTexture_Black;
var Texture2D		DefaultTexture_White;

/** The key which opens the console. */
var globalconfig name ConsoleKey;

/** The key which opens the typing bar. */
var globalconfig name TypeKey;

/**  Visible Console stuff */
var globalconfig int 	MaxScrollbackSize;

/** Holds the scrollback buffer */
var array<string> 		Scrollback;

/**  Where in the scrollback buffer are we */
var int 				SBHead, SBPos;

/** index into the History array for the latest command that was entered */
var config int HistoryTop;

/** index into the History array for the earliest command that was entered */
var config int HistoryBot;

/** the index of the current position in the History array */
var config int HistoryCur;

/** tracks previously entered console commands */
var config string History[MaxHistory];

/** tracks whether the user is using arrows keys to navigate the history, so that auto-complete doesn't override */
var transient bool bNavigatingHistory;

/** The command the user is currently typing. */
var string TypedStr;

var int TypedStrPos;						//Current position in TypedStr

/**
 * Indicates that InputChar events should be captured to prevent them from being passed on to other interactions.  Reset
 * when the another keydown event is received.
 */
var	transient	bool bCaptureKeyInput;

/** True while a control key is pressed. */
var bool bCtrl;

var config bool bEnableUI;

struct native AutoCompleteCommand
{
	var string		Command;
	var string		Desc;
};

/** Manual list of auto-complete commands and info specified in BaseInput.ini */
var config array<AutoCompleteCommand>	ManualAutoCompleteList;
/** Full list of auto-complete commands and info */
var transient array<AutoCompleteCommand>	AutoCompleteList;
/** Is the current auto-complete selection locked */
var transient bool 			bAutoCompleteLocked;
/** Currently selected auto complete index */
var transient int			AutoCompleteIndex;
/** Should the user be required to hold ctrl to use the up/down arrows when navigating auto-complete */
var config bool				bRequireCtrlToNavigateAutoComplete;
/** Do we need to rebuild auto complete? */
var transient bool			bIsRuntimeAutoCompleteUpToDate;

/** Node for storing an auto-complete tree based on each char in the command */
struct native AutoCompleteNode
{
	/** Char for node in the tree */
	var int IndexChar;
	/** Indicies into AutoCompleteList for commands that match to this level */
	var init array<int> AutoCompleteListIndices;
	/** Children for further matching */
	var init array<pointer> ChildNodes{FAutoCompleteNode};

structcpptext
{
	FAutoCompleteNode()
	{
		IndexChar = INDEX_NONE;
	}
	FAutoCompleteNode(INT NewChar)
	{
		IndexChar = NewChar;
	}
	~FAutoCompleteNode()
	{
		for (INT Idx = 0; Idx < ChildNodes.Num(); Idx++)
		{
			FAutoCompleteNode *Node = ChildNodes(Idx);
			delete Node;
		}
		ChildNodes.Empty();
	}
}
};
var native transient AutoCompleteNode AutoCompleteTree;

/** Current list of matching commands for auto-complete, @see UpdateCompleteIndices() */
var transient array<int> AutoCompleteIndices;

/**
 * Called when the Console is added to the GameViewportClient's Interactions array.
 */
function Initialized()
{
	Super.Initialized();
}

function SetInputText( string Text )
{
	TypedStr = Text;
}

function SetCursorPos( int Position )
{
	TypedStrPos = Position;
}

/**
* Searches console command history and removes any entries matching the specified command.
* @param Command - The command to search for and purge from the history.
*/
function PurgeCommandFromHistory(string Command)
{
	local int HistoryIdx, Idx, NextIdx;

	if ( (HistoryTop >= 0) && (HistoryTop < MaxHistory) )
	{
		for (HistoryIdx=0; HistoryIdx<MaxHistory; ++HistoryIdx)
		{
			if (History[HistoryIdx] ~= Command)
			{
				// from here to the top idx, shift everything back one
				Idx = HistoryIdx;
				NextIdx = (HistoryIdx + 1) % MaxHistory;

				while (Idx != HistoryTop)
				{
					History[Idx] = History[NextIdx];
					Idx = NextIdx;
					NextIdx = (NextIdx + 1) % MaxHistory;
				}

				// top index backs up one as well
				HistoryTop = (HistoryTop == 0) ? (MaxHistory - 1) : (HistoryTop - 1);
			}
		}
	}
}

/**
 * Executes a console command.
 * @param Command - The command to execute.
 */
function ConsoleCommand(string Command)
{
	// Store the command in the console history.
	if ((HistoryTop == 0) ? !(History[MaxHistory - 1] ~= Command) : !(History[HistoryTop - 1] ~= Command))
	{
		// ensure uniqueness
		PurgeCommandFromHistory(Command);

		History[HistoryTop] = Command;
		HistoryTop = (HistoryTop+1) % MaxHistory;

		if ( ( HistoryBot == -1) || ( HistoryBot == HistoryTop ) )
			HistoryBot = (HistoryBot+1) % MaxHistory;
	}
	HistoryCur = HistoryTop;

	// Save the command history to the INI.
	SaveConfig();

	OutputText("\n>>>" @ Command @ "<<<");

	if(ConsoleTargetPlayer != None)
	{
		// If there is a console target player, execute the command in the player's context.
		ConsoleTargetPlayer.Actor.ConsoleCommand(Command);
	}
	else if(GamePlayers.Length > 0 && GamePlayers[0].Actor != None)
	{
		// If there are any players, execute the command in the first players context.
		GamePlayers[0].Actor.ConsoleCommand(Command);
	}
	else
	{
		// Otherwise, execute the command in the context of the viewport.
		Outer.ConsoleCommand(Command);
	}
}

/**
 * Clears the console output buffer.
 */
function ClearOutput()
{
	SBHead = 0;
	ScrollBack.Remove(0,ScrollBack.Length);
}

/**
 * Prints a single line of text to the console.
 * @param Text - A line of text to display on the console.
 */
function OutputTextLine(coerce string Text)
{
	// If we are full, delete the first line
	if (ScrollBack.Length > MaxScrollbackSize)
	{
		ScrollBack.Remove(0,1);
		SBHead = MaxScrollBackSize-1;
	}
	else
		SBHead++;

	// Add the line
	ScrollBack.Length = ScrollBack.Length+1;
	ScrollBack[SBHead] = Text;
}

/**
 * Prints a (potentially multi-line) string of text to the console.
 * The text is split into separate lines and passed to OutputTextLine.
 * @param Text - Text to display on the console.
 */
event OutputText(coerce string Text)
{
	local string RemainingText;
	local int StringLength;
	local int LineLength;

	RemainingText = Text;
	StringLength = Len(Text);
	while(StringLength > 0)
	{
		// Find the number of characters in the next line of text.
		LineLength = InStr(RemainingText,"\n");
		if(LineLength == -1)
		{
			// There aren't any more newlines in the string, assume there's a newline at the end of the string.
			LineLength = StringLength;
		}

		// Output the line to the console.
		OutputTextLine(Left(RemainingText,LineLength));

		// Remove the line from the string.
		RemainingText = Mid(RemainingText,LineLength + 1);
		StringLength -= LineLength + 1;
	};
}

/**
 * Opens the typing bar with text already entered.
 * @param Text - The text to enter in the typing bar.
 */
function StartTyping(coerce string Text)
{
	GotoState('Typing');
	SetInputText(Text);
	SetCursorPos(Len(Text));
}

function PostRender_Console(Canvas Canvas);

/**
 * Process an input key event routed through unrealscript from another object. This method is assigned as the value for the
 * OnRecievedNativeInputKey delegate so that native input events are routed to this unrealscript function.
 *
 * @param	ControllerId	the controller that generated this input key event
 * @param	Key				the name of the key which an event occured for (KEY_Up, KEY_Down, etc.)
 * @param	EventType		the type of event which occured (pressed, released, etc.)
 * @param	AmountDepressed	for analog keys, the depression percent.
 *
 * @return	true to consume the key event, false to pass it on.
 */
function bool InputKey( int ControllerId, name Key, EInputEvent Event, float AmountDepressed = 1.f, bool bGamepad = FALSE )
{
	local PlayerController PC;
	if ( Event == IE_Pressed )
	{

		bCaptureKeyInput = false;

`if(`__TW_)
		if(Key == 'F10')
		{
			foreach class'Engine'.static.GetCurrentWorldInfo().LocalPlayerControllers(class'PlayerController', PC)
			{
				PC.ForceDisconnect();
			}
		}	
`endif

		if ( Key == ConsoleKey )
		{
			GotoState('Open');
			bCaptureKeyInput = true;
			return true;
		}
		else if ( Key == TypeKey )
		{
			GotoState('Typing');
			bCaptureKeyInput = true;
			return true;
		}
	}

	return bCaptureKeyInput;
}

`if(`__TW_)
function AttempDisconnect()
{

}
`endif

/**
 * Process a character input event (typing) routed through unrealscript from another object. This method is assigned as the value for the
 * OnRecievedNativeInputKey delegate so that native input events are routed to this unrealscript function.
 *
 * @param	ControllerId	the controller that generated this character input event
 * @param	Unicode			the character that was typed
 *
 * @return	True to consume the character, false to pass it on.
 */
function bool InputChar( int ControllerId, string Unicode )
{
	return bCaptureKeyInput;
}

/**
 * Clears out all pressed keys from the player's input object.
 */
function FlushPlayerInput()
{
	local PlayerController PC;

	if(ConsoleTargetPlayer != None)
	{
		PC = ConsoleTargetPlayer.Actor;
	}
	else if(GamePlayers.Length > 0 && GamePlayers[0].Actor != None)
	{
		// If there are any players, execute the command in the first players context.
		PC = GamePlayers[0].Actor;
	}

	if ( PC != None && PC.PlayerInput != None )
	{
		PC.PlayerInput.ResetInput();
	}
}

/** looks for Control key presses and the copy/paste combination that apply to both the console bar and the full open console */
function bool ProcessControlKey(name Key, EInputEvent Event)
{
	if (Key == 'LeftControl' || Key == 'RightControl')
	{
		if (Event == IE_Released)
		{
			bCtrl = false;
		}
		else if (Event == IE_Pressed)
		{
			bCtrl = true;
		}

		return true;
	}
	else if (bCtrl && Event == IE_Pressed && GamePlayers.length > 0 && GamePlayers[0].Actor != None)
	{
		if (Key == 'v')
		{
			// paste
			AppendInputText(GamePlayers[0].Actor.PasteFromClipboard());
			return true;
		}
		else if (Key == 'c')
		{
			// copy
			GamePlayers[0].Actor.CopyToClipboard(TypedStr);
			return true;
		}
		else if (Key == 'x')
		{
			// cut
			if (TypedStr != "")
			{
				GamePlayers[0].Actor.CopyToClipboard(TypedStr);
				SetInputText("");
				SetCursorPos(0);
			}
			return true;
		}
	}

	return false;
}

/** appends the specified text to the string of typed text */
function AppendInputText(string Text)
{
	local int Character;

	while (Len(Text) > 0)
	{
		Character = Asc(Left(Text, 1));
		Text = Mid(Text, 1);

		if (Character >= 0x20 && Character < 0x100)
		{
			SetInputText(Left(TypedStr, TypedStrPos) $ Chr(Character) $ Right(TypedStr, Len(TypedStr) - TypedStrPos));
			SetCursorPos(TypedStrPos + 1);
		}
	};
	UpdateCompleteIndices();
}

final native function BuildRuntimeAutoCompleteList(optional bool bForce);

native function UpdateCompleteIndices();

/**
 * This state is used when the typing bar is open.
 */
state Typing
{
	/**
	 * Process a character input event (typing) routed through unrealscript from another object. This method is assigned as the value for the
	 * OnRecievedNativeInputKey delegate so that native input events are routed to this unrealscript function.
	 *
	 * @param	ControllerId	the controller that generated this character input event
	 * @param	Unicode			the character that was typed
	 *
	 * @return	True to consume the character, false to pass it on.
	 */
	function bool InputChar( int ControllerId, string Unicode )
	{
		if ( bCaptureKeyInput )
		{
			return true;
		}

		AppendInputText(Unicode);

		return true;
	}

	/**
	 * Process an input key event routed through unrealscript from another object. This method is assigned as the value for the
	 * OnRecievedNativeInputKey delegate so that native input events are routed to this unrealscript function.
	 *
	 * @param	ControllerId	the controller that generated this input key event
	 * @param	Key				the name of the key which an event occured for (KEY_Up, KEY_Down, etc.)
	 * @param	EventType		the type of event which occured (pressed, released, etc.)
	 * @param	AmountDepressed	for analog keys, the depression percent.
	 *
	 * @return	true to consume the key event, false to pass it on.
	 */
	function bool InputKey( int ControllerId, name Key, EInputEvent Event, float AmountDepressed = 1.f, bool bGamepad = FALSE )
	{
		local string Temp;
		local int NewPos, SpacePos, PeriodPos;

		//`log(`location@`showvar(Key));

		if ( Event == IE_Pressed )
		{
			bCaptureKeyInput = false;
		}

		if (ProcessControlKey(Key, Event))
		{
			return true;
		}
		else if( bGamepad )
		{
			return FALSE;
		}
		else if( Key == 'Escape' && Event == IE_Released )
		{
			if( TypedStr!="" )
			{
				SetInputText("");
				SetCursorPos(0);
				HistoryCur = HistoryTop;
				return true;
			}
			else
			{
				GotoState( '' );
			}

			return true;
		}
		else if ( Key==ConsoleKey && Event == IE_Pressed )
		{
			GotoState('Open');
			bCaptureKeyInput = true;
			return true;
		}
		else if(Key == TypeKey && Event == IE_Pressed)
		{
			if (AutoCompleteIndices.Length > 0 && !bAutoCompleteLocked)
			{
				TypedStr = AutoCompleteList[AutoCompleteIndices[AutoCompleteIndex]].Command;
				SetCursorPos(Len(TypedStr));
				bAutoCompleteLocked = TRUE;
`if(`__TW_)
				// Prevent TypeKey from appending onto auto complete
				bCaptureKeyInput = true;
`endif
			}
			else
			{
				GotoState('');
				bCaptureKeyInput = true;
			}
			return true;
		}
		else if( Key=='Enter' && Event == IE_Released )
		{
			if( TypedStr!="" )
			{
				// Make a local copy of the string.
				Temp=TypedStr;

				SetInputText("");
				SetCursorPos(0);

				ConsoleCommand(Temp);

				//OutputText( Localize("Errors","Exec","Core") );

				OutputText( "" );
				GotoState('');

				UpdateCompleteIndices();
			}
			else
			{
				GotoState('');
			}

			return true;
		}
		else if( Global.InputKey( ControllerId, Key, Event, AmountDepressed, bGamepad ) )
		{
			return true;
		}
		else if( Event != IE_Pressed && Event != IE_Repeat )
		{
			if( !bGamepad )
			{
				return	Key != 'LeftMouseButton'
					&&	Key != 'MiddleMouseButton'
					&&	Key != 'RightMouseButton';
			}
			return FALSE;
		}
		else if( Key=='up' )
		{
			if (!bNavigatingHistory && ((bRequireCtrlToNavigateAutoComplete && bCtrl) || (!bRequireCtrlToNavigateAutoComplete && !bCtrl && AutoCompleteIndices.Length > 1)))
			{
				if (++AutoCompleteIndex == AutoCompleteIndices.Length)
				{
					AutoCompleteIndex = 0;
				}
			}
			else
			if ( HistoryBot >= 0 )
			{
				if (HistoryCur == HistoryBot)
					HistoryCur = HistoryTop;
				else
				{
					HistoryCur--;
					if (HistoryCur<0)
						HistoryCur = MaxHistory-1;
				}

				SetInputText(History[HistoryCur]);
				SetCursorPos(Len(History[HistoryCur]));
				UpdateCompleteIndices();
				bNavigatingHistory = TRUE;
			}
			return True;
		}
		else if( Key=='down' )
		{
			if (!bNavigatingHistory && ((bRequireCtrlToNavigateAutoComplete && bCtrl) || (!bRequireCtrlToNavigateAutoComplete && !bCtrl && AutoCompleteIndices.Length > 1)))
			{
				if (--AutoCompleteIndex < 0)
				{
					AutoCompleteIndex = AutoCompleteIndices.Length - 1;
				}
				bAutoCompleteLocked = FALSE;
			}
			else
			if ( HistoryBot >= 0 )
			{
				if (HistoryCur == HistoryTop)
					HistoryCur = HistoryBot;
				else
					HistoryCur = (HistoryCur+1) % MaxHistory;

				SetInputText(History[HistoryCur]);
				SetCursorPos(Len(History[HistoryCur]));
				UpdateCompleteIndices();
				bNavigatingHistory = TRUE;
			}

		}
		else if( Key=='backspace' )
		{
			if( TypedStrPos>0 )
			{
				SetInputText(Left(TypedStr,TypedStrPos-1) $ Right(TypedStr, Len(TypedStr) - TypedStrPos));
				SetCursorPos(TypedStrPos-1);
				// unlock auto-complete (@todo - track the lock position so we don't bother unlocking under bogus cases)
				bAutoCompleteLocked = FALSE;
			}

			return true;
		}
		else if ( Key=='delete' )
		{
			if ( TypedStrPos < Len(TypedStr) )
			{
				SetInputText(Left(TypedStr,TypedStrPos) $ Right(TypedStr, Len(TypedStr) - TypedStrPos - 1));
			}
			return true;
		}
		else if ( Key=='left' )
		{
			if (bCtrl)
			{
				// find the nearest '.' or ' '
				NewPos = Max(InStr(TypedStr,".",TRUE,FALSE,TypedStrPos),InStr(TypedStr," ",TRUE,FALSE,TypedStrPos));
				SetCursorPos(Max(0,NewPos));
			}
			else
			{
				SetCursorPos(Max(0, TypedStrPos - 1));
			}
			return true;
		}
		else if ( Key=='right' )
		{
			if (bCtrl)
			{
				// find the nearest '.' or ' '
				SpacePos = InStr(TypedStr," ",FALSE,FALSE,TypedStrPos+1);
				PeriodPos = InStr(TypedStr,".",FALSE,FALSE,TypedStrPos+1);
				// pick the closest valid index
				NewPos = SpacePos < 0 ? PeriodPos : (PeriodPos < 0 ? SpacePos : Min(SpacePos,PeriodPos));
				// jump to end if nothing in between
				if (NewPos == INDEX_NONE)
				{
					NewPos = Len(TypedStr);
				}
				SetCursorPos(Min(Len(TypedStr),Max(TypedStrPos,NewPos)));
			}
			else
			{
				SetCursorPos(Min(Len(TypedStr), TypedStrPos + 1));
			}
			return true;
		}
		else if ( Key=='home' )
		{
			SetCursorPos(0);
			return true;
		}
		else if ( Key=='end' )
		{
			SetCursorPos(Len(TypedStr));
			return true;
		}

		return TRUE;
	}

	event PostRender_Console(Canvas Canvas)
	{
		local float y, xl, yl, info_xl, info_yl, ClipX, ClipY, LeftPos;
		local string OutStr;
		local int MatchIdx, Idx, StartIdx;

		Global.PostRender_Console(Canvas);

		// Blank out a space

		// use the smallest font
		Canvas.Font	 = class'Engine'.Static.GetSmallFont();
		// determine the position for the cursor
		OutStr = "(>"@TypedStr;
		Canvas.Strlen(OutStr,xl,yl);

		ClipX = Canvas.ClipX;
		ClipY = Canvas.ClipY;
		LeftPos = 0;

		if (Class'WorldInfo'.Static.IsConsoleBuild())
		{
			ClipX	-= 32;
			ClipY	-= 32;
			LeftPos	 = 32;
		}

		// start at the bottom of the screen, then come up 6 pixels more than the height of the font
		Canvas.SetPos(LeftPos,ClipY-6-yl);
		// draw the background texture
		Canvas.DrawTile( DefaultTexture_Black, ClipX, yl+6,0,0,32,32);

		Canvas.SetPos(LeftPos,ClipY-6-yl);

		// change the draw color to green
		Canvas.SetDrawColor(0,255,0);

		// draw the top border of the typing region
		Canvas.DrawTile( DefaultTexture_White, ClipX, 2,0,0,32,32);

		// center the text between the bottom of the screen and the bottom of the border line
		Canvas.SetPos(LeftPos,ClipY-3-yl);
		Canvas.bCenter = False;
		Canvas.DrawText( OutStr, false );

		// draw the remaining text for matching auto-complete
		if (AutoCompleteIndices.Length > 0)
		{
			Idx = AutoCompleteIndices[AutoCompleteIndex];
			//Canvas.StrLen(OutStr,xl,yl);
			Canvas.SetPos(LeftPos+xl,ClipY-3-yl);
			Canvas.SetDrawColor(87,148,87);
			Canvas.DrawText(Right(AutoCompleteList[Idx].Command,Len(AutoCompleteList[Idx].Command) - Len(TypedStr)),FALSE);
			Canvas.StrLen("(>",xl,yl);

			StartIdx = AutoCompleteIndex - 5;
			if (StartIdx < 0)
			{
				StartIdx = Max(0,AutoCompleteIndices.Length + StartIdx);
			}
			Idx = StartIdx;
			y = ClipY - 6 - (yl * 2);
			for (MatchIdx = 0; MatchIdx < 10; MatchIdx++)
			{
				OutStr = AutoCompleteList[AutoCompleteIndices[Idx]].Desc;
				Canvas.StrLen(OutStr, info_xl, info_yl);
				y -= info_yl - yl;
				Canvas.SetPos(LeftPos + xl, y);
				Canvas.SetDrawColor(0, 0, 0);
				Canvas.DrawTile(DefaultTexture_White, info_xl, info_yl, 0, 0, 32, 32);
				Canvas.SetPos(LeftPos + xl, y);
				if (Idx == AutoCompleteIndex)
				{
					Canvas.SetDrawColor(0,255,0);
				}
				else
				{
					Canvas.SetDrawColor(0,150,0);
				}
				Canvas.DrawText(OutStr,false);
				if (++Idx >= AutoCompleteIndices.Length)
				{
					Idx = 0;
				}
				y -= yl;
				// break out if we loop on lists < 10
				if (Idx == StartIdx)
				{
					break;
				}
			}
			if (AutoCompleteIndices.Length >= 10)
			{
				OutStr = "[" $ (AutoCompleteIndices.Length - 10 + 1) @ "more matches]";
				Canvas.StrLen(OutStr, info_xl, info_yl);
				Canvas.SetPos(LeftPos + xl, y);
				Canvas.SetDrawColor(0, 0, 0);
				Canvas.DrawTile(DefaultTexture_White, info_xl, info_yl, 0, 0, 32, 32);
				Canvas.SetPos(LeftPos + xl, y);
				Canvas.SetDrawColor(0, 255, 0);
				Canvas.DrawText(OutStr, false);
			}
		}

		// determine the cursor position
		OutStr = "(>"@Left(TypedStr,TypedStrPos);
		Canvas.StrLen(OutStr,xl,yl);

		// move the pen to that position
		Canvas.SetPos(LeftPos + xl,ClipY-1-yl);

		// draw the cursor
		Canvas.DrawText("_");
	}

	event BeginState(Name PreviousStateName)
	{
		if ( PreviousStateName == '' )
		{
			FlushPlayerInput();
		}
		bCaptureKeyInput = true;
		HistoryCur = HistoryTop;
	}

	event EndState( Name NextStateName )
	{
		bAutoCompleteLocked = FALSE;
	}
}

/**
 * This state is used when the console is open.
 */
state Open
{
	function bool InputChar( int ControllerId, string Unicode )
	{
		if ( bCaptureKeyInput )
		{
			return true;
		}

		AppendInputText(Unicode);

		return true;
	}

//@HSL_BEGIN_XBOX
	function KeyboardInputComplete(bool bWasSuccessful)
	{
		local string Temp;
		local byte bWasCancelled;
		local OnlineSubsystem OnlineSub;
		
		OnlineSub = Class'GameEngine'.static.GetOnlineSubsystem();
		TypedStr = OnlineSub.PlayerInterface.GetKeyboardInputResults(bWasCancelled);

		if( TypedStr!="" )
		{
			// Make a local copy of the string.
			Temp=TypedStr;
			SetInputText("");
			SetCursorPos(0);

			if (Temp~="cls")
			{
				ClearOutput();
			}
			else
			{
				ConsoleCommand(Temp);
			}

			UpdateCompleteIndices();
		}

		GotoState('');
	}
//@HSL_END_XBOX

	/**
	 * Process an input key event routed through unrealscript from another object. This method is assigned as the value for the
	 * OnRecievedNativeInputKey delegate so that native input events are routed to this unrealscript function.
	 *
	 * @param	ControllerId	the controller that generated this input key event
	 * @param	Key				the name of the key which an event occured for (KEY_Up, KEY_Down, etc.)
	 * @param	EventType		the type of event which occured (pressed, released, etc.)
	 * @param	AmountDepressed	for analog keys, the depression percent.
	 *
	 * @return	true to consume the key event, false to pass it on.
	 */
	function bool InputKey( int ControllerId, name Key, EInputEvent Event, float AmountDepressed = 1.f, bool bGamepad = FALSE )
	{
		local string Temp;

		if (Event == IE_Pressed)
		{
			bCaptureKeyInput=false;
		}

		if (ProcessControlKey(Key, Event))
		{
			return true;
		}
		else if( bGamepad )
		{
			return FALSE;
		}
		else if( Key == 'Escape' && Event == IE_Released )
		{
			if( TypedStr!="" )
			{
				SetInputText("");
				SetCursorPos(0);
				HistoryCur = HistoryTop;
				return true;
			}
			else
			{
				GotoState( '' );
			}
		}
		else if( Key==ConsoleKey && Event == IE_Pressed )
		{
			GotoState('');
			bCaptureKeyInput = true;
			return true;
		}
		else if(Key == TypeKey && Event == IE_Pressed)
		{
			if (AutoCompleteIndices.Length > 0 && !bAutoCompleteLocked)
			{
				TypedStr = AutoCompleteList[AutoCompleteIndices[0]].Command;
				SetCursorPos(Len(TypedStr));
				bAutoCompleteLocked = TRUE;
`if(`__TW_)
				// Prevent TypeKey from appending onto auto complete
				bCaptureKeyInput = true;
`endif
			}
			else
			{
				GotoState('');
				bCaptureKeyInput = true;
			}
			return true;
		}
		else if( Key=='Enter' && Event == IE_Released )
		{
			if( TypedStr!="" )
			{
				// Make a local copy of the string.
				Temp=TypedStr;
				SetInputText("");
				SetCursorPos(0);

				if (Temp~="cls")
				{
					ClearOutput();
				}
				else
				{
					ConsoleCommand(Temp);
				}

				UpdateCompleteIndices();
			}

			return true;
		}
		else if( Global.InputKey( ControllerId, Key, Event, AmountDepressed, bGamepad ) )
		{
			return true;
		}
		else if( Event != IE_Pressed && Event != IE_Repeat )
		{
			if( !bGamepad )
			{
				return	Key != 'LeftMouseButton'
					&&	Key != 'MiddleMouseButton'
					&&	Key != 'RightMouseButton';
			}
			return FALSE;
		}
		else if( Key=='up' )
		{

			if (!bCtrl)
			{

				if ( HistoryBot >= 0 )
				{
					if (HistoryCur == HistoryBot)
						HistoryCur = HistoryTop;
					else
					{
						HistoryCur--;
						if (HistoryCur<0)
							HistoryCur = MaxHistory-1;
					}

					SetInputText(History[HistoryCur]);
					SetCursorPos(Len(History[HistoryCur]));
				}
			}
			else
			{
				if (SBPos<ScrollBack.Length-1)
				{
					SBPos++;

					if (SBPos>=ScrollBack.Length)
					  SBPos = ScrollBack.Length-1;
				}
			}
			return True;
		}
		else if( Key=='down' )
		{
			if (!bCtrl)
			{
				if ( HistoryBot >= 0 )
				{
					if (HistoryCur == HistoryTop)
						HistoryCur = HistoryBot;
					else
						HistoryCur = (HistoryCur+1) % MaxHistory;

					SetInputText(History[HistoryCur]);
					SetCursorPos(Len(History[HistoryCur]));
				}
			}
			else
			{
				if (SBPos>0)
				{
					SBPos--;
					if (SBPos<0)
						SBPos = 0;
				}

			}
			return true;
		}
		else if( Key=='backspace' )
		{
			if( TypedStrPos>0 )
			{
				SetInputText(Left(TypedStr,TypedStrPos-1) $ Right(TypedStr, Len(TypedStr) - TypedStrPos));
				SetCursorPos(TypedStrPos-1);
				// unlock auto-complete (@todo - track the lock position so we don't bother unlocking under bogus cases)
				bAutoCompleteLocked = FALSE;
			}

			return true;
		}
		else if ( Key=='delete' )
		{
			if ( TypedStrPos < Len(TypedStr) )
			{
				SetInputText(Left(TypedStr,TypedStrPos) $ Right(TypedStr, Len(TypedStr) - TypedStrPos - 1));
			}
			return true;
		}
		else if ( Key=='left' )
		{
			SetCursorPos(Max(0, TypedStrPos - 1));
			return true;
		}
		else if ( Key=='right' )
		{
			SetCursorPos(Min(Len(TypedStr), TypedStrPos + 1));
			return true;
		}
		else if (bCtrl && Key=='home')
		{
			SBPos=0;
		}
		else if ( Key=='home' )
		{
			SetCursorPos(0);
			return true;
		}
		else if (bCtrl && Key=='end')
		{
			SBPos = ScrollBack.Length-1;
		}
		else if ( Key=='end' )
		{
			SetCursorPos(Len(TypedStr));
			return true;
		}

		else if ( Key=='pageup' || Key=='mousescrollup')
		{
			if (SBPos<ScrollBack.Length-1)
			{
				if (bCtrl)
					SBPos+=5;
				else
					SBPos++;

				if (SBPos>=ScrollBack.Length)
				  SBPos = ScrollBack.Length-1;
			}

			return true;
		}
		else if ( Key=='pagedown' || Key=='mousescrolldown')
		{
			if (SBPos>0)
			{
				if (bCtrl)
					SBPos-=5;
				else
					SBPos--;

				if (SBPos<0)
					SBPos = 0;
			}

			return true;
		}


		return TRUE;
	}

	event PostRender_Console(Canvas Canvas)
	{

		local float Height;
		local float xl, yl, y, ScrollLineXL, ScrollLineYL, info_xl, info_yl;
		local string OutStr;
		local int idx, MatchIdx;

		// render the buffer

		// Blank out a space
		Canvas.Font = class'Engine'.Static.GetSmallFont();

		// the height of the buffer will be 75% of the height of the screen
		Height = Canvas.ClipY * 0.75;

		// change the draw color to white
		Canvas.SetDrawColor(255,255,255,255);

		// move the pen to the top-left pixel
		Canvas.SetPos(0,0);

		// draw the black background tile
		Canvas.DrawTile( DefaultTexture_Black, Canvas.ClipX, Height,0,0,32,32);

		// now render the typing region
		OutStr = "(>"@TypedStr;

		// determine the height of the text
		Canvas.Strlen(OutStr,xl,yl);

		// move the pen up + 12 pixels of buffer (for the green borders and some space)
		Canvas.SetPos(0,Height-12-yl);

		// change the draw color to green
		Canvas.SetDrawColor(0,255,0);

		// draw the top typing region border
		Canvas.DrawTile( DefaultTexture_White, Canvas.ClipX, 2,0,0,32,32);

		// move the pen to the bottom of the console buffer area
		Canvas.SetPos(0,Height);

		// draw the bottom typing region border
		Canvas.DrawTile( DefaultTexture_White, Canvas.ClipX, 2,0,0,32,32);

		// center the pen between the two borders
		Canvas.SetPos(0,Height-5-yl);
		Canvas.bCenter = False;

		// render the text that is being typed
		Canvas.DrawText( OutStr, false );

		// draw the remaining text for matching auto-complete
		if (AutoCompleteIndices.Length > 0)
		{
			Idx = AutoCompleteIndices[0];
			//Canvas.StrLen(OutStr,xl,yl);
			Canvas.SetPos(0+xl,Height-5-yl);
			Canvas.SetDrawColor(87,148,87);
			Canvas.DrawText(Right(AutoCompleteList[Idx].Command,Len(AutoCompleteList[Idx].Command) - Len(TypedStr)),FALSE);

			Canvas.StrLen("(>", xl, yl);
			y = Height + 5;
			for (MatchIdx = 0; MatchIdx < AutoCompleteIndices.Length && MatchIdx < 10; MatchIdx++)
			{
				Idx = AutoCompleteIndices[MatchIdx];
				Canvas.SetPos(0 + xl, y);
				Canvas.StrLen(AutoCompleteList[Idx].Desc, info_xl, info_yl);
				Canvas.SetDrawColor(0, 0, 0);
				Canvas.DrawTile(DefaultTexture_White, info_xl, info_yl, 0, 0, 32, 32);
				Canvas.SetPos(0 + xl, y);
				Canvas.SetDrawColor(0, 255, 0);
				Canvas.DrawText(AutoCompleteList[Idx].Desc, false);
				y += info_yl;
			}
		}

		OutStr = "(>"@Left(TypedStr,TypedStrPos);

		// position the pen at the cursor position
		Canvas.StrLen(OutStr,xl,yl);
		Canvas.SetPos(xl,Height-3-yl);

		// render the cursor
		Canvas.DrawText("_");

		// figure out which element of the scrollback buffer to should appear first (at the top of the screen)
		idx = SBHead - SBPos;
		y = Height-16-(yl*2);

		if (ScrollBack.Length==0)
			return;

		// change the draw color to white
		Canvas.SetDrawColor(255,255,255,255);

		// while we have enough room to draw another line and there are more lines to draw
		while (y>yl && idx>=0)
		{
			// move the pen to the correct position
			Canvas.SetPos(0, y);

			// adjust the location for any word wrapping due to long text lines
			Canvas.StrLen(ScrollBack[idx], ScrollLineXL, ScrollLineYL);
			if (ScrollLineYL > yl)
			{
				y -= (ScrollLineYL - yl);
				Canvas.SetPos(Canvas.CurX, y, Canvas.CurZ);
			}

			// draw the next line down in the buffer
			Canvas.DrawText(Scrollback[idx],false);
			idx--;
			y-=yl;
		}
	}

	event BeginState(Name PreviousStateName)
	{
//@HSL_BEGIN_XBOX
		local OnlineSubsystem OnlineSub;
		
		bCaptureKeyInput = true;
		HistoryCur = HistoryTop;

		SBPos = 0;
		bCtrl = false;

		OnlineSub = Class'GameEngine'.static.GetOnlineSubsystem();
		OnlineSub.PlayerInterface.AddKeyboardInputDoneDelegate(KeyboardInputComplete);

		OnlineSub.PlayerInterface.ShowKeyboardUI(0, "Console Input", "Input console commands");

		if ( PreviousStateName == '' )
		{
			FlushPlayerInput();
		}
	}

	event EndState( Name NextStateName )
	{
		local OnlineSubsystem OnlineSub;

		OnlineSub = Class'GameEngine'.static.GetOnlineSubsystem();

		OnlineSub.PlayerInterface.HideKeyboardUI(0);
		OnlineSub.PlayerInterface.ClearKeyboardInputDoneDelegate(KeyboardInputComplete);
	}
//@HSL_END_XBOX
}

defaultproperties
{
	OnReceivedNativeInputKey=InputKey
	OnReceivedNativeInputChar=InputChar

	DefaultTexture_Black=Texture2D'EngineResources.Black'
	DefaultTexture_White=Texture2D'EngineResources.WhiteSquareTexture'
}
