enum EWidgetPlacementType
{
	DOCKED = 0,
	FLOATING = 1,
}

enum EScreenRatio
{
	Screen_21x9 = 0,
	Screen_9x21 = 1,
	Screen_9x16 = 2,
	Screen_3x4 = 3,
	Screen_4x3 = 4,
	Screen_1x1 = 5,
}

enum ELayoutType
{
	Generic = 0,
	Militech = 1,
	Arasaka = 2,
	Zetatech = 3,
}

class UIRefreshedEvent extends Event
{
}

class UIUnstreamedEvent extends Event
{
}

class AsyncSpawnData extends IScriptable
{
	var m_callbackTarget : weak< IScriptable >;
	var m_controller : weak< IScriptable >;
	var m_functionName : CName;
	var m_libraryID : CName;
	var m_widgetData : Variant;

	public function Initialize( callbackTarget : weak< IScriptable >, functionName : CName, widgetData : Variant, optional controller : weak< IScriptable > )
	{
		m_callbackTarget = callbackTarget;
		m_functionName = functionName;
		m_widgetData = widgetData;
		m_controller = controller;
	}

}

class WidgetCustomData extends IScriptable
{
}

class ImageButtonCustomData extends WidgetCustomData
{
	var imageAtlasImageID : CName;
	var additionalText : String;
}

class DoorWidgetCustomData extends WidgetCustomData
{
	private var passcode : Int32;
	private var card : CName;
	private var isPasswordKnown : Bool;

	public const function GetPasscode() : Int32
	{
		return passcode;
	}

	public function SetPasscode( newCode : Int32 )
	{
		passcode = newCode;
	}

	public const function GetCardName() : CName
	{
		return card;
	}

	public function SetCardName( cardName : CName )
	{
		card = cardName;
	}

	public function IsPasswordKnown() : Bool
	{
		return isPasswordKnown;
	}

	public function SetIsPasswordKnown( choice : Bool )
	{
		isPasswordKnown = choice;
	}

}

class LiftWidgetCustomData extends WidgetCustomData
{
	private var movementState : gamePlatformMovementState;

	public function GetMovementState() : gamePlatformMovementState
	{
		return movementState;
	}

	public function SetMovementState( value : gamePlatformMovementState )
	{
		movementState = value;
	}

}

class UIActionEvent extends Event
{
	var action : DeviceAction;
	var executor : weak< GameObject >;
}

struct ScreenDefinitionPackage
{
	var screenDefinition : DeviceUIDefinition_Record;
	var style : WidgetStyle_Record;
}

struct SUIScreenDefinition
{
	[ customEditor = "TweakDBGroupInheritance;DeviceUIDefinition" ]
	instanceeditable var screenDefinition : TweakDBID;
	[ customEditor = "TweakDBGroupInheritance;WidgetStyle" ]
	instanceeditable var style : TweakDBID;
}

struct SWidgetPackageBase
{
	var libraryPath : ResRef;
	var libraryID : CName;
	var widgetTweakDBID : TweakDBID;
	var widget : weak< inkWidget >;
	var widgetName : String;
	var placement : EWidgetPlacementType;
	var orientation : inkEOrientation;
	var isValid : Bool;
	default isValid = true;

	public static function ResolveWidgetTweakDBData( widgetTweakDBID : TweakDBID, out id : CName, out path : ResRef ) : Bool
	{
		var record : WidgetDefinition_Record;
		if( TDBID.IsValid( widgetTweakDBID ) )
		{
			record = TweakDBInterface.GetWidgetDefinitionRecord( widgetTweakDBID );
			if( record != NULL )
			{
				path = record.LibraryPath();
				id = StringToName( record.LibraryID() );
				return true;
			}
		}
		return false;
	}

	public static function GetLibraryPath( widgetDef : WidgetDefinition_Record ) : ResRef
	{
		return widgetDef.LibraryPath();
	}

