class MonoWireQuickHackApplyEffector extends AbstractApplyQuickhackEffector
{
	var m_hasSpreadWindowBeenOpened : Bool;
	var m_targetsToSpreadQuickhack : array< MonowireSpreadableNPC >;
	var m_timeOfPossibleSpread : Float;
	var m_spreadWindowTime : Float;
	default m_spreadWindowTime = 0.1f;
	var m_spreadCallbackID : DelayID;

	protected override function ProcessApplyQuickhackAction( hitEvent : gameHitEvent, playerPuppet : PlayerPuppet, targetScriptedPuppet : ScriptedPuppet )
	{
		var playerFreeCostActionIDName : TweakDBID;
		var hitAttackData : AttackData;
		var weaponCharge : Float;
		var hasSpreadablePerkPurchased : Bool;
		hitAttackData = hitEvent.attackData;
		playerFreeCostActionIDName = ( ( TweakDBID )( m_blackboard.GetVariant( GetAllBlackboardDefs().PlayerStateMachine.CostFreeActionID ) ) );
		weaponCharge = hitAttackData.GetWeaponCharge();
		if( ( AttackData.IsStrongMelee( hitAttackData.GetAttackType() ) && !( TDBID.IsValid( playerFreeCostActionIDName ) ) ) && ( weaponCharge >= 0.80000001 ) )
		{
			SpawnFXs( hitEvent, targetScriptedPuppet, true );
			ProcessStrongAttack( playerPuppet, targetScriptedPuppet, hitAttackData.GetWeapon() );
			return;
		}
		hasSpreadablePerkPurchased = RPGManager.HasStatFlag( playerPuppet, gamedataStatType.CanSpreadMonoWireQuickhack );
		if( hasSpreadablePerkPurchased && AttackData.IsLightMelee( hitAttackData.GetAttackType() ) )
		{
			ProcessNormalAttack( playerPuppet, targetScriptedPuppet, hitAttackData.GetAttackTime(), hitEvent );
		}
	}

	public function SpawnFXs( hitEvent : gameHitEvent, npcPuppet : ScriptedPuppet, isStrongImpact : Bool )
	{
		var transform : WorldTransform;
		var position : WorldPosition;
		var effect : FxResource;
		var hitShapeData : HitShapeData;
		effect = ( ( isStrongImpact ) ? ( npcPuppet.GetFxResourceByKey( 'monowire_relictree_impact' ) ) : ( npcPuppet.GetFxResourceByKey( 'monowire_relictree_light_impact' ) ) );
		if( FxResource.IsValid( effect ) )
		{
			if( hitEvent.hitRepresentationResult.hitShapes.Size() > 0 )
			{
				hitShapeData = ( ( HitShapeData )( hitEvent.hitRepresentationResult.hitShapes[ 0 ] ) );
			}
			WorldPosition.SetVector4( position, hitShapeData.result.hitPositionEnter );
			WorldTransform.SetWorldPosition( transform, position );
			GameInstance.GetFxSystem( npcPuppet.GetGame() ).SpawnEffect( effect, transform );
		}
	}

	private function ProcessStrongAttack( playerOwnerPuppet : PlayerPuppet, targetScriptedPuppet : ScriptedPuppet, weaponObject : WeaponObject )
	{
		var monowireQuickhackData : QuickhackData;
		var quickhackUploadTime : Float;
		var monoWireQuickhackContagiousTargetCallback : OnMonowireQuickhackContagiousTargetStatusAppliedCallback;
		monowireQuickhackData = RPGManager.GetMonoWireQuickHackData( playerOwnerPuppet, targetScriptedPuppet, weaponObject );
		if( !( monowireQuickhackData ) )
		{
			return;
		}
		monowireQuickhackData.m_action.m_IsAppliedByMonowire = true;
		TriggerSpecialQuickHackAttack( playerOwnerPuppet, monowireQuickhackData, m_applyQuickhackDelayConst );
		if( RPGManager.HasStatFlag( playerOwnerPuppet, gamedataStatType.CanSpreadMonoWireQuickhack ) )
		{
			quickhackUploadTime = monowireQuickhackData.m_uploadTime;
			monoWireQuickhackContagiousTargetCallback = OnMonowireQuickhackContagiousTargetStatusAppliedCallback.Create( targetScriptedPuppet );
			GameInstance.GetDelaySystem( playerOwnerPuppet.GetGame() ).DelayCallback( monoWireQuickhackContagiousTargetCallback, quickhackUploadTime + m_applyQuickhackDelayConst );
			m_blackboard.SetVariant( GetAllBlackboardDefs().PlayerStateMachine.MeleeSpreadableQuickhackActionID, monowireQuickhackData.m_action.GetObjectActionID() );
		}
	}

	private function ProcessNormalAttack( playerOwnerPuppet : PlayerPuppet, targetScriptedPuppet : ScriptedPuppet, attackTime : Float, hitEvent : gameHitEvent )
	{
		var onSpreadWindowCallback : OnMonowireWindowToSpreadQuickhackCallback;
		var spreadableQuickhackActionID : TweakDBID;
		var newSpreadableNPC : MonowireSpreadableNPC;
		spreadableQuickhackActionID = ( ( TweakDBID )( m_blackboard.GetVariant( GetAllBlackboardDefs().PlayerStateMachine.MeleeSpreadableQuickhackActionID ) ) );
		if( spreadableQuickhackActionID == TDBID.None() )
		{
			return;
		}
		if( !( m_hasSpreadWindowBeenOpened ) && ( m_timeOfPossibleSpread == 0.0 ) )
		{
			m_timeOfPossibleSpread = attackTime;
			onSpreadWindowCallback = OnMonowireWindowToSpreadQuickhackCallback.Create( this, playerOwnerPuppet );
			m_spreadCallbackID = GameInstance.GetDelaySystem( playerOwnerPuppet.GetGame() ).DelayCallback( onSpreadWindowCallback, m_spreadWindowTime );
			m_hasSpreadWindowBeenOpened = true;
		}
		if( StatusEffectSystem.ObjectHasStatusEffect( targetScriptedPuppet, T"BaseStatusEffect.MonoWireQuickhackContagiousHittableTarget" ) && !( ScriptedPuppet.IsAlive( targetScriptedPuppet ) ) )
		{
			GameInstance.GetDelaySystem( playerOwnerPuppet.GetGame() ).CancelCallback( m_spreadCallbackID );
			ClearSpreadAttack();
			return;
		}
		if( attackTime != m_timeOfPossibleSpread )
		{
			GameInstance.GetDelaySystem( playerOwnerPuppet.GetGame() ).CancelCallback( m_spreadCallbackID );
			ClearSpreadAttack();
			ProcessNormalAttack( playerOwnerPuppet, targetScriptedPuppet, attackTime, hitEvent );
			return;
		}
		newSpreadableNPC = new MonowireSpreadableNPC;
		newSpreadableNPC.m_NPCPuppet = ( ( NPCPuppet )( targetScriptedPuppet ) );
		newSpreadableNPC.m_HitEvent = hitEvent;
		m_targetsToSpreadQuickhack.PushBack( newSpreadableNPC );
	}

	public function ClearSpreadAttack()
	{
		m_hasSpreadWindowBeenOpened = false;
		m_targetsToSpreadQuickhack.Clear();
		m_timeOfPossibleSpread = 0.0;
	}

	protected export override function ActionOff( owner : GameObject )
	{
		ClearSpreadAttack();
	}

}

class MonowireSpreadableNPC
{
	var m_NPCPuppet : weak< NPCPuppet >;
	var m_HitEvent : gameHitEvent;
}

