struct SHighlightTarget
{
	var targetID : EntityID;
	var highlightType : EFocusForcedHighlightType;
}

class AreaEffectVisualizationComponent extends ScriptableComponent
{
	protected var m_fxResourceMapper : FxResourceMapperComponent;
	private var m_forceHighlightTargetBuckets : array< GameEffectTargetVisualizationData >;
	private var m_availableQuickHacks : array< CName >;
	private var m_availablespiderbotActions : array< CName >;
	private var m_activeAction : BaseScriptableAction;
	private var m_activeEffectIndex : Int32;
	default m_activeEffectIndex = -1;

	protected event OnRequestComponents( ri : EntityRequestComponentsInterface )
	{
		EntityRequestComponentsInterface.RequestComponent( ri, 'FxResourceMapper', 'FxResourceMapperComponent', false );
	}

	protected event OnTakeControl( ri : EntityResolveComponentsInterface )
	{
		m_fxResourceMapper = ( ( FxResourceMapperComponent )( EntityResolveComponentsInterface.GetComponent( ri, 'FxResourceMapper' ) ) );
	}

	protected event OnHUDInstruction( evt : HUDInstruction )
	{
		var instanceState : InstanceState;
		if( GetOwner().GetHudManager().IsQuickHackPanelOpened() )
		{
			return false;
		}
		instanceState = evt.highlightInstructions.GetState();
		if( ( instanceState == InstanceState.ON || instanceState == InstanceState.HIDDEN ) && evt.highlightInstructions.isLookedAt )
		{
			ResolveAreaEffectVisualisations( true, instanceState );
		}
		else
		{
			if( evt.highlightInstructions.WasProcessed() )
			{
				ResolveAreaEffectVisualisations( false, instanceState );
			}
		}
	}

	protected function GetFxMapper() : FxResourceMapperComponent
	{
		return ( ( Device )( GetOwner() ) ).GetFxResourceMapper();
	}

	public function ResolveAreaEffectVisualisations( activated : Bool, instanceState : InstanceState )
	{
		if( ( GetFxMapper().GetAreaEffectDataSize() <= 0 ) && ( GetFxMapper().GetAreaEffectInFocusSize() <= 0 ) )
		{
			return;
		}
		ResolveAreaEffectsVisibility( activated, instanceState );
	}

	protected event OnAreaEffectVisualisationRequest( evt : AreaEffectVisualisationRequest )
	{
		var areaEffectIndex : Int32;
		areaEffectIndex = GetFxMapper().GetAreaEffectDataIndexByName( evt.areaEffectID );
		if( areaEffectIndex != -1 )
		{
			ToggleAreaEffectVisibility( areaEffectIndex, evt.show );
		}
	}

	protected virtual function ResolveAreaEffectsVisibility( show : Bool, instanceState : InstanceState )
	{
		ResolveAreaSpiderbotVisibility( show );
		if( !( show ) || instanceState == InstanceState.ON )
		{
			ResolveAreaEffectsInFocusModeVisibility( show );
		}
	}

	protected virtual function ResolveAreaEffectsInFocusModeVisibility( show : Bool )
	{
		var i : Int32;
		var effectIndex : Int32;
		for( i = 0; i < GetFxMapper().GetAreaEffectInFocusSize(); i += 1 )
		{
			if( !( GetFxMapper().GetAreaEffectInFocusModeByIndex( i ).onSelf ) )
			{
				continue;
			}
			effectIndex = GetFxMapper().GetAreaEffectDataIndexByName( GetFxMapper().GetAreaEffectInFocusModeByIndex( i ).areaEffectID );
			if( effectIndex >= 0 )
			{
				ToggleAreaEffectVisibility( effectIndex, show );
			}
		}
	}

	protected virtual function ResolveAreaQuickHacksVisibility( show : Bool )
	{
		var i : Int32;
		var quickHackIndex : Int32;
		var availableQuickHacks : array< CName >;
		if( show )
		{
			availableQuickHacks = ( ( Device )( GetOwner() ) ).GetDevicePS().GetAvailableQuickHacks();
			if( availableQuickHacks.Size() > 0 )
			{
				m_availableQuickHacks = availableQuickHacks;
			}
		}
		for( i = 0; i < m_availableQuickHacks.Size(); i += 1 )
		{
			quickHackIndex = GetFxMapper().GetAreaEffectDataIndexByName( m_availableQuickHacks[ i ] );
			if( quickHackIndex >= 0 )
			{
				ToggleAreaEffectVisibility( quickHackIndex, show );
			}
		}
	}