	public static function GetLibraryID( widgetDef : WidgetDefinition_Record, screenTypeDef : DeviceScreenType_Record, styleDef : WidgetStyle_Record ) : String
	{
		var i : Int32;
		var id : String;
		var styles : array< weak< WidgetStyle_Record > >;
		var ratios : array< weak< WidgetRatio_Record > >;
		var contentRatio : weak< WidgetRatio_Record >;
		var expectedRatio : String;
		if( !( widgetDef ) )
		{
			return id;
		}
		id = widgetDef.LibraryID();
		widgetDef.Styles( styles );
		widgetDef.Ratios( ratios );
		for( i = 0; i < styles.Size(); i += 1 )
		{
			if( styleDef.Type() == styles[ i ].Type() )
			{
				id += ( "_" + styles[ i ].EnumName() );
			}
		}
		if( screenTypeDef )
		{
			if( widgetDef.UseContentRatio() )
			{
				contentRatio = screenTypeDef.ContentRatio();
			}
			if( contentRatio )
			{
				expectedRatio = contentRatio.EnumName();
			}
			else
			{
				expectedRatio = screenTypeDef.Ratio().EnumName();
			}
			for( i = 0; i < ratios.Size(); i += 1 )
			{
				if( expectedRatio == ratios[ i ].EnumName() )
				{
					id += ( "_" + ratios[ i ].EnumName() );
				}
			}
		}
		return id;
	}

	public static function GetLibraryIDPackage( widgetDef : WidgetDefinition_Record, screenTypeDef : DeviceScreenType_Record, styleDef : WidgetStyle_Record ) : array< String >
	{
		var i : Int32;
		var styles : array< weak< WidgetStyle_Record > >;
		var ratios : array< weak< WidgetRatio_Record > >;
		var contentRatio : weak< WidgetRatio_Record >;
		var idPackage : array< String >;
		var idpart1 : String;
		var idpart2 : String;
		var idpart3 : String;
		var fullID : String;
		var expectedRatio : String;
		if( !( widgetDef ) )
		{
			return idPackage;
		}
		idpart1 = widgetDef.LibraryID();
		widgetDef.Styles( styles );
		widgetDef.Ratios( ratios );
		fullID = idpart1;
		for( i = 0; i < styles.Size(); i += 1 )
		{
			if( styleDef.Type() == styles[ i ].Type() )
			{
				idpart2 = styles[ i ].EnumName();
				fullID += ( "_" + idpart2 );
				break;
			}
		}
		if( screenTypeDef )
		{
			if( widgetDef.UseContentRatio() )
			{
				contentRatio = screenTypeDef.ContentRatio();
			}
			if( contentRatio )
			{
				expectedRatio = contentRatio.EnumName();
			}
			else
			{
				expectedRatio = screenTypeDef.Ratio().EnumName();
			}
			for( i = 0; i < ratios.Size(); i += 1 )
			{
				if( expectedRatio == ratios[ i ].EnumName() )
				{
					idpart3 = ratios[ i ].EnumName();
					fullID += ( "_" + idpart3 );
					break;
				}
			}
		}
		idPackage.PushBack( fullID );
		if( IsStringValid( idpart2 ) )
		{
			idPackage.PushBack( ( idpart1 + "_" ) + idpart2 );
		}
		if( IsStringValid( idpart3 ) )
		{
			idPackage.PushBack( ( idpart1 + "_" ) + idpart3 );
		}
		idPackage.PushBack( idpart1 );
		return idPackage;
	}

}

struct SWidgetPackage extends SWidgetPackageBase
{
	var displayName : String;
	var ownerID : PersistentID;
	var ownerIDClassName : CName;
	var customData : WidgetCustomData;
	var isWidgetInactive : Bool;
	var widgetState : EWidgetState;
	var iconID : CName;
	var bckgroundTextureID : TweakDBID;
	var iconTextureID : TweakDBID;
	var textData : inkTextParams;
}

enum EWidgetState
{
	DEFAULT = 0,
	ON = 1,
	OFF = 2,
	INACTIVE = 3,
	ALLOWED = 4,
	LOCKED = 5,
	SEALED = 6,
}

struct SActionWidgetPackage extends SWidgetPackage
{
	var action : DeviceAction;
	var wasInitalized : Bool;
	var dependendActions : array< DeviceAction >;
}

struct SThumbnailWidgetPackage extends SWidgetPackage
{
	var thumbnailAction : ThumbnailUI;
	var deviceStatus : String;
}

struct SDeviceWidgetPackage extends SWidgetPackage
{
	var deviceStatus : String;
	var deviceState : EDeviceStatus;
	var actionWidgets : array< SActionWidgetPackage >;
}

struct SBreadcrumbElementData
{
	var elementName : String;
	var elementID : Int32;
}

