class RefreshSlavesEvent extends ProcessDevicesEvent
{
	var onInitialize : Bool;
	var force : Bool;
}

class FillTakeOverChainBBoardEvent extends Event
{
	var requesterID : PersistentID;
}

class MasterController extends ScriptableDeviceComponent
{

	public const override function GetPS() : MasterControllerPS
	{
		return ( ( MasterControllerPS )( GetBasePS() ) );
	}

}

class MasterControllerPS extends ScriptableDeviceComponentPS
{
	protected var m_clearance : Clearance;

	protected event OnInstantiated()
	{
		super.OnInstantiated();
	}

	protected override function Initialize()
	{
		super.Initialize();
	}

	protected export override function OnDeviceDynamicConnectionChange( evt : DeviceDynamicConnectionChange ) : EntityNotificationType
	{
		super.OnDeviceDynamicConnectionChange( evt );
		return EntityNotificationType.DoNotNotifyEntity;
	}

	public const virtual function GetExpectedSlaveState() : EDeviceStatus
	{
		return EDeviceStatus.INVALID;
	}

	protected const override function GetClearance() : Clearance
	{
		return Clearance.CreateClearance( 1, 100 );
	}

	protected override function DetermineGameplayViability( const context : ref< GetActionsContext >, hasActiveActions : Bool ) : Bool
	{
		return MasterViabilityInterpreter.Evaluate( this, hasActiveActions );
	}

	public override function GetWidgetTypeName() : CName
	{
		return 'GenericMasterDeviceWidget';
	}

	public function NetrunnerGiveConnectedDevices() : array< DeviceComponentPS >
	{
		var entityID : EntityID;
		var devices : array< DeviceComponentPS >;
		entityID = PersistentID.ExtractEntityID( GetID() );
		GameInstance.GetDeviceSystem( GetGameInstance() ).GetChildren( entityID, devices );
		return devices;
	}

	public const override function IsMasterType() : Bool
	{
		return true;
	}

	public const override function GetAttachedSlaveForPing( optional context : MasterControllerPS ) : DeviceComponentPS
	{
		var slaves : array< DeviceComponentPS >;
		var slave : ScriptableDeviceComponentPS;
		var i : Int32;
		if( context )
		{
			slaves = context.GetImmediateSlaves();
		}
		else
		{
			slaves = GetImmediateSlaves();
		}
		for( i = 0; i < slaves.Size(); i += 1 )
		{
			slave = ( ( ScriptableDeviceComponentPS )( slaves[ i ] ) );
			if( !( slave ) || ( slave == this ) )
			{
				continue;
			}
			if( slave.IsAttachedToGame() )
			{
				if( !( slave.ShouldRevealDevicesGrid() ) || slave.HasNetworkBackdoor() )
				{
					continue;
				}
				return slave;
			}
		}
		return NULL;
	}

	public const override function GetFirstAttachedSlave() : DeviceComponentPS
	{
		var slaves : array< DeviceComponentPS >;
		var i : Int32;
		slaves = GetImmediateSlaves();
		for( i = 0; i < slaves.Size(); i += 1 )
		{
			if( slaves[ i ].IsAttachedToGame() )
			{
				return slaves[ i ];
			}
		}
		return NULL;
	}

	public const function GetAllDescendants( out outDevices : array< DeviceComponentPS > )
	{
		GameInstance.GetDeviceSystem( GetGameInstance() ).GetAllDescendants( PersistentID.ExtractEntityID( GetID() ), outDevices );
	}

	public const function GetPuppets() : array< PuppetDeviceLinkPS >
	{
		var allSlaves : array< DeviceComponentPS >;
		var puppets : array< PuppetDeviceLinkPS >;
		var puppetLink : PuppetDeviceLinkPS;
		var i : Int32;
		GetAllDescendants( allSlaves );
		for( i = 0; i < allSlaves.Size(); i += 1 )
		{
			puppetLink = ( ( PuppetDeviceLinkPS )( allSlaves[ i ] ) );
			if( puppetLink )
			{
				puppets.PushBack( puppetLink );
			}
		}
		return puppets;
	}