	protected virtual function ResolveAreaQuickHacksVisibility( show : Bool, action : BaseScriptableAction )
	{
		var quickHackIndex : Int32;
		if( action == NULL )
		{
			return;
		}
		quickHackIndex = GetFxMapper().GetAreaEffectDataIndexByAction( action );
		if( ( show && ( quickHackIndex == m_activeEffectIndex ) ) || ( !( show ) && ( m_activeEffectIndex == -1 ) ) )
		{
			return;
		}
		if( quickHackIndex >= 0 )
		{
			if( show && ( m_activeEffectIndex >= 0 ) )
			{
				ToggleAreaEffectVisibility( m_activeEffectIndex, false, action );
				m_activeAction = NULL;
				m_activeEffectIndex = -1;
				return;
			}
			else
			{
				ToggleAreaEffectVisibility( quickHackIndex, show );
			}
		}
		else if( m_activeEffectIndex >= 0 )
		{
			ToggleAreaEffectVisibility( m_activeEffectIndex, false );
			show = false;
		}
		if( show )
		{
			m_activeAction = action;
			m_activeEffectIndex = quickHackIndex;
		}
		else
		{
			m_activeAction = NULL;
			m_activeEffectIndex = -1;
		}
	}

	protected virtual function ResolveAreaSpiderbotVisibility( show : Bool )
	{
		var i : Int32;
		var actionIndex : Int32;
		var availablespiderbotActions : array< CName >;
		if( show )
		{
			availablespiderbotActions = ( ( Device )( GetOwner() ) ).GetDevicePS().GetAvailableSpiderbotActions();
			if( availablespiderbotActions.Size() > 0 )
			{
				m_availablespiderbotActions = availablespiderbotActions;
			}
		}
		for( i = 0; i < m_availablespiderbotActions.Size(); i += 1 )
		{
			actionIndex = GetFxMapper().GetAreaEffectDataIndexByName( m_availablespiderbotActions[ i ] );
			if( actionIndex >= 0 )
			{
				ToggleAreaEffectVisibility( actionIndex, show );
			}
		}
	}

	protected function ToggleAreaEffectVisibility( effectDataIDX : Int32, show : Bool, optional responseData : IScriptable )
	{
		if( show )
		{
			StartDrawingAreaEffectRange( GetFxMapper().GetAreaEffectDataByIndex( effectDataIDX ) );
			if( GetFxMapper().GetAreaEffectDataByIndex( effectDataIDX ).highlightTargets )
			{
				StartHighlightingTargets( effectDataIDX, responseData );
			}
		}
		else
		{
			StopDrawingAreaEffectRange( GetFxMapper().GetAreaEffectDataByIndex( effectDataIDX ) );
			StopHighlightingTargets( effectDataIDX, responseData );
		}
	}

	protected function StartDrawingAreaEffectRange( effectData : AreaEffectData )
	{
		var effectBlackboard : worldEffectBlackboard;
		if( !( effectData.useIndicatorEffect ) || !( IsNameValid( effectData.indicatorEffectName ) ) )
		{
			return;
		}
		effectBlackboard = new worldEffectBlackboard;
		effectBlackboard.SetValue( 'change_size', effectData.indicatorEffectSize );
		GameObjectEffectHelper.StartEffectEvent( GetOwner(), effectData.indicatorEffectName, false, effectBlackboard );
	}

	protected function StopDrawingAreaEffectRange( effectData : AreaEffectData )
	{
		if( !( effectData.useIndicatorEffect ) || !( IsNameValid( effectData.indicatorEffectName ) ) )
		{
			return;
		}
		GameObjectEffectHelper.StopEffectEvent( GetOwner(), effectData.indicatorEffectName );
	}