class DeviceInkGameControllerBase extends inkGameController
{
	[ category = "Animations" ]
	protected editable inlined var m_animationManager : WidgetAnimationManager;
	protected var m_rootWidget : weak< inkCanvas >;
	protected var m_actionWidgetsData : array< SActionWidgetPackage >;
	protected var m_deviceWidgetsData : array< SDeviceWidgetPackage >;
	protected var m_breadcrumbStack : array< SBreadcrumbElementData >;
	protected var m_cashedState : EDeviceStatus;
	protected var m_isInitialized : Bool;
	protected var m_hasUICameraZoom : Bool;
	protected var m_activeBreadcrumb : SBreadcrumbElementData;
	private var m_onRefreshListener : CallbackHandle;
	private var m_onActionWidgetsUpdateListener : CallbackHandle;
	private var m_onDeviceWidgetsUpdateListener : CallbackHandle;
	private var m_onBreadcrumbBarUpdateListener : CallbackHandle;
	protected var m_bbCallbacksRegistered : Bool;

	protected event OnInitialize()
	{
		m_rootWidget = ( ( inkCanvas )( GetRootWidget() ) );
		RegisterBlackboardCallbacks( GetBlackboard() );
		m_hasUICameraZoom = GetOwner().GetDevicePS().HasUICameraZoom();
		SetupWidgets();
		m_isInitialized = true;
		if( GetOwner().IsReadyForUI() )
		{
			Refresh( GetOwner().GetDeviceState() );
		}
	}

	protected event OnUninitialize()
	{
		UpdateUnstreamedUI();
		if( m_bbCallbacksRegistered )
		{
			UnRegisterBlackboardCallbacks( GetBlackboard() );
		}
	}

	protected virtual function SetupWidgets() {}

	protected virtual function GetOwner() : Device
	{
		return ( ( Device )( GetOwnerEntity() ) );
	}

	public function IsOwnerFactInvoker() : Bool
	{
		return ( ( FactInvoker )( GetOwner() ) );
	}

	public const function GetDeviceState() : EDeviceStatus
	{
		return m_cashedState;
	}

	protected function GetBlackboard() : IBlackboard
	{
		var device : Device;
		device = GetOwner();
		if( device )
		{
			return device.GetBlackboard();
		}
		else
		{
			return NULL;
		}
	}

	public function GetScreenDefinition() : ScreenDefinitionPackage
	{
		return GetOwner().GetScreenDefinition();
	}

	protected function CreateActionWidget( parentWidget : weak< inkWidget >, const widgetData : ref< SActionWidgetPackage > ) : weak< inkWidget >
	{
		var widget : inkWidget;
		var screenDef : ScreenDefinitionPackage;
		screenDef = GetScreenDefinition();
		widget = FindWidgetInLibrary( parentWidget, TweakDBInterface.GetWidgetDefinitionRecord( widgetData.widgetTweakDBID ), screenDef.screenDefinition.TerminalScreenType(), screenDef.style, widgetData.libraryID, widgetData.libraryPath );
		if( widget != NULL )
		{
			widget.SetSizeRule( inkESizeRule.Stretch );
			widget.SetHAlign( inkEHorizontalAlign.Fill );
		}
		return widget;
	}

	protected function CreateActionWidgetAsync( parentWidget : weak< inkWidget >, widgetData : SActionWidgetPackage )
	{
		var screenDef : ScreenDefinitionPackage;
		var spawnData : AsyncSpawnData;
		if( HasActionWidgetData( widgetData ) )
		{
			return;
		}
		screenDef = GetScreenDefinition();
		spawnData = new AsyncSpawnData;
		spawnData.Initialize( this, 'OnActionWidgetSpawned', widgetData, this );
		widgetData.libraryID = RequestWidgetFromLibrary( parentWidget, TweakDBInterface.GetWidgetDefinitionRecord( widgetData.widgetTweakDBID ), screenDef.screenDefinition.TerminalScreenType(), screenDef.style, widgetData.libraryID, widgetData.libraryPath, spawnData );
		AddActionWidgetData( widgetData );
	}

	protected event OnActionWidgetSpawned( widget : inkWidget, userData : IScriptable )
	{
		var spawnData : AsyncSpawnData;
		var widgetData : SActionWidgetPackage;
		if( widget != NULL )
		{
			widget.SetSizeRule( inkESizeRule.Stretch );
			widget.SetHAlign( inkEHorizontalAlign.Fill );
		}
		spawnData = ( ( AsyncSpawnData )( userData ) );
		if( spawnData != NULL )
		{
			widgetData = ( ( SActionWidgetPackage )spawnData.m_widgetData );
			widgetData.widget = widget;
			widgetData.libraryID = spawnData.m_libraryID;
			UpdateActionWidgetData( widgetData, GetActionWidgetDataIndex( widgetData ) );
			InitializeActionWidget( widget, widgetData );
		}
	}

