class ControlledDeviceData extends WidgetCustomData
{
	var m_isActive : Bool;
	var m_isNest : Bool;
}

class ControlledDevicesInkGameController extends inkGameController
{
	protected var m_rootWidget : weak< inkCanvas >;
	private var m_devicesStackSlot : weak< inkHorizontalPanel >;
	private var m_currentDeviceText : weak< inkText >;
	protected var m_controlledDevicesWidgetsData : array< SWidgetPackage >;
	private var m_isDeviceWorking_BBID : CallbackHandle;
	private var m_activeDevice_BBID : CallbackHandle;
	private var m_deviceChain_BBID : CallbackHandle;
	private var m_chainLocked_BBID : CallbackHandle;

	protected event OnInitialize()
	{
		var canActivateOverclock : Bool;
		var rootWidgetMargin : inkMargin;
		m_rootWidget = ( ( inkCanvas )( GetRootWidget() ) );
		m_devicesStackSlot = ( ( inkHorizontalPanel )( GetWidget( 'main_canvas/main_panel/horizontalContainer/devices_stack' ) ) );
		m_currentDeviceText = ( ( inkText )( GetWidget( 'main_canvas/main_panel/horizontalContainer/leftFlap/device_text' ) ) );
		m_rootWidget.SetVisible( false );
		RegisterBlackboardCallbacks();
		OnTakeControllOverDevice( GetBlackboard().GetVariant( GetBlackboardDef().DevicesChain ) );
		OnControlledDeviceChanged( GetBlackboard().GetEntityID( GetBlackboardDef().ActiveDevice ) );
		OnControlledDeviceWorkStateChanged( GetBlackboard().GetBool( GetBlackboardDef().IsDeviceWorking ) );
		canActivateOverclock = QuickHackableHelper.CanActivateOverclockedState( GetPlayerControlledObject() );
		if( canActivateOverclock )
		{
			rootWidgetMargin = m_rootWidget.GetMargin();
			rootWidgetMargin.left += 180.0;
			m_rootWidget.SetMargin( rootWidgetMargin );
		}
	}

	protected event OnUninitialize()
	{
		UnRegisterBlackboardCallbacks();
	}

	private function GetBlackboard() : IBlackboard
	{
		return GetBlackboardSystem().Get( GetBlackboardDef() );
	}

	private function RegisterBlackboardCallbacks()
	{
		m_deviceChain_BBID = GetBlackboard().RegisterListenerVariant( GetBlackboardDef().DevicesChain, this, 'OnTakeControllOverDevice' );
		m_chainLocked_BBID = GetBlackboard().RegisterListenerBool( GetBlackboardDef().ChainLocked, this, 'OnDeviceChainLocked' );
		m_activeDevice_BBID = GetBlackboard().RegisterListenerEntityID( GetBlackboardDef().ActiveDevice, this, 'OnControlledDeviceChanged' );
		m_isDeviceWorking_BBID = GetBlackboard().RegisterListenerBool( GetBlackboardDef().IsDeviceWorking, this, 'OnControlledDeviceWorkStateChanged' );
	}

	private function UnRegisterBlackboardCallbacks()
	{
		GetBlackboard().UnregisterListenerVariant( GetAllBlackboardDefs().DeviceTakeControl.DevicesChain, m_deviceChain_BBID );
		GetBlackboard().UnregisterListenerBool( GetAllBlackboardDefs().DeviceTakeControl.ChainLocked, m_chainLocked_BBID );
		GetBlackboard().UnregisterListenerEntityID( GetAllBlackboardDefs().DeviceTakeControl.ActiveDevice, m_activeDevice_BBID );
		GetBlackboard().UnregisterListenerBool( GetAllBlackboardDefs().DeviceTakeControl.IsDeviceWorking, m_isDeviceWorking_BBID );
	}

	private function GetBlackboardDef() : DeviceTakeControlDef
	{
		return GetAllBlackboardDefs().DeviceTakeControl;
	}

