class ToggleBulletBendingEvent extends Event
{
	var m_enabled : Bool;
}

class Katana extends WeaponObject
{
	private editable var m_bentBulletTemplateName : CName;
	private editable var m_bulletBendingReferenceSlotName : CName;
	private var m_colliderComponent : IComponent;
	private var m_slotComponent : SlotComponent;

	protected event OnRequestComponents( ri : EntityRequestComponentsInterface )
	{
		EntityRequestComponentsInterface.RequestComponent( ri, 'BulletBendingCollider', 'entColliderComponent', false );
		EntityRequestComponentsInterface.RequestComponent( ri, 'SlotComponent', 'entSlotComponent', false );
	}

	protected event OnTakeControl( ri : EntityResolveComponentsInterface )
	{
		m_colliderComponent = EntityResolveComponentsInterface.GetComponent( ri, 'BulletBendingCollider' );
		m_slotComponent = ( ( SlotComponent )( EntityResolveComponentsInterface.GetComponent( ri, 'SlotComponent' ) ) );
	}

	protected event OnHit( evt : gameHitEvent )
	{
		GameObject.PlaySoundEvent( this, 'w_melee_katana_bending_sparks' );
		GameObjectEffectHelper.StartEffectEvent( this, 'deflection', false );
		QueueEventToPlayerEntity();
	}

	protected function QueueEventToPlayerEntity()
	{
		var playerID : EntityID;
		var magFieldHit : MagFieldHitEvent;
		playerID = GameInstance.GetPlayerSystem( GetGame() ).GetLocalPlayerMainGameObject().GetEntityID();
		magFieldHit = new MagFieldHitEvent;
		QueueEventForEntityID( playerID, magFieldHit );
	}

	public function GetSlotComponent() : SlotComponent
	{
		return m_slotComponent;
	}

	protected event OnToggleCollider( evt : ToggleBulletBendingEvent )
	{
		m_colliderComponent.Toggle( evt.m_enabled );
	}

	private function CalculateBendingVector( hitPosition : Vector4 ) : Vector4
	{
		var bendingFactor : Float;
		var referencePosition : Vector4;
		var slotTransform : WorldTransform;
		var distanceVector : Vector4;
		bendingFactor = TDB.GetFloat( ItemID.GetTDBID( GetItemID() ) + T".bulletBendingFactor", 1.0 );
		if( m_slotComponent.GetSlotTransform( m_bulletBendingReferenceSlotName, slotTransform ) )
		{
			referencePosition = WorldPosition.ToVector4( WorldTransform.GetWorldPosition( slotTransform ) );
		}
		else
		{
			referencePosition = GetWorldPosition();
		}
		distanceVector = hitPosition - referencePosition;
		return Vector4.Normalize( distanceVector ) * ( bendingFactor / Vector4.Length2D( distanceVector ) );
	}

}