	[ profile = "" ]
	protected function StartHighlightingTargets( effectDataIDX : Int32, optional responseData : IScriptable )
	{
		var effect : EffectInstance;
		var position : Vector4;
		var stimType : gamedataStimType;
		var device : Device;
		var bbData : PuppetForceVisionAppearanceData;
		var stimID : TweakDBID;
		if( GetFxMapper().GetAreaEffectDataByIndex( effectDataIDX ).effectInstance == NULL )
		{
			device = ( ( Device )( GetOwner() ) );
			position = GetOwner().GetAcousticQuerryStartPoint();
			if( IsNameValid( GetFxMapper().GetAreaEffectDataByIndex( effectDataIDX ).gameEffectOverrideName ) )
			{
				effect = GameInstance.GetGameEffectSystem( GetOwner().GetGame() ).CreateEffectStatic( 'forceVisionAppearanceOnNPC', GetFxMapper().GetAreaEffectDataByIndex( effectDataIDX ).gameEffectOverrideName, GetOwner() );
			}
			else
			{
				effect = GameInstance.GetGameEffectSystem( GetOwner().GetGame() ).CreateEffectStatic( 'forceVisionAppearanceOnNPC', 'inRange', GetOwner() );
			}
			bbData = new PuppetForceVisionAppearanceData;
			bbData.m_highlightType = GetFxMapper().GetAreaEffectDataByIndex( effectDataIDX ).highlightType;
			bbData.m_outlineType = GetFxMapper().GetAreaEffectDataByIndex( effectDataIDX ).outlineType;
			bbData.m_effectName = NameToString( GetFxMapper().GetAreaEffectDataNameByIndex( effectDataIDX ) );
			bbData.m_priority = GetFxMapper().GetAreaEffectDataByIndex( effectDataIDX ).highlightPriority;
			EffectData.SetFloat( effect.GetSharedData(), GetAllBlackboardDefs().EffectSharedData.maxPathLength, GetFxMapper().GetAreaEffectDataByIndex( effectDataIDX ).stimRange * 1.5 );
			EffectData.SetVector( effect.GetSharedData(), GetAllBlackboardDefs().EffectSharedData.position, position );
			EffectData.SetFloat( effect.GetSharedData(), GetAllBlackboardDefs().EffectSharedData.radius, GetFxMapper().GetAreaEffectDataByIndex( effectDataIDX ).stimRange );
			if( device )
			{
				bbData.m_investigationSlots = device.GetTotalCountOfInvestigationSlots();
				stimType = Device.MapStimType( GetFxMapper().GetAreaEffectDataByIndex( effectDataIDX ).stimType );
				EffectData.SetInt( effect.GetSharedData(), GetAllBlackboardDefs().EffectSharedData.stimType, ( ( Int32 )( stimType ) ) );
				stimID = TDBID.Create( ( "stims." + EnumValueToString( "gamedataStimType", ( ( Int32 )( stimType ) ) ) ) + "Stimuli" );
				if( TDBID.IsValid( stimID ) )
				{
					bbData.m_stimRecord = TweakDBInterface.GetStimRecord( stimID );
				}
			}
			EffectData.SetVariant( effect.GetSharedData(), GetAllBlackboardDefs().EffectSharedData.forceVisionAppearanceData, bbData );
			effect.Run();
			GetFxMapper().GetAreaEffectDataByIndex( effectDataIDX ).SetEffectInstance( effect );
		}
	}

	protected function StopHighlightingTargets( effectDataIDX : Int32, optional responseData : IScriptable )
	{
		var evt : ForceVisionApperanceEvent;
		var highlight : FocusForcedHighlightData;
		var effectInstance : EffectInstance;
		highlight = new FocusForcedHighlightData;
		highlight.sourceID = GetOwner().GetEntityID();
		highlight.sourceName = GetFxMapper().GetAreaEffectDataNameByIndex( effectDataIDX );
		highlight.highlightType = GetFxMapper().GetAreaEffectDataByIndex( effectDataIDX ).highlightType;
		highlight.outlineType = GetFxMapper().GetAreaEffectDataByIndex( effectDataIDX ).outlineType;
		highlight.priority = GetFxMapper().GetAreaEffectDataByIndex( effectDataIDX ).highlightPriority;
		highlight.isRevealed = true;
		effectInstance = GetFxMapper().GetAreaEffectDataByIndex( effectDataIDX ).effectInstance;
		if( effectInstance )
		{
			effectInstance.Terminate();
		}
		GetFxMapper().GetAreaEffectDataByIndex( effectDataIDX ).EffectInstanceClear();
		CancelForcedVisionAppearance( highlight );
		evt = new ForceVisionApperanceEvent;
		evt.apply = false;
		evt.forcedHighlight = highlight;
		evt.responseData = responseData;
		SendEventToBucket( highlight.sourceName, evt );
		RemoveBucket( highlight.sourceName );
	}