	public const virtual function GetImmediateSlaves() : array< DeviceComponentPS >
	{
		var slaves : array< DeviceComponentPS >;
		GameInstance.GetDeviceSystem( GetGameInstance() ).GetChildren( GetMyEntityID(), slaves );
		return slaves;
	}

	public const virtual function GetLazySlaves() : array< LazyDevice >
	{
		var slaves : array< LazyDevice >;
		GameInstance.GetDeviceSystem( GetGameInstance() ).GetLazyChildren( GetMyEntityID(), slaves );
		return slaves;
	}

	public const override function HasAnySlave() : Bool
	{
		return GameInstance.GetDeviceSystem( GetGameInstance() ).HasAnyChild( GetMyEntityID() );
	}

	public const function GetImmediateDescendants() : array< DeviceComponentPS >
	{
		var entityID : EntityID;
		var immediateDescendants : array< DeviceComponentPS >;
		entityID = PersistentID.ExtractEntityID( GetID() );
		GameInstance.GetDeviceSystem( GetGameInstance() ).GetAllDescendants( entityID, immediateDescendants );
		return immediateDescendants;
	}

	public const function GetLazyDescendants() : array< LazyDevice >
	{
		var entityID : EntityID;
		var descendants : array< LazyDevice >;
		entityID = PersistentID.ExtractEntityID( GetID() );
		GameInstance.GetDeviceSystem( GetGameInstance() ).GetLazyAllDescendants( entityID, descendants );
		return descendants;
	}

	protected function ExtractActionFromSlave( slave : DeviceComponentPS, actionName : CName, out outAction : DeviceAction ) : Bool
	{
		outAction = ( ( ScriptableDeviceComponentPS )( slave ) ).GetActionByName( actionName );
		if( outAction )
		{
			return true;
		}
		else
		{
			return false;
		}
	}

	protected const function SendActionsToAllSlaves( const actions : ref< array< ScriptableDeviceAction > > )
	{
		var i : Int32;
		for( i = 0; i < actions.Size(); i += 1 )
		{
			SendActionToAllSlaves( actions[ i ] );
		}
	}

	protected const virtual function SendActionToAllSlaves( action : ScriptableDeviceAction )
	{
		var slaves : array< DeviceComponentPS >;
		var i : Int32;
		slaves = GetImmediateSlaves();
		for( i = 0; i < slaves.Size(); i += 1 )
		{
			ExecutePSAction( action, slaves[ i ] );
		}
	}

	protected const function SendEventToAllSlaves( evt : Event )
	{
		var slaves : array< DeviceComponentPS >;
		var i : Int32;
		slaves = GetImmediateSlaves();
		for( i = 0; i < slaves.Size(); i += 1 )
		{
			GetPersistencySystem().QueuePSEvent( slaves[ i ].GetID(), slaves[ i ].GetClassName(), evt );
		}
	}

	protected const function GetQuickHacksFromSlave( out outActions : array< DeviceAction >, const context : ref< GetActionsContext > )
	{
		var slaves : array< DeviceComponentPS >;
		var i : Int32;
		slaves = GetImmediateSlaves();
		for( i = 0; i < slaves.Size(); i += 1 )
		{
			( ( ScriptableDeviceComponentPS )( slaves[ i ] ) ).GetQuickHackActionsExternal( outActions, context );
		}
	}

	public function RequestAreaEffectVisualisationUpdateOnSlaves( areaEffectID : CName, show : Bool )
	{
		var i : Int32;
		var evt : AreaEffectVisualisationRequest;
		var devices : array< DeviceComponentPS >;
		evt = new AreaEffectVisualisationRequest;
		evt.show = show;
		evt.areaEffectID = areaEffectID;
		devices = GetImmediateSlaves();
		for( i = 0; i < devices.Size(); i += 1 )
		{
			if( GetID() == devices[ i ].GetID() )
			{
				continue;
			}
			GetPersistencySystem().QueueEntityEvent( PersistentID.ExtractEntityID( devices[ i ].GetID() ), evt );
		}
	}

	public virtual function OnRequestThumbnailWidgetsUpdate( evt : RequestThumbnailWidgetsUpdateEvent )
	{
		RequestThumbnailWidgetsUpdate( GetBlackboard() );
	}