	protected function GetActionWidget( const widgetData : ref< SActionWidgetPackage > ) : weak< inkWidget >
	{
		var index : Int32;
		index = GetActionWidgetDataIndex( widgetData );
		if( ( index >= 0 ) && ( index < m_actionWidgetsData.Size() ) )
		{
			return m_actionWidgetsData[ index ].widget;
		}
		return NULL;
	}

	protected function UpdateActionWidgetData( const widgetData : ref< SActionWidgetPackage >, index : Int32 )
	{
		if( ( index >= 0 ) && ( index < m_actionWidgetsData.Size() ) )
		{
			m_actionWidgetsData[ index ] = widgetData;
		}
	}

	protected function GetActionWidgetDataIndex( widgetData : SActionWidgetPackage ) : Int32
	{
		var i : Int32;
		var screenDef : ScreenDefinitionPackage;
		screenDef = GetScreenDefinition();
		widgetData.libraryID = GetCurrentFullLibraryID( TweakDBInterface.GetWidgetDefinitionRecord( widgetData.widgetTweakDBID ), screenDef.screenDefinition.TerminalScreenType(), screenDef.style );
		for( i = 0; i < m_actionWidgetsData.Size(); i += 1 )
		{
			if( ( ( ( m_actionWidgetsData[ i ].ownerID == widgetData.ownerID && m_actionWidgetsData[ i ].widgetName == widgetData.widgetName ) && ( m_actionWidgetsData[ i ].widgetTweakDBID == widgetData.widgetTweakDBID ) ) && m_actionWidgetsData[ i ].libraryPath == widgetData.libraryPath ) && m_actionWidgetsData[ i ].libraryID == widgetData.libraryID )
			{
				return i;
			}
		}
		return -1;
	}

	protected function HasActionWidgetData( const widgetData : ref< SActionWidgetPackage > ) : Bool
	{
		return GetActionWidgetDataIndex( widgetData ) >= 0;
	}

	protected function HasActionWidget( const widgetData : ref< SActionWidgetPackage > ) : Bool
	{
		return GetActionWidget( widgetData ) != NULL;
	}

	protected function AddActionWidgetData( const widgetData : ref< SActionWidgetPackage > )
	{
		m_actionWidgetsData.PushBack( widgetData );
	}

	protected function AddActionWidget( widget : inkWidget, widgetData : SActionWidgetPackage ) : weak< inkWidget >
	{
		var screenDef : ScreenDefinitionPackage;
		screenDef = GetScreenDefinition();
		widgetData.libraryID = GetCurrentFullLibraryID( TweakDBInterface.GetWidgetDefinitionRecord( widgetData.widgetTweakDBID ), screenDef.screenDefinition.TerminalScreenType(), screenDef.style );
		widgetData.widget = widget;
		m_actionWidgetsData.PushBack( widgetData );
		return widgetData.widget;
	}

	protected function HideActionWidgets()
	{
		var i : Int32;
		for( i = 0; i < m_actionWidgetsData.Size(); i += 1 )
		{
			if( m_actionWidgetsData[ i ].widget != NULL )
			{
				m_actionWidgetsData[ i ].widget.SetVisible( false );
			}
		}
	}

	protected function InitializeActionWidget( widget : inkWidget, const widgetData : ref< SActionWidgetPackage > )
	{
		var controller : DeviceActionWidgetControllerBase;
		controller = ( ( DeviceActionWidgetControllerBase )( widget.GetController() ) );
		if( controller != NULL )
		{
			controller.Initialize( this, widgetData );
		}
		widget.SetVisible( true );
	}

	protected function CreateDeviceWidgetAsync( parentWidget : weak< inkWidget >, widgetData : SDeviceWidgetPackage )
	{
		var screenDef : ScreenDefinitionPackage;
		var spawnData : AsyncSpawnData;
		if( HasDeviceWidgetData( widgetData ) )
		{
			return;
		}
		screenDef = GetScreenDefinition();
		spawnData = new AsyncSpawnData;
		spawnData.Initialize( this, 'OnDeviceWidgetSpawned', widgetData, this );
		widgetData.libraryID = RequestWidgetFromLibrary( parentWidget, TweakDBInterface.GetWidgetDefinitionRecord( widgetData.widgetTweakDBID ), screenDef.screenDefinition.TerminalScreenType(), screenDef.style, widgetData.libraryID, widgetData.libraryPath, spawnData );
		AddDeviceWidgetData( widgetData );
	}

