class GenericHitPrereqState extends PrereqState
{
	var m_listener : HitCallback;
	var m_hitEvent : gameHitEvent;
	var m_missEvent : gameMissEvent;

	public final function SetHitEvent( hitEvent : gameHitEvent )
	{
		m_hitEvent = hitEvent;
	}

	public const final function GetHitEvent() : gameHitEvent
	{
		return m_hitEvent;
	}

	public final function SetMissEvent( missEvent : gameMissEvent )
	{
		m_missEvent = missEvent;
	}

	public const final function GetMissEvent() : gameMissEvent
	{
		return m_missEvent;
	}

	public virtual function Evaluate( hitEvent : gameHitEvent ) : Bool
	{
		var i : Int32;
		var result : Bool;
		const var prereq : GenericHitPrereq;
		prereq = ( ( GenericHitPrereq )( GetPrereq() ) );
		for( i = 0; i < prereq.m_conditions.Size(); i += 1 )
		{
			result = prereq.m_conditions[ i ].Evaluate( hitEvent );
			if( !( result ) )
			{
				return false;
			}
		}
		if( prereq.m_ignoreSelfInflictedPressureWave )
		{
			if( ( hitEvent.target.IsPlayer() && hitEvent.attackData.GetInstigator().IsPlayer() ) && hitEvent.attackData.GetAttackType() == gamedataAttackType.PressureWave )
			{
				return false;
			}
		}
		return true;
	}

	public virtual function OnMissTriggered( missEvent : gameMissEvent )
	{
		var i : Int32;
		const var prereq : GenericHitPrereq;
		prereq = ( ( GenericHitPrereq )( GetPrereq() ) );
		for( i = 0; i < prereq.m_conditions.Size(); i += 1 )
		{
			prereq.m_conditions[ i ].OnMissTriggered( missEvent );
		}
	}

	protected function GetObjectToCheck( const obj : ref< String >, hitEvent : gameHitEvent ) : weak< GameObject >
	{
		switch( obj )
		{
			case "Instigator":
				return hitEvent.attackData.GetInstigator();
			case "Source":
				return hitEvent.attackData.GetSource();
			case "Target":
				return hitEvent.target;
			default:
				return NULL;
		}
	}

}

class GenericHitPrereq extends IScriptablePrereq
{
	var m_isSync : Bool;
	var m_processMiss : Bool;
	var m_callbackType : gameDamageCallbackType;
	var m_pipelineStage : gameDamagePipelineStage;
	var m_pipelineType : DMGPipelineType;
	var m_attackType : gamedataAttackType;
	var m_conditions : array< BaseHitPrereqCondition >;
	var m_ignoreSelfInflictedPressureWave : Bool;

	protected export const override function OnRegister( state : PrereqState, game : GameInstance, context : IScriptable ) : Bool
	{
		var owner : weak< GameObject >;
		var castedState : GenericHitPrereqState;
		owner = ( ( GameObject )( context ) );
		castedState = ( ( GenericHitPrereqState )( state ) );
		if( m_callbackType == gameDamageCallbackType.HitTriggered )
		{
			castedState.m_listener = new HitTriggeredCallback;
		}
		else if( m_callbackType == gameDamageCallbackType.HitReceived )
		{
			castedState.m_listener = new HitReceivedCallback;
		}
		else if( m_callbackType == gameDamageCallbackType.PipelineProcessed )
		{
			castedState.m_listener = new PipelineProcessedCallback;
		}
		castedState.m_listener.RegisterState( castedState );
		if( m_isSync )
		{
			GameInstance.GetDamageSystem( game ).RegisterSyncListener( castedState.m_listener, owner.GetEntityID(), m_callbackType, m_pipelineStage, m_pipelineType );
			if( m_processMiss )
			{
				GameInstance.GetDamageSystem( game ).RegisterSyncListener( castedState.m_listener, owner.GetEntityID(), gameDamageCallbackType.MissTriggered, m_pipelineStage, m_pipelineType );
			}
		}
		else
		{
			GameInstance.GetDamageSystem( game ).RegisterListener( castedState.m_listener, owner.GetEntityID(), m_callbackType, m_pipelineType );
			if( m_processMiss )
			{
				GameInstance.GetDamageSystem( game ).RegisterListener( castedState.m_listener, owner.GetEntityID(), gameDamageCallbackType.MissTriggered, m_pipelineType );
			}
		}
		return false;
	}