	public virtual function OnRequestDeviceWidgetUpdate( evt : RequestDeviceWidgetUpdateEvent )
	{
		RequestDeviceWidgetsUpdate( GetBlackboard(), evt.requester );
	}

	public const override function GetBlackboardDef() : MasterDeviceBaseBlackboardDef
	{
		return GetAllBlackboardDefs().MasterDeviceBaseBlackboard;
	}

	public virtual function GetThumbnailWidgets() : array< SThumbnailWidgetPackage >
	{
		var i : Int32;
		var devices : array< DeviceComponentPS >;
		var widgetsData : array< SThumbnailWidgetPackage >;
		var currentWidget : SThumbnailWidgetPackage;
		devices = GetImmediateSlaves();
		for( i = 0; i < devices.Size(); i += 1 )
		{
			if( devices[ i ].GetID() == GetID() )
			{
				continue;
			}
			currentWidget = devices[ i ].GetThumbnailWidget();
			if( currentWidget.isValid )
			{
				widgetsData.PushBack( currentWidget );
			}
		}
		return widgetsData;
	}

	public virtual function GetDeviceWidgets() : array< SDeviceWidgetPackage >
	{
		var i : Int32;
		var devices : array< DeviceComponentPS >;
		var widgetsData : array< SDeviceWidgetPackage >;
		var currentWidget : SDeviceWidgetPackage;
		devices = GetImmediateSlaves();
		for( i = 0; i < devices.Size(); i += 1 )
		{
			currentWidget = devices[ i ].GetDeviceWidget( GenerateContext( gamedeviceRequestType.External, GetClearance() ) );
			if( currentWidget.isValid )
			{
				widgetsData.PushBack( devices[ i ].GetDeviceWidget( GenerateContext( gamedeviceRequestType.External, GetClearance() ) ) );
			}
		}
		return widgetsData;
	}

	public virtual function GetSlaveDeviceWidget( deviceID : PersistentID ) : SDeviceWidgetPackage
	{
		var i : Int32;
		var devices : array< DeviceComponentPS >;
		var widgetData : SDeviceWidgetPackage;
		devices = GetImmediateSlaves();
		widgetData.isValid = false;
		for( i = 0; i < devices.Size(); i += 1 )
		{
			if( devices[ i ].GetID() == GetID() )
			{
				continue;
			}
			if( devices[ i ].GetID() == deviceID )
			{
				widgetData = devices[ i ].GetDeviceWidget( GenerateContext( gamedeviceRequestType.External, GetClearance() ) );
				break;
			}
		}
		return widgetData;
	}

	public virtual function RequestThumbnailWidgetsUpdate( blackboard : IBlackboard )
	{
		var widgetsData : array< SThumbnailWidgetPackage >;
		widgetsData = GetThumbnailWidgets();
		if( blackboard )
		{
			blackboard.SetVariant( GetBlackboardDef().ThumbnailWidgetsData, widgetsData );
			blackboard.SignalVariant( GetBlackboardDef().ThumbnailWidgetsData );
			blackboard.FireCallbacks();
		}
	}

	public virtual function RequestDeviceWidgetsUpdate( blackboard : IBlackboard, const devices : ref< array< PersistentID > > )
	{
		var i : Int32;
		var widgetsData : array< SDeviceWidgetPackage >;
		var widgetData : SDeviceWidgetPackage;
		for( i = 0; i < devices.Size(); i += 1 )
		{
			widgetData = GetSlaveDeviceWidget( devices[ i ] );
			if( widgetData.isValid )
			{
				widgetsData.PushBack( widgetData );
			}
		}
		RequestDeviceWidgetsUpdate( blackboard, widgetsData );
	}

	public virtual function RequestDeviceWidgetsUpdate( blackboard : IBlackboard, deviceID : PersistentID )
	{
		var widgetsData : array< SDeviceWidgetPackage >;
		var widgetData : SDeviceWidgetPackage;
		widgetData = GetSlaveDeviceWidget( deviceID );
		if( widgetData.isValid )
		{
			widgetsData.PushBack( widgetData );
		}
		RequestDeviceWidgetsUpdate( blackboard, widgetsData );
	}