	protected function GetControlledDeviceWidget( id : Int32 ) : weak< inkWidget >
	{
		var widget : weak< inkWidget >;
		if( m_controlledDevicesWidgetsData.Size() < id )
		{
			if( m_controlledDevicesWidgetsData[ id ].widget == NULL )
			{
				m_controlledDevicesWidgetsData.Erase( id );
			}
			else
			{
				widget = m_controlledDevicesWidgetsData[ id ].widget;
			}
		}
		return widget;
	}

	protected function GetControlledDeviceWidget( const widgetData : ref< SWidgetPackage > ) : weak< inkWidget >
	{
		var i : Int32;
		for( i = 0; i < m_controlledDevicesWidgetsData.Size(); i += 1 )
		{
			if( ( ( ( m_controlledDevicesWidgetsData[ i ].ownerID == widgetData.ownerID && m_controlledDevicesWidgetsData[ i ].widgetName == widgetData.widgetName ) && ( m_controlledDevicesWidgetsData[ i ].widgetTweakDBID == widgetData.widgetTweakDBID ) ) && m_controlledDevicesWidgetsData[ i ].libraryPath == widgetData.libraryPath ) && m_controlledDevicesWidgetsData[ i ].libraryID == widgetData.libraryID )
			{
				return m_controlledDevicesWidgetsData[ i ].widget;
			}
		}
		return NULL;
	}

	protected function AddControlledDeviceWidget( widget : inkWidget, widgetData : SWidgetPackage ) : weak< inkWidget >
	{
		widgetData.widget = widget;
		m_controlledDevicesWidgetsData.PushBack( widgetData );
		return widgetData.widget;
	}

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

	protected function InitializeControlledDeviceWidget( widget : inkWidget, const widgetData : ref< SWidgetPackage > )
	{
		var controller : ControlledDeviceLogicController;
		var customData : ControlledDeviceData;
		controller = ( ( ControlledDeviceLogicController )( widget.GetController() ) );
		if( controller != NULL )
		{
			if( widgetData.ownerIDClassName == 'SniperNestControllerPS' )
			{
				customData = ( ( ControlledDeviceData )( widgetData.customData ) );
				customData.m_isNest = true;
			}
			controller.Initialize( this, widgetData );
		}
		widget.SetVisible( true );
	}

	public virtual function UpdateControlledDevicesWidgets( const widgetsData : ref< array< SWidgetPackage > > )
	{
		var i : Int32;
		var widget : inkWidget;
		var customData : ControlledDeviceData;
		var widgetUserData : SWidgetPackageWrapper;
		HideControlledDeviceWidgets();
		for( i = 0; i < widgetsData.Size(); i += 1 )
		{
			customData = ( ( ControlledDeviceData )( widgetsData[ i ].customData ) );
			if( customData && customData.m_isActive )
			{
				m_currentDeviceText.SetText( widgetsData[ i ].displayName );
			}
			widget = GetControlledDeviceWidget( widgetsData[ i ] );
			if( widget == NULL )
			{
				widgetUserData = new SWidgetPackageWrapper;
				widgetUserData.WidgetPackage = widgetsData[ i ];
				if( HasExternalLibrary( widgetsData[ i ].libraryPath, widgetsData[ i ].libraryID ) )
				{
					AsyncSpawnFromExternal( m_devicesStackSlot, widgetsData[ i ].libraryPath, widgetsData[ i ].libraryID, this, 'OnDeviceSpawned', widgetUserData );
				}
				else
				{
					AsyncSpawnFromLocal( m_devicesStackSlot, widgetsData[ i ].libraryID, this, 'OnDeviceSpawned', widgetUserData );
				}
			}
			else
			{
				InitializeControlledDeviceWidget( widget, widgetsData[ i ] );
			}
		}
	}