	protected export const override function OnUnregister( state : PrereqState, game : GameInstance, context : IScriptable )
	{
		var owner : GameObject;
		var castedState : GenericHitPrereqState;
		owner = ( ( GameObject )( context ) );
		castedState = ( ( GenericHitPrereqState )( state ) );
		if( m_isSync )
		{
			GameInstance.GetDamageSystem( game ).UnregisterSyncListener( castedState.m_listener, owner.GetEntityID(), m_callbackType, m_pipelineStage );
			if( m_processMiss )
			{
				GameInstance.GetDamageSystem( game ).UnregisterSyncListener( castedState.m_listener, owner.GetEntityID(), gameDamageCallbackType.MissTriggered, m_pipelineStage );
			}
		}
		else
		{
			GameInstance.GetDamageSystem( game ).UnregisterListener( castedState.m_listener, owner.GetEntityID(), m_callbackType );
			if( m_processMiss )
			{
				GameInstance.GetDamageSystem( game ).UnregisterListener( castedState.m_listener, owner.GetEntityID(), gameDamageCallbackType.MissTriggered );
			}
		}
		castedState.m_listener = NULL;
	}

	protected export override function Initialize( recordID : TweakDBID )
	{
		var i : Int32;
		var str : String;
		var conds : array< weak< HitPrereqCondition_Record > >;
		var condition : BaseHitPrereqCondition;
		m_conditions.Clear();
		m_isSync = TweakDBInterface.GetBool( recordID + T".isSynchronous", false );
		m_processMiss = TweakDBInterface.GetBool( recordID + T".processMiss", false );
		str = TweakDBInterface.GetString( recordID + T".callbackType", "HitTriggered" );
		m_callbackType = ( ( gameDamageCallbackType )( ( ( Int32 )( EnumValueFromString( "gameDamageCallbackType", str ) ) ) ) );
		str = TweakDBInterface.GetString( recordID + T".pipelineStage", "Process" );
		m_pipelineStage = ( ( gameDamagePipelineStage )( ( ( Int32 )( EnumValueFromString( "gameDamagePipelineStage", str ) ) ) ) );
		str = TweakDBInterface.GetString( recordID + T".pipelineType", "All" );
		m_pipelineType = ( ( DMGPipelineType )( ( ( Int32 )( EnumValueFromString( "DMGPipelineType", str ) ) ) ) );
		TweakDBInterface.GetHitPrereqRecord( recordID ).Conditions( conds );
		for( i = 0; i < conds.Size(); i += 1 )
		{
			condition = CreateHitCondition( conds[ i ] );
			if( condition )
			{
				m_conditions.PushBack( condition );
			}
		}
		m_ignoreSelfInflictedPressureWave = TweakDBInterface.GetBool( recordID + T".ignoreSelfInflictedPressureWave", true );
	}