	protected event OnDeviceWidgetSpawned( widget : inkWidget, userData : IScriptable )
	{
		var spawnData : AsyncSpawnData;
		var widgetData : SDeviceWidgetPackage;
		spawnData = ( ( AsyncSpawnData )( userData ) );
		if( spawnData != NULL )
		{
			widgetData = ( ( SDeviceWidgetPackage )spawnData.m_widgetData );
			widgetData.widget = widget;
			widgetData.libraryID = spawnData.m_libraryID;
			widget.SetAnchor( inkEAnchor.Fill );
			widget.SetSizeRule( inkESizeRule.Stretch );
			UpdateDeviceWidgetData( widgetData, GetDeviceWidgetDataIndex( widgetData ) );
			InitializeDeviceWidget( widget, widgetData );
		}
	}

	protected function CreateDeviceWidget( parentWidget : weak< inkWidget >, const widgetData : ref< SDeviceWidgetPackage > ) : weak< inkWidget >
	{
		var widget : inkWidget;
		var screenDef : ScreenDefinitionPackage;
		screenDef = GetScreenDefinition();
		widget = FindWidgetInLibrary( parentWidget, TweakDBInterface.GetWidgetDefinitionRecord( widgetData.widgetTweakDBID ), screenDef.screenDefinition.TerminalScreenType(), screenDef.style, widgetData.libraryID, widgetData.libraryPath );
		return widget;
	}

	protected function UpdateDeviceWidgetData( const widgetData : ref< SDeviceWidgetPackage >, index : Int32 )
	{
		if( ( index >= 0 ) && ( index < m_deviceWidgetsData.Size() ) )
		{
			m_deviceWidgetsData[ index ] = widgetData;
		}
	}

	protected function GetDeviceWidgetDataIndex( widgetData : SDeviceWidgetPackage ) : Int32
	{
		var i : Int32;
		var screenDef : ScreenDefinitionPackage;
		screenDef = GetScreenDefinition();
		widgetData.libraryID = GetCurrentFullLibraryID( TweakDBInterface.GetWidgetDefinitionRecord( widgetData.widgetTweakDBID ), screenDef.screenDefinition.TerminalScreenType(), screenDef.style );
		for( i = 0; i < m_deviceWidgetsData.Size(); i += 1 )
		{
			if( ( ( ( m_deviceWidgetsData[ i ].ownerID == widgetData.ownerID && m_deviceWidgetsData[ i ].widgetName == widgetData.widgetName ) && ( m_deviceWidgetsData[ i ].widgetTweakDBID == widgetData.widgetTweakDBID ) ) && m_deviceWidgetsData[ i ].libraryPath == widgetData.libraryPath ) && m_deviceWidgetsData[ i ].libraryID == widgetData.libraryID )
			{
				return i;
			}
		}
		return -1;
	}

	protected function GetDeviceWidget( const widgetData : ref< SDeviceWidgetPackage > ) : weak< inkWidget >
	{
		var index : Int32;
		index = GetDeviceWidgetDataIndex( widgetData );
		if( ( index >= 0 ) && ( index < m_deviceWidgetsData.Size() ) )
		{
			return m_deviceWidgetsData[ index ].widget;
		}
		return NULL;
	}

	protected function HasDeviceWidgetData( const widgetData : ref< SDeviceWidgetPackage > ) : Bool
	{
		return GetDeviceWidgetDataIndex( widgetData ) >= 0;
	}

	protected function HasDeviceWidget( const widgetData : ref< SDeviceWidgetPackage > ) : Bool
	{
		return GetDeviceWidget( widgetData ) != NULL;
	}

	protected function AddDeviceWidgetData( const widgetData : ref< SDeviceWidgetPackage > )
	{
		m_deviceWidgetsData.PushBack( widgetData );
	}

	protected function AddDeviceWidget( widget : inkWidget, widgetData : SDeviceWidgetPackage ) : weak< inkWidget >
	{
		var screenDef : ScreenDefinitionPackage;
		screenDef = GetScreenDefinition();
		widgetData.libraryID = GetCurrentFullLibraryID( TweakDBInterface.GetWidgetDefinitionRecord( widgetData.widgetTweakDBID ), screenDef.screenDefinition.TerminalScreenType(), screenDef.style );
		widgetData.widget = widget;
		m_deviceWidgetsData.PushBack( widgetData );
		return widgetData.widget;
	}