	protected function ForceVisionAppearance( data : FocusForcedHighlightData )
	{
		var evt : ForceVisionApperanceEvent;
		evt = new ForceVisionApperanceEvent;
		evt.forcedHighlight = data;
		evt.apply = true;
		GameInstance.GetPersistencySystem( GetOwner().GetGame() ).QueueEntityEvent( GetOwner().GetEntityID(), evt );
	}

	protected function CancelForcedVisionAppearance( data : FocusForcedHighlightData )
	{
		var evt : ForceVisionApperanceEvent;
		evt = new ForceVisionApperanceEvent;
		evt.forcedHighlight = data;
		evt.apply = false;
		if( GetOwner().HasHighlight( data.highlightType, data.outlineType, GetOwner().GetEntityID(), data.sourceName ) )
		{
			GameInstance.GetPersistencySystem( GetOwner().GetGame() ).QueueEntityEvent( GetOwner().GetEntityID(), evt );
		}
	}

	protected event OnAddForceHighlightTarget( evt : AddForceHighlightTargetEvent )
	{
		AddTargetToBucket( evt.effecName, evt.targetID );
	}

	protected event OnQHackWheelItemChanged( evt : QHackWheelItemChangedEvent )
	{
		if( !( evt.currentEmpty ) )
		{
			ResolveAreaQuickHacksVisibility( true, ( ( BaseScriptableAction )( evt.commandData.m_action ) ) );
		}
		else
		{
			ResolveAreaQuickHacksVisibility( false, m_activeAction );
		}
	}

	protected event OnResponse( evt : ResponseEvent )
	{
		var action : BaseScriptableAction;
		action = ( ( BaseScriptableAction )( evt.responseData ) );
		if( action )
		{
			ResolveAreaQuickHacksVisibility( true, action );
		}
	}

	protected function AddTargetToBucket( bucketName : CName, entityID : EntityID )
	{
		var i : Int32;
		var newBucket : GameEffectTargetVisualizationData;
		for( i = 0; i < m_forceHighlightTargetBuckets.Size(); i += 1 )
		{
			if( m_forceHighlightTargetBuckets[ i ].GetBucketName() == bucketName )
			{
				m_forceHighlightTargetBuckets[ i ].AddTargetToBucket( entityID );
				return;
			}
		}
		newBucket = new GameEffectTargetVisualizationData;
		newBucket.SetBucketName( bucketName );
		newBucket.AddTargetToBucket( entityID );
		m_forceHighlightTargetBuckets.PushBack( newBucket );
		1 + 1;
	}

	protected function SendEventToBucket( bucketName : CName, evt : Event )
	{
		var i : Int32;
		for( i = 0; i < m_forceHighlightTargetBuckets.Size(); i += 1 )
		{
			if( m_forceHighlightTargetBuckets[ i ].GetBucketName() == bucketName )
			{
				m_forceHighlightTargetBuckets[ i ].SendEventToAll( GetOwner().GetGame(), evt );
				return;
			}
		}
	}

	protected function RemoveBucket( bucketName : CName )
	{
		var i : Int32;
		for( i = 0; i < m_forceHighlightTargetBuckets.Size(); i += 1 )
		{
			if( m_forceHighlightTargetBuckets[ i ].GetBucketName() == bucketName )
			{
				m_forceHighlightTargetBuckets.Erase( i );
				return;
			}
		}
	}

}

class GameEffectTargetVisualizationData
{
	private var bucketName : CName;
	private var m_forceHighlightTargets : array< EntityID >;

	public const function GetBucketName() : CName
	{
		return bucketName;
	}

	public function SetBucketName( _bucketName : CName )
	{
		bucketName = _bucketName;
	}

	public function AddTargetToBucket( entityID : EntityID )
	{
		var i : Int32;
		for( i = 0; i < m_forceHighlightTargets.Size(); i += 1 )
		{
			if( m_forceHighlightTargets[ i ] == entityID )
			{
				return;
			}
		}
		m_forceHighlightTargets.PushBack( entityID );
	}

	public function ClearBucket()
	{
		m_forceHighlightTargets.Clear();
	}

	public function SendEventToAll( instance : GameInstance, evt : Event )
	{
		var i : Int32;
		for( i = 0; i < m_forceHighlightTargets.Size(); i += 1 )
		{
			GameInstance.GetPersistencySystem( instance ).QueueEntityEvent( m_forceHighlightTargets[ i ], evt );
		}
	}

}