import class gameEffectExecutor_KatanaBulletBending extends EffectExecutor_Scripted
{
	public import function SpawnFX( tag : CName, object : GameObject, from : Vector4, to : Vector4, attachSlotName : CName );

	public export function Process( ctx : EffectScriptContext, target : Entity, hitPosition : Vector4 )
	{
		var weapon : Katana;
		var hitDirection : Vector4;
		weapon = ( ( Katana )( target ) );
		EffectData.GetVector( EffectScriptContext.GetSharedData( ctx ), GetAllBlackboardDefs().EffectSharedData.forward, hitDirection );
		if( weapon )
		{
			SpawnRicochet( weapon, hitPosition, hitDirection );
			EffectData.SetVector( EffectScriptContext.GetSharedData( ctx ), GetAllBlackboardDefs().EffectSharedData.raycastEnd, hitPosition );
		}
	}

	private function SpawnRicochet( katana : Katana, out hitPosition : Vector4, hitDirection : Vector4 )
	{
		var slotName : CName;
		var slotTransform : WorldTransform;
		var itemRecordPath : TweakDBID;
		var ricochetDirection : Vector4;
		var ricochetReferenceOffset : Float;
		var ricochetBendFactor : Float;
		var hitPositionLocal : Vector4;
		var ricochetDirectionLocal : Vector4;
		var bladeLength : Float;
		var playerOrientation : Quaternion;
		var ricochetLocalY : Float;
		var ricochetLocalZOffsetMax : Float;
		var hitPositionLogicalLocalOffset : Vector3;
		var hitPositionVisualLocalOffset : Vector3;
		itemRecordPath = ItemID.GetTDBID( katana.GetItemID() );
		slotName = TDB.GetCName( itemRecordPath + T".magneticFieldSlotName", '' );
		ricochetReferenceOffset = TDB.GetFloat( itemRecordPath + T".bulletRicochetReferenceOffset", 0.0 );
		ricochetBendFactor = TDB.GetFloat( itemRecordPath + T".bulletRicochetBendFactor", 1.0 );
		bladeLength = TDB.GetFloat( itemRecordPath + T".bladeLength", 1.0 );
		ricochetLocalY = TDB.GetFloat( itemRecordPath + T".ricochetLocalY", -0.5 );
		ricochetLocalZOffsetMax = TDB.GetFloat( itemRecordPath + T".ricochetLocalZOffsetMax", 0.1 );
		hitPositionLogicalLocalOffset = TDB.GetVector3( itemRecordPath + T".hitPositionLogicalLocalOffset", Vector3( 0.0, 0.0, 0.0 ) );
		hitPositionVisualLocalOffset = TDB.GetVector3( itemRecordPath + T".hitPositionVisualLocalOffset", Vector3( 0.0, 0.0, 0.0 ) );
		katana.GetSlotComponent().GetSlotTransform( slotName, slotTransform );
		playerOrientation = GetPlayer( GetGameInstance() ).GetWorldOrientation();
		WorldTransform.SetOrientation( slotTransform, playerOrientation );
		hitPositionLocal = WorldPosition.ToVector4( WorldTransform.TransformPoint( WorldTransform.GetInverse( slotTransform ), hitPosition ) );
		hitPositionLocal.X = SgnF( hitPositionLocal.X ) * 0.01;
		hitPositionLocal.Y = 0.0;
		hitPositionLocal.Z = ClampF( hitPositionLocal.Z + RandNoiseF( 0, ricochetLocalZOffsetMax ), -( bladeLength ) * 0.5, bladeLength * 0.5 );
		hitPositionLocal += Vector4( hitPositionLogicalLocalOffset.X, hitPositionLogicalLocalOffset.Y, hitPositionLogicalLocalOffset.Z, 0.0 );
		ricochetDirectionLocal.X = hitPositionLocal.X;
		ricochetDirectionLocal.Y = ricochetLocalY;
		ricochetDirectionLocal.Z = ( hitPositionLocal.Z + ricochetReferenceOffset ) * ricochetBendFactor;
		ricochetDirection = Vector4.Normalize( Transform.TransformVector( WorldTransform._ToXForm( slotTransform ), ricochetDirectionLocal ) );
		hitPositionLocal += Vector4( hitPositionVisualLocalOffset.X, hitPositionVisualLocalOffset.Y, hitPositionVisualLocalOffset.Z, 0.0 );
		hitPosition = WorldPosition.ToVector4( WorldTransform.TransformPoint( slotTransform, hitPositionLocal ) );
		SpawnRicochetFx( katana, hitPosition, ricochetDirection, slotName );
	}

	private function SpawnBeamSpark( katana : Katana, bladeTransform : Transform, hitPosition : Vector4, slotName : CName )
	{
		var positionOnBlade : Vector4;
		var hitPositionLocal : Vector4;
		var bladePositionOffsetMax : Float;
		var bladeLength : Float;
		bladePositionOffsetMax = TDB.GetFloat( ItemID.GetTDBID( katana.GetItemID() ) + T".bladePositionOffsetMax", 0.0 );
		bladeLength = TDB.GetFloat( ItemID.GetTDBID( katana.GetItemID() ) + T".bladeLength", 1.0 );
		hitPositionLocal = Transform.TransformPoint( Transform.GetInverse( bladeTransform ), hitPosition );
		hitPositionLocal.X = 0.0;
		hitPositionLocal.Y = 0.0;
		hitPositionLocal.Z += RandNoiseF( 0, bladePositionOffsetMax, -( bladePositionOffsetMax ) );
		hitPositionLocal.Z = ClampF( hitPositionLocal.Z, -( bladeLength ) * 0.5, bladeLength * 0.5 );
		positionOnBlade = Transform.TransformPoint( bladeTransform, hitPositionLocal );
		SpawnFX( 'lightning', katana, positionOnBlade, hitPosition, slotName );
	}

	private function SpawnRicochetFx( katana : Katana, position : Vector4, direction : Vector4, slotName : CName )
	{
		var targetPosition : Vector4;
		targetPosition = position + ( Vector4.Normalize( direction ) * 50.0 );
		SpawnFX( 'sparks', katana, position, targetPosition, slotName );
		SpawnFX( 'trail', katana, position, targetPosition, slotName );
	}

}