	protected function InitializeDeviceWidget( widget : inkWidget, const widgetData : ref< SDeviceWidgetPackage > )
	{
		var controller : DeviceWidgetControllerBase;
		controller = ( ( DeviceWidgetControllerBase )( widget.GetController() ) );
		if( controller != NULL )
		{
			controller.Initialize( this, widgetData );
		}
		widget.SetVisible( true );
	}

	protected function HideDeviceWidgets()
	{
		var i : Int32;
		for( i = 0; i < m_deviceWidgetsData.Size(); i += 1 )
		{
			if( m_deviceWidgetsData[ i ].widget != NULL )
			{
				m_deviceWidgetsData[ i ].widget.SetVisible( false );
			}
		}
	}

	public function GetCurrentFullLibraryID( widgetRecord : WidgetDefinition_Record, optional screenTypeRecord : DeviceScreenType_Record, optional styleRecord : WidgetStyle_Record ) : CName
	{
		return FindLibraryID( widgetRecord, screenTypeRecord, styleRecord );
	}

	public virtual function UpdateActionWidgets( const widgetsData : ref< array< SActionWidgetPackage > > )
	{
		HideActionWidgets();
	}

	public virtual function UpdateDeviceWidgets( const widgetsData : ref< array< SDeviceWidgetPackage > > )
	{
		HideDeviceWidgets();
	}

	public virtual function UpdateBreadCrumbBar( const data : ref< SBreadCrumbUpdateData > ) {}

	public virtual function Refresh( state : EDeviceStatus )
	{
		m_cashedState = state;
	}

	protected virtual function RegisterBlackboardCallbacks( blackboard : IBlackboard )
	{
		if( blackboard )
		{
			m_onRefreshListener = blackboard.RegisterListenerBool( GetOwner().GetBlackboardDef().UIupdate, this, 'OnRefresh' );
			m_onActionWidgetsUpdateListener = blackboard.RegisterListenerVariant( GetOwner().GetBlackboardDef().ActionWidgetsData, this, 'OnActionWidgetsUpdate' );
			m_onDeviceWidgetsUpdateListener = blackboard.RegisterListenerVariant( GetOwner().GetBlackboardDef().DeviceWidgetsData, this, 'OnDeviceWidgetsUpdate' );
			m_onBreadcrumbBarUpdateListener = blackboard.RegisterListenerVariant( GetOwner().GetBlackboardDef().BreadCrumbElement, this, 'OnBreadcrumbBarUpdate' );
			m_bbCallbacksRegistered = true;
		}
	}

	protected virtual function UnRegisterBlackboardCallbacks( blackboard : IBlackboard )
	{
		if( blackboard )
		{
			blackboard.UnregisterListenerBool( GetOwner().GetBlackboardDef().UIupdate, m_onRefreshListener );
			blackboard.UnregisterListenerVariant( GetOwner().GetBlackboardDef().ActionWidgetsData, m_onActionWidgetsUpdateListener );
			blackboard.UnregisterListenerVariant( GetOwner().GetBlackboardDef().DeviceWidgetsData, m_onDeviceWidgetsUpdateListener );
			blackboard.UnregisterListenerVariant( GetOwner().GetBlackboardDef().BreadCrumbElement, m_onBreadcrumbBarUpdateListener );
			m_bbCallbacksRegistered = false;
		}
	}

	protected event OnRefresh( value : Bool )
	{
		var evt : UIRefreshedEvent;
		if( m_isInitialized )
		{
			Refresh( GetOwner().GetDeviceState() );
			evt = new UIRefreshedEvent;
			GetOwner().QueueEvent( evt );
		}
	}

	protected event OnBreadcrumbBarUpdate( value : Variant )
	{
		var data : SBreadCrumbUpdateData;
		data = ( ( SBreadCrumbUpdateData )value );
		UpdateBreadCrumbBar( data );
	}

	protected event OnActionWidgetsUpdate( value : Variant )
	{
		var widgets : array< SActionWidgetPackage >;
		widgets = ( ( array< SActionWidgetPackage > )value );
		UpdateActionWidgets( widgets );
	}

	protected event OnDeviceWidgetsUpdate( value : Variant )
	{
		var widgetsData : array< SDeviceWidgetPackage >;
		widgetsData = ( ( array< SDeviceWidgetPackage > )value );
		UpdateDeviceWidgets( widgetsData );
	}

	protected event OnGlitchingStateChanged( value : Variant )
	{
		var glitchData : GlitchData;
		glitchData = ( ( GlitchData )value );
		if( glitchData.state == EGlitchState.NONE )
		{
			StopGlitchingScreen();
		}
		else
		{
			StartGlitchingScreen( glitchData );
		}
	}