	protected event OnDeviceSpawned( widget : inkWidget, userData : IScriptable )
	{
		var widgetUserData : SWidgetPackageWrapper;
		widgetUserData = ( ( SWidgetPackageWrapper )( userData ) );
		if( widget != NULL )
		{
			AddControlledDeviceWidget( widget, widgetUserData.WidgetPackage );
			InitializeControlledDeviceWidget( widget, widgetUserData.WidgetPackage );
		}
	}

	private function ClearControlledDevicesStack()
	{
		var i : Int32;
		for( i = 0; i < m_controlledDevicesWidgetsData.Size(); i += 1 )
		{
			m_devicesStackSlot.RemoveChild( m_controlledDevicesWidgetsData[ i ].widget );
		}
		m_controlledDevicesWidgetsData.Clear();
	}

	private function CreateSwitchCameraHint( isVisible : Bool )
	{
		var data : InputHintData;
		var psmBlackboard : IBlackboard;
		psmBlackboard = GetBlackboardSystem().GetLocalInstanced( GetPlayerControlledObject().GetEntityID(), GetAllBlackboardDefs().PlayerStateMachine );
		psmBlackboard.SetBool( GetAllBlackboardDefs().PlayerStateMachine.IsControllingCamera, isVisible );
		data.action = 'SwitchDeviceNext';
		data.source = 'ControlledDevicesChain';
		data.localizedLabel = "LocKey#52035";
		data.sortingPriority = ( ( Int32 )( TakeOverControlSystemInputHintSortPriority.NextDevice ) );
		SendInputHintData( GetPlayerControlledObject().GetGame(), isVisible, data );
		data.action = 'SwitchDevicePrevious';
		data.source = 'ControlledDevicesChain';
		data.localizedLabel = "LocKey#52036";
		data.sortingPriority = ( ( Int32 )( TakeOverControlSystemInputHintSortPriority.PreviousDevice ) );
		SendInputHintData( GetPlayerControlledObject().GetGame(), isVisible, data );
	}

	private function SetRootVisibility( isVisible : Bool )
	{
		if( isVisible )
		{
			if( !( GetBlackboard().GetBool( GetBlackboardDef().ChainLocked ) ) )
			{
				m_rootWidget.SetVisible( isVisible );
				CreateSwitchCameraHint( isVisible );
			}
		}
		else
		{
			m_rootWidget.SetVisible( isVisible );
			CreateSwitchCameraHint( isVisible );
		}
	}

	protected event OnTakeControllOverDevice( value : Variant )
	{
		var widgets : array< SWidgetPackage >;
		widgets = ( ( array< SWidgetPackage > )value );
		if( widgets.Size() <= 1 )
		{
			SetRootVisibility( false );
			ClearControlledDevicesStack();
		}
		else
		{
			SetRootVisibility( true );
			UpdateControlledDevicesWidgets( widgets );
		}
	}

	protected event OnDeviceChainLocked( value : Bool )
	{
		if( value )
		{
			SetRootVisibility( false );
		}
		else
		{
			SetRootVisibility( true );
		}
	}

	protected event OnControlledDeviceChanged( value : EntityID )
	{
		var i : Int32;
		var customData : ControlledDeviceData;
		for( i = 0; i < m_controlledDevicesWidgetsData.Size(); i += 1 )
		{
			customData = new ControlledDeviceData;
			if( PersistentID.ExtractEntityID( m_controlledDevicesWidgetsData[ i ].ownerID ) == value )
			{
				customData.m_isActive = true;
				m_currentDeviceText.SetText( m_controlledDevicesWidgetsData[ i ].displayName );
			}
			else
			{
				customData.m_isActive = false;
			}
			m_controlledDevicesWidgetsData[ i ].customData = customData;
			InitializeControlledDeviceWidget( m_controlledDevicesWidgetsData[ i ].widget, m_controlledDevicesWidgetsData[ i ] );
		}
	}

	protected event OnControlledDeviceWorkStateChanged( value : Bool ) {}
}

class SWidgetPackageWrapper extends IScriptable
{
	var WidgetPackage : SWidgetPackage;
}