	public function RequestAllDevicesWidgetsUpdate( blackboard : IBlackboard )
	{
		var widgetsData : array< SDeviceWidgetPackage >;
		widgetsData = GetDeviceWidgets();
		RequestDeviceWidgetsUpdate( blackboard, widgetsData );
	}

	public function RefreshSlaves_Event( optional onInitialize : Bool, optional force : Bool )
	{
		var evt : RefreshSlavesEvent;
		evt = new RefreshSlavesEvent;
		evt.onInitialize = onInitialize;
		evt.force = force;
		ProcessDevicesLazy( GetLazySlaves(), evt );
	}

	public function RefreshPowerOnSlaves_Event()
	{
		var evt : RefreshPowerOnSlavesEvent;
		evt = new RefreshPowerOnSlavesEvent;
		ProcessDevicesLazy( GetLazySlaves(), evt );
	}

	public function RefreshDefaultHighlightOnSlaves()
	{
		var slaves : array< DeviceComponentPS >;
		var evt : ForceUpdateDefaultHighlightEvent;
		var i : Int32;
		slaves = GetImmediateDescendants();
		evt = new ForceUpdateDefaultHighlightEvent;
		for( i = 0; i < slaves.Size(); i += 1 )
		{
			QueuePSEvent( slaves[ i ], evt );
		}
	}

	public function SetSlavesAsQuestImportant( isImportant : Bool )
	{
		var slaves : array< DeviceComponentPS >;
		var evt : SetAsQuestImportantEvent;
		var i : Int32;
		slaves = GetImmediateDescendants();
		evt = new SetAsQuestImportantEvent;
		evt.SetImportant( isImportant );
		for( i = 0; i < slaves.Size(); i += 1 )
		{
			QueuePSEvent( slaves[ i ], evt );
		}
	}

	protected virtual function OnRefreshSlavesEvent( evt : RefreshSlavesEvent ) : EntityNotificationType
	{
		return EntityNotificationType.DoNotNotifyEntity;
	}

	protected virtual function OnFillTakeOverChainBBoardEvent( evt : FillTakeOverChainBBoardEvent ) : EntityNotificationType
	{
		return EntityNotificationType.DoNotNotifyEntity;
	}

	protected function FillTakeOverChainBB()
	{
		var i : Int32;
		var i1 : Int32;
		var validDeviceNumber : Int32;
		var devices : array< DeviceComponentPS >;
		var chainBlackBoard : IBlackboard;
		var deviceChain : array< SWidgetPackage >;
		var newDeviceChainnStruct : SWidgetPackage;
		var customData : ControlledDeviceData;
		var isDuplicated : Bool;
		chainBlackBoard = GameInstance.GetBlackboardSystem( GetGameInstance() ).Get( GetAllBlackboardDefs().DeviceTakeControl );
		deviceChain = ( ( array< SWidgetPackage > )( chainBlackBoard.GetVariant( GetAllBlackboardDefs().DeviceTakeControl.DevicesChain ) ) );
		devices = GetImmediateSlaves();
		validDeviceNumber = deviceChain.Size();
		for( i = 0; i < devices.Size(); i += 1 )
		{
			isDuplicated = false;
			if( ( ( ScriptableDeviceComponentPS )( devices[ i ] ) ).CanBeInDeviceChain() )
			{
				for( i1 = 0; i1 < deviceChain.Size(); i1 += 1 )
				{
					if( deviceChain[ i1 ].ownerID == devices[ i ].GetID() )
					{
						isDuplicated = true;
					}
				}
				if( isDuplicated )
				{
					continue;
				}
				validDeviceNumber += 1;
				customData = new ControlledDeviceData;
				customData.m_isActive = ( ( ScriptableDeviceComponentPS )( devices[ i ] ) ).IsControlledByPlayer();
				newDeviceChainnStruct.displayName = ( ( ( devices[ i ].GetDeviceName() + " " ) + "[" ) + IntToString( validDeviceNumber ) ) + "]";
				newDeviceChainnStruct.ownerID = devices[ i ].GetID();
				newDeviceChainnStruct.ownerIDClassName = devices[ i ].GetClassName();
				newDeviceChainnStruct.customData = customData;
				newDeviceChainnStruct.libraryID = 'device';
				deviceChain.PushBack( newDeviceChainnStruct );
			}
		}
		chainBlackBoard.SetVariant( GetAllBlackboardDefs().DeviceTakeControl.DevicesChain, deviceChain );
	}