	private virtual function StartGlitchingScreen( glitchData : GlitchData ) {}

	private virtual function StopGlitchingScreen() {}

	protected function ExecuteAction( action : DeviceAction, executor : weak< GameObject > )
	{
		var actionEvent : UIActionEvent;
		var owner : Device;
		actionEvent = new UIActionEvent;
		actionEvent.action = action;
		actionEvent.executor = executor;
		owner = GetOwner();
		if( owner )
		{
			owner.QueueEvent( actionEvent );
		}
	}

	protected event OnDeviceActionCallback( e : inkPointerEvent )
	{
		if( e.IsAction( 'click' ) && !( IsInteractivityBlocked() ) )
		{
			ExecuteDeviceActions( ( ( DeviceActionWidgetControllerBase )( e.GetCurrentTarget().GetController() ) ) );
		}
	}

	protected event OnExecuteButtonAction( widget : weak< inkWidget > )
	{
		if( !( IsInteractivityBlocked() ) )
		{
			ExecuteDeviceActions( ( ( DeviceActionWidgetControllerBase )( widget.GetController() ) ) );
		}
	}

	protected virtual function ExecuteDeviceActions( controller : DeviceActionWidgetControllerBase )
	{
		var i : Int32;
		var actions : array< weak< DeviceAction > >;
		var executor : weak< GameObject >;
		if( controller != NULL )
		{
			if( controller.CanExecuteAction() )
			{
				actions = controller.GetActions();
			}
		}
		for( i = 0; i < actions.Size(); i += 1 )
		{
			executor = GetPlayer( GetOwner().GetGame() );
			ExecuteAction( actions[ i ], executor );
			controller.FinalizeActionExecution( executor, actions[ i ] );
		}
	}

	protected function IsInteractivityBlocked() : Bool
	{
		var isBlockedByBB : Bool;
		var isBlockedByRestriction : Bool;
		var isBlockedByPS : Bool;
		var isBlockedByAiming : Bool;
		var player : PlayerPuppet;
		var owner : Device;
		owner = GetOwner();
		if( owner )
		{
			player = GetPlayer( owner.GetGame() );
			isBlockedByBB = GetBlackboard().GetBool( owner.GetBlackboardDef().UI_InteractivityBlocked );
			isBlockedByPS = !( owner.GetDevicePS().IsInteractive() );
		}
		if( player )
		{
			isBlockedByAiming = player.GetPlayerStateMachineBlackboard().GetInt( GetAllBlackboardDefs().PlayerStateMachine.UpperBody ) == ( ( Int32 )( gamePSMUpperBodyStates.Aim ) );
			isBlockedByRestriction = ( StatusEffectSystem.ObjectHasStatusEffect( player, T"GameplayRestriction.BlockDeviceInteractions" ) || StatusEffectSystem.ObjectHasStatusEffect( player, T"GameplayRestriction.NoWorldInteractions" ) ) || StatusEffectSystem.ObjectHasStatusEffect( player, T"GameplayRestriction.PhoneCallRestriction" );
		}
		return ( ( isBlockedByBB || isBlockedByRestriction ) || isBlockedByPS ) || isBlockedByAiming;
	}

	protected function GoUp()
	{
		if( m_breadcrumbStack.Size() > 0 )
		{
			m_breadcrumbStack.PopBack();
		}
	}

	protected function ClearBreadcrumbStack()
	{
		m_breadcrumbStack.Clear();
	}

	protected function GoDown( const element : ref< SBreadcrumbElementData > )
	{
		m_breadcrumbStack.PushBack( element );
	}

	protected virtual function ResolveBreadcrumbLevel() {}

	public const function GetCurrentBreadcrumbElement() : SBreadcrumbElementData
	{
		var element : SBreadcrumbElementData;
		if( m_breadcrumbStack.Size() > 0 )
		{
			return m_breadcrumbStack.Last();
		}
		else
		{
			element.elementName = "";
			return element;
		}
	}

	public const function GetCurrentBreadcrumbElementName() : String
	{
		var element : SBreadcrumbElementData;
		if( m_breadcrumbStack.Size() > 0 )
		{
			element = m_breadcrumbStack.Last();
			return element.elementName;
		}
		else
		{
			return "";
		}
	}

	public const function GetActiveBreadcrumbElement() : SBreadcrumbElementData
	{
		return m_activeBreadcrumb;
	}

	public const function GetActiveBreadcrumbElementName() : String
	{
		return m_activeBreadcrumb.elementName;
	}