	private function CreateHitCondition( record : HitPrereqCondition_Record ) : BaseHitPrereqCondition
	{
		var condition : BaseHitPrereqCondition;
		var type : gamedataHitPrereqConditionType;
		type = record.Type().Type();
		switch( type )
		{
			case gamedataHitPrereqConditionType.AttackSubType:
				condition = new AttackSubtypeHitPrereqCondition;
			break;
			case gamedataHitPrereqConditionType.AttackType:
				condition = new AttackTypeHitPrereqCondition;
			break;
			case gamedataHitPrereqConditionType.AttackTag:
				condition = new AttackTagHitPrereqCondition;
			break;
			case gamedataHitPrereqConditionType.BodyPart:
				condition = new BodyPartHitPrereqCondition;
			break;
			case gamedataHitPrereqConditionType.DamageOverTimeType:
				condition = new DamageOverTimeTypeHitPrereqCondition;
			break;
			case gamedataHitPrereqConditionType.DamageType:
				condition = new DamageTypeHitPrereqCondition;
			break;
			case gamedataHitPrereqConditionType.DistanceCovered:
				condition = new DistanceCoveredHitPrereqCondition;
			break;
			case gamedataHitPrereqConditionType.HitFlag:
				condition = new HitFlagHitPrereqCondition;
			break;
			case gamedataHitPrereqConditionType.InstigatorType:
				condition = new InstigatorTypeHitPrereqCondition;
			break;
			case gamedataHitPrereqConditionType.SameTarget:
				condition = new SameTargetHitPrereqCondition;
			break;
			case gamedataHitPrereqConditionType.SourceType:
				condition = new SourceTypeHitPrereqCondition;
			break;
			case gamedataHitPrereqConditionType.StatPool:
				condition = new StatPoolHitPrereqCondition;
			break;
			case gamedataHitPrereqConditionType.StatPoolComparison:
				condition = new StatPoolComparisonHitPrereqCondition;
			break;
			case gamedataHitPrereqConditionType.Stat:
				condition = new StatHitPrereqCondition;
			break;
			case gamedataHitPrereqConditionType.StatusEffectPresent:
				condition = new StatusEffectPresentHitPrereqCondition;
			break;
			case gamedataHitPrereqConditionType.TargetIsCrowd:
				condition = new TargetNPCIsCrowdHitPrereqCondition;
			break;
			case gamedataHitPrereqConditionType.TargetKilled:
				condition = new TargetKilledHitPrereqCondition;
			break;
			case gamedataHitPrereqConditionType.TargetCanGetKilledByDamage:
				condition = new TargetCanGetKilledByDamagePrereqCondition;
			break;
			case gamedataHitPrereqConditionType.TargetBreachCanGetKilledByDamage:
				condition = new TargetBreachCanGetKilledByDamagePrereqCondition;
			break;
			case gamedataHitPrereqConditionType.TargetNPCRarity:
				condition = new TargetNPCRarityHitPrereqCondition;
			break;
			case gamedataHitPrereqConditionType.TargetNPCType:
				condition = new TargetNPCTypeHitPrereqCondition;
			break;
			case gamedataHitPrereqConditionType.AgentMoving:
				condition = new AgentMovingHitPrereqCondition;
			break;
			case gamedataHitPrereqConditionType.AmmoState:
				condition = new AmmoStateHitPrereqCondition;
			break;
			case gamedataHitPrereqConditionType.WeaponType:
				condition = new WeaponTypeHitPrereqCondition;
			break;
			case gamedataHitPrereqConditionType.WeaponItemType:
				condition = new WeaponItemTypeHitPrereqCondition;
			break;
			case gamedataHitPrereqConditionType.WeaponEvolution:
				condition = new WeaponEvolutionHitPrereqCondition;
			break;
			case gamedataHitPrereqConditionType.TargetType:
				condition = new TargetTypeHitPrereqCondition;
			break;
			case gamedataHitPrereqConditionType.ReactionPreset:
				condition = new ReactionPresetHitPrereqCondition;
			break;
			case gamedataHitPrereqConditionType.TriggerMode:
				condition = new TriggerModeHitPrereqCondition;
			break;
			case gamedataHitPrereqConditionType.ConsecutiveHits:
				condition = new ConsecutiveHitsPrereqCondition;
			break;
			case gamedataHitPrereqConditionType.SelfHit:
				condition = new SelfHitPrereqCondition;
			break;
			case gamedataHitPrereqConditionType.HitIsQuickhackPresentInQueue:
				condition = new HitIsQuickhackPresentInQueuePrereqCondition;
			break;
			case gamedataHitPrereqConditionType.EffectNamePresent:
				condition = new EffectNamePresentHitPrereqCondition;
			break;
			case gamedataHitPrereqConditionType.DismembermentTriggered:
				condition = new DismembermentTriggeredHitPrereqCondition;
			break;
			default:
				return NULL;
			break;
		}
		condition.SetData( record.GetID() );
		return condition;
	}

}

class HitCallback extends ScriptedDamageSystemListener
{
	protected var m_state : weak< GenericHitPrereqState >;

	public virtual function RegisterState( state : PrereqState )
	{
		m_state = ( ( GenericHitPrereqState )( state ) );
	}

	protected virtual function UpdateState( hitEvent : gameHitEvent )
	{
		var checkPassed : Bool;
		m_state.SetHitEvent( hitEvent );
		checkPassed = m_state.Evaluate( hitEvent );
		if( checkPassed )
		{
			m_state.OnChangedRepeated( false );
		}
	}

	protected override function OnMissTriggered( missEvent : gameMissEvent )
	{
		m_state.SetMissEvent( missEvent );
		m_state.OnMissTriggered( missEvent );
	}

}

class HitTriggeredCallback extends HitCallback
{

	protected export override function OnHitTriggered( hitEvent : gameHitEvent )
	{
		UpdateState( hitEvent );
	}

	protected override function OnHitReceived( hitEvent : gameHitEvent ) {}
}

class HitReceivedCallback extends HitCallback
{

	protected override function OnHitTriggered( hitEvent : gameHitEvent ) {}

	protected export override function OnHitReceived( hitEvent : gameHitEvent )
	{
		UpdateState( hitEvent );
	}

}

class PipelineProcessedCallback extends HitCallback
{

	protected override function OnHitTriggered( hitEvent : gameHitEvent ) {}

	protected override function OnHitReceived( hitEvent : gameHitEvent ) {}

	protected override function OnPipelineProcessed( hitEvent : gameHitEvent )
	{
		UpdateState( hitEvent );
	}

}