	public override function RevealDevicesGrid( shouldDraw : Bool, optional ownerEntityPosition : Vector4, optional fxDefault : FxResource, optional isPing : Bool, optional lifetime : Float, optional revealSlave : Bool, optional revealMaster : Bool, optional ignoreRevealed : Bool )
	{
		var slaves : array< DeviceComponentPS >;
		var slave : ScriptableDeviceComponentPS;
		var slaveEntity : Device;
		var slavePosition : Vector4;
		var networkSystem : NetworkSystem;
		var linkData : SNetworkLinkData;
		var linksData : array< SNetworkLinkData >;
		var registerLinkRequest : RegisterNetworkLinkRequest;
		var unregisterLinkRequest : UnregisterNetworkLinksByIDRequest;
		var i : Int32;
		if( !( ShouldRevealDevicesGrid() ) )
		{
			return;
		}
		networkSystem = GetNetworkSystem();
		if( !( shouldDraw ) )
		{
			if( networkSystem.HasNetworkLink( PersistentID.ExtractEntityID( GetID() ) ) )
			{
				unregisterLinkRequest = new UnregisterNetworkLinksByIDRequest;
				unregisterLinkRequest.ID = PersistentID.ExtractEntityID( GetID() );
				networkSystem.QueueRequest( unregisterLinkRequest );
			}
			return;
		}
		if( Vector4.IsZero( ownerEntityPosition ) )
		{
			return;
		}
		if( m_fullDepth )
		{
			slaves = GetImmediateDescendants();
		}
		else
		{
			slaves = GetImmediateSlaves();
		}
		if( slaves.Size() == 0 )
		{
			return;
		}
		if( ( ( IsUnpowered() && !( CanRevealDevicesGridWhenUnpowered() ) ) || IsDisabled() ) || ( ignoreRevealed && WasRevealedInNetworkPing() ) )
		{
			return;
		}
		linkData.masterID = PersistentID.ExtractEntityID( GetID() );
		linkData.masterPos = ownerEntityPosition;
		linkData.linkType = ELinkType.GRID;
		linkData.isPing = isPing;
		linkData.lifetime = lifetime;
		linkData.revealSlave = revealSlave;
		linkData.revealMaster = revealMaster;
		if( isPing )
		{
			linkData.permanent = lifetime > 0.0;
		}
		linkData.fxResource = fxDefault;
		for( i = 0; i < slaves.Size(); i += 1 )
		{
			slave = ( ( ScriptableDeviceComponentPS )( slaves[ i ] ) );
			if( ( ( ( slave == NULL ) || !( slave.ShouldRevealDevicesGrid() ) ) || ( ignoreRevealed && slave.WasRevealedInNetworkPing() ) ) || ( slave.IsUnpowered() && !( slave.CanRevealDevicesGridWhenUnpowered() ) ) )
			{
				continue;
			}
			slaveEntity = ( ( Device )( slaves[ i ].GetOwnerEntityWeak() ) );
			if( !( slaveEntity ) )
			{
				GameInstance.GetDeviceSystem( GetGameInstance() ).GetNodePosition( PersistentID.ExtractEntityID( slaves[ i ].GetID() ), slavePosition );
			}
			else
			{
				slavePosition = slaveEntity.GetNetworkBeamEndpoint();
			}
			if( Vector4.IsZero( slavePosition ) )
			{
				continue;
			}
			linkData.slaveID = PersistentID.ExtractEntityID( slaves[ i ].GetID() );
			linkData.slavePos = slavePosition;
			linkData.drawLink = m_drawGridLink && slave.ShouldDrawGridLink();
			linkData.isDynamic = IsLinkDynamic() || slave.IsLinkDynamic();
			if( isPing && !( linkData.drawLink ) )
			{
				continue;
			}
			linksData.PushBack( linkData );
		}
		if( linksData.Size() > 0 )
		{
			registerLinkRequest = new RegisterNetworkLinkRequest;
			registerLinkRequest.linksData = linksData;
			networkSystem.QueueRequest( registerLinkRequest );
		}
	}

}