	protected function SetActiveBreadcrumbElement( const element : ref< SBreadcrumbElementData > )
	{
		m_activeBreadcrumb = element;
	}

	public function SetUICameraZoomState( hasUICameraZoom : Bool )
	{
		var evt : SetUICameraZoomEvent;
		if( hasUICameraZoom == m_hasUICameraZoom )
		{
			return;
		}
		evt = new SetUICameraZoomEvent;
		evt.hasUICameraZoom = hasUICameraZoom;
		GetOwner().QueueEvent( evt );
		m_hasUICameraZoom = hasUICameraZoom;
	}

	protected function RequestActionWidgetsUpdate()
	{
		var actionWidgetsEvent : RequestActionWidgetsUpdateEvent;
		actionWidgetsEvent = new RequestActionWidgetsUpdateEvent;
		actionWidgetsEvent.screenDefinition = GetOwner().GetScreenDefinition();
		GetOwner().QueueEvent( actionWidgetsEvent );
	}

	protected function RequestDeviceWidgetsUpdate()
	{
		var deviceWidgetEvent : RequestDeviceWidgetUpdateEvent;
		deviceWidgetEvent = new RequestDeviceWidgetUpdateEvent;
		deviceWidgetEvent.screenDefinition = GetOwner().GetScreenDefinition();
		GetOwner().QueueEvent( deviceWidgetEvent );
	}

	protected function RequestUIRefresh( optional context : CName )
	{
		var refreshEvent : RequestUIRefreshEvent;
		refreshEvent = new RequestUIRefreshEvent;
		refreshEvent.context = context;
		GetOwner().QueueEvent( refreshEvent );
	}

	protected function RequestBeadcrumbBarUpdate( const data : ref< SBreadCrumbUpdateData > )
	{
		var breadcrumbEvent : RequestBreadCrumbBarUpdateEvent;
		breadcrumbEvent = new RequestBreadCrumbBarUpdateEvent;
		breadcrumbEvent.breadCrumbData = data;
		GetOwner().QueueEvent( breadcrumbEvent );
	}

	protected event OnButtonHoverOver( e : inkPointerEvent )
	{
		var controller : DeviceButtonLogicControllerBase;
		controller = ( ( DeviceButtonLogicControllerBase )( e.GetCurrentTarget().GetController() ) );
		if( controller )
		{
			PlaySound( controller.GetWidgetAudioName(), 'OnHoverOver', controller.GetOnHoverOverKeySfxName() );
		}
	}

	protected event OnButtonHoverOut( e : inkPointerEvent )
	{
		var controller : DeviceButtonLogicControllerBase;
		controller = ( ( DeviceButtonLogicControllerBase )( e.GetCurrentTarget().GetController() ) );
		if( controller )
		{
			PlaySound( controller.GetWidgetAudioName(), 'OnHoverOut', controller.GetOnHoverOutKeySfxName() );
		}
	}

	protected event OnButtonPress( e : inkPointerEvent )
	{
		var controller : DeviceButtonLogicControllerBase;
		if( e.IsAction( 'click' ) )
		{
			controller = ( ( DeviceButtonLogicControllerBase )( e.GetCurrentTarget().GetController() ) );
			if( controller )
			{
				PlaySound( controller.GetWidgetAudioName(), 'OnPress', controller.GetOnPressKeySfxName() );
			}
		}
	}

	protected event OnButtonRelease( e : inkPointerEvent )
	{
		var controller : DeviceButtonLogicControllerBase;
		if( e.IsAction( 'click' ) )
		{
			controller = ( ( DeviceButtonLogicControllerBase )( e.GetCurrentTarget().GetController() ) );
			if( controller )
			{
				PlaySound( controller.GetWidgetAudioName(), 'OnRelease', controller.GetOnReleaseKeySfxName() );
			}
		}
	}

	public function TriggerAnimationByName( animName : CName, playbackOption : EInkAnimationPlaybackOption, optional targetWidget : inkWidget, optional playbackOptionsOverrideData : PlaybackOptionsUpdateData )
	{
		if( m_animationManager )
		{
			m_animationManager.TriggerAnimationByName( this, animName, playbackOption, targetWidget, playbackOptionsOverrideData );
		}
	}

	protected function UpdateUnstreamedUI()
	{
		var evt : UIUnstreamedEvent;
		if( m_isInitialized )
		{
			evt = new UIUnstreamedEvent;
			GetOwner().QueueEvent( evt );
		}
	}

}

