import class AttackData extends IScriptable
{
	private import var attackType : gamedataAttackType;
	private import var instigator : weak< GameObject >;
	private import var source : weak< GameObject >;
	private import var weapon : weak< WeaponObject >;
	private import var attackDefinition : IAttack;
	private import var attackPosition : Vector4;
	private import var weaponCharge : Float;
	private import var numRicochetBounces : Int32;
	private import var numAttackSpread : Int32;
	private import var attackTime : Float;
	private import var triggerMode : gamedataTriggerMode;
	private var flags : array< SHitFlag >;
	private var statusEffects : array< SHitStatusEffect >;
	private var hitType : gameuiHitType;
	private var vehicleImpactForce : Float;
	private var minimumHealthPercent : Float;
	private var additionalCritChance : Float;
	private var randRoll : Float;
	private var hitReactionMin : Int32;
	private var hitReactionMax : Int32;

	public function SetAttackType( attackTypeOverride : gamedataAttackType )
	{
		attackType = attackTypeOverride;
	}

	public static function IsMelee( attackType : gamedataAttackType ) : Bool
	{
		return ( attackType == gamedataAttackType.Melee || attackType == gamedataAttackType.QuickMelee ) || attackType == gamedataAttackType.StrongMelee;
	}

	public static function IsQuickMelee( attackType : gamedataAttackType ) : Bool
	{
		return attackType == gamedataAttackType.QuickMelee;
	}

	public static function IsLightMelee( attackType : gamedataAttackType ) : Bool
	{
		return attackType == gamedataAttackType.Melee;
	}

	public static function IsStrongMelee( attackType : gamedataAttackType ) : Bool
	{
		return attackType == gamedataAttackType.StrongMelee;
	}

	public static function IsReflect( attackType : gamedataAttackType ) : Bool
	{
		return attackType == gamedataAttackType.Reflect;
	}

	public static function IsRangedOnly( attackType : gamedataAttackType ) : Bool
	{
		return attackType == gamedataAttackType.Ranged;
	}

	public static function IsRangedOrDirect( attackType : gamedataAttackType ) : Bool
	{
		return attackType == gamedataAttackType.Direct || attackType == gamedataAttackType.Ranged;
	}

	public static function IsRangedOrDirectOrThrown( attackType : gamedataAttackType ) : Bool
	{
		return ( ( attackType == gamedataAttackType.Direct || attackType == gamedataAttackType.Ranged ) || attackType == gamedataAttackType.Thrown ) || attackType == gamedataAttackType.Explosion;
	}

	public static function IsExplosion( attackType : gamedataAttackType ) : Bool
	{
		return attackType == gamedataAttackType.Explosion;
	}

	public static function IsPressureWave( attackType : gamedataAttackType ) : Bool
	{
		return attackType == gamedataAttackType.PressureWave;
	}

	public static function IsAreaOfEffect( attackType : gamedataAttackType ) : Bool
	{
		return attackType == gamedataAttackType.PressureWave || attackType == gamedataAttackType.Explosion;
	}

	public static function IsDismembermentCause( attackType : gamedataAttackType ) : Bool
	{
		return attackType == gamedataAttackType.ChargedWhipAttack || attackType == gamedataAttackType.Explosion;
	}

	public static function IsEffect( attackType : gamedataAttackType ) : Bool
	{
		return attackType == gamedataAttackType.Effect;
	}

	public static function IsDoT( attackType : gamedataAttackType ) : Bool
	{
		return attackType == gamedataAttackType.Effect;
	}

	public static function IsDoT( attackData : AttackData ) : Bool
	{
		return attackData.HasFlag( hitFlag.DamageOverTime );
	}

	public static function IsHack( attackType : gamedataAttackType ) : Bool
	{
		return attackType == gamedataAttackType.Hack;
	}

	public static function IsThrown( attackType : gamedataAttackType ) : Bool
	{
		return attackType == gamedataAttackType.Thrown;
	}

	public static function IsWhip( attackType : gamedataAttackType ) : Bool
	{
		return attackType == gamedataAttackType.WhipAttack || attackType == gamedataAttackType.ChargedWhipAttack;
	}

	public function PreAttack()
	{
		var attackRecord : Attack_Record;
		if( attackDefinition )
		{
			attackRecord = attackDefinition.GetRecord();
		}
		if( attackRecord )
		{
			attackType = attackRecord.AttackType().Type();
			hitReactionMin = attackRecord.HitReactionSeverityMin();
			hitReactionMax = attackRecord.HitReactionSeverityMax();
		}
	}

	public function SetInstigator( i : weak< GameObject > )
	{
		instigator = i;
	}

	public function SetSource( s : weak< GameObject > )
	{
		source = s;
	}

	public function SetWeapon( w : weak< WeaponObject > )
	{
		weapon = w;
	}

	public function SetAttackDefinition( a : IAttack )
	{
		attackDefinition = a;
	}

	public function SetHitType( h : gameuiHitType )
	{
		hitType = h;
	}

	public function SetAttackPosition( position : Vector4 )
	{
		attackPosition = position;
	}

	public function SetWeaponCharge( charge : Float )
	{
		weaponCharge = charge;
	}

	public function SetVehicleImpactForce( force : Float )
	{
		vehicleImpactForce = force;
	}

	public function SetAdditionalCritChance( f : Float )
	{
		additionalCritChance = f;
	}

	public function SetHitReactionSeverityMin( i : Int32 )
	{
		hitReactionMin = i;
	}

	public function SetHitReactionSeverityMax( i : Int32 )
	{
		hitReactionMax = i;
	}

	public function SetAttackTime( time : Float )
	{
		attackTime = time;
	}

	public function SetTriggerMode( mode : gamedataTriggerMode )
	{
		triggerMode = mode;
	}

	public function SetRandRoll( roll : Float )
	{
		randRoll = roll;
	}

	public function SetMinimumHealthPercent( value : Float )
	{
		minimumHealthPercent = value;
	}

	public const function GetInstigator() : weak< GameObject >
	{
		return instigator;
	}

	public const function GetSource() : weak< GameObject >
	{
		return source;
	}

	public const function GetWeapon() : weak< WeaponObject >
	{
		return weapon;
	}

	public const function GetAttackDefinition() : IAttack
	{
		return attackDefinition;
	}

	public const function GetAttackPosition() : Vector4
	{
		return attackPosition;
	}

	public const function GetAttackType() : gamedataAttackType
	{
		return attackType;
	}

	public const function GetStatusEffects() : array< SHitStatusEffect >
	{
		return statusEffects;
	}

	public const function GetHitType() : gameuiHitType
	{
		return hitType;
	}

	public const function GetWeaponCharge() : Float
	{
		return weaponCharge;
	}

	public const function GetNumRicochetBounces() : Int32
	{
		return numRicochetBounces;
	}

	public const function GetNumAttackSpread() : Int32
	{
		return numAttackSpread;
	}

	public const function GetVehicleImpactForce() : Float
	{
		return vehicleImpactForce;
	}

	public const function GetAdditionalCritChance() : Float
	{
		return additionalCritChance;
	}

	public const function GetHitReactionSeverityMin() : Int32
	{
		return hitReactionMin;
	}

	public const function GetHitReactionSeverityMax() : Int32
	{
		return hitReactionMax;
	}

	public const function GetAttackTime() : Float
	{
		return attackTime;
	}

	public const function GetTriggerMode() : gamedataTriggerMode
	{
		return triggerMode;
	}

	public const function GetRandRoll() : Float
	{
		return randRoll;
	}

	public const function GetMinimumHealthPercent() : Float
	{
		return minimumHealthPercent;
	}

	public const function GetAttackSubtype() : gamedataAttackSubtype
	{
		var record : Attack_Melee_Record;
		var attackSubTypeRecord : AttackSubtype_Record;
		if( AttackData.IsMelee( attackType ) )
		{
			record = ( ( Attack_Melee_Record )( attackDefinition.GetRecord() ) );
			if( record )
			{
				attackSubTypeRecord = record.AttackSubtype();
				if( attackSubTypeRecord )
				{
					return attackSubTypeRecord.Type();
				}
			}
		}
		return gamedataAttackSubtype.Invalid;
	}

	public static function CanEffectCriticallyHit( attackData : AttackData, statsSystem : StatsSystem ) : Bool
	{
		var effectTag : CName;
		var rec : Attack_GameEffect_Record;
		var result : Bool;
		rec = ( ( Attack_GameEffect_Record )( attackData.GetAttackDefinition().GetRecord() ) );
		effectTag = rec.AttackTag();
		switch( effectTag )
		{
			case 'Bleeding':
				result = statsSystem.GetStatValue( attackData.GetInstigator().GetEntityID(), gamedataStatType.CanBleedingCriticallyHit ) > 0.0;
			break;
			case 'Poisoned':
				result = false;
			break;
			case 'Electrocuted':
				result = false;
			break;
			case 'Burning':
				result = attackData.HasFlag( hitFlag.AirDropBurningDoT );
			break;
			default:
				return false;
		}
		return result;
	}

	public static function IsPlayerInCombat( attackData : AttackData ) : Bool
	{
		var psmBB : IBlackboard;
		var combatState : Int32;
		psmBB = GameInstance.GetBlackboardSystem( GetGameInstance() ).GetLocalInstanced( attackData.GetInstigator().GetEntityID(), GetAllBlackboardDefs().PlayerStateMachine );
		combatState = psmBB.GetInt( GetAllBlackboardDefs().PlayerStateMachine.Combat );
		return combatState == ( ( Int32 )( gamePSMCombat.InCombat ) );
	}

	public static function CanGrenadeCriticallyHit( attackData : AttackData, statsSystem : StatsSystem ) : Bool
	{
		var result : Bool;
		result = statsSystem.GetStatValue( attackData.GetInstigator().GetEntityID(), gamedataStatType.CanGrenadesCriticallyHit ) > 0.0;
		return result;
	}

	public function WasBlocked() : Bool
	{
		return HasFlag( hitFlag.WasBlocked );
	}

	public function WasBulletBlocked() : Bool
	{
		return HasFlag( hitFlag.WasBulletBlocked );
	}

	public function WasDeflected() : Bool
	{
		return HasFlag( hitFlag.WasDeflected );
	}

	public function WasBulletDeflected() : Bool
	{
		return HasFlag( hitFlag.WasBulletDeflected ) || HasFlag( hitFlag.WasBulletParried );
	}

	public function WasDeflectedAny() : Bool
	{
		return ( HasFlag( hitFlag.WasDeflected ) || HasFlag( hitFlag.WasBulletDeflected ) ) || HasFlag( hitFlag.WasBulletParried );
	}

	public function DoesAttackWeaponHaveTag( tag : CName ) : Bool
	{
		var record : Item_Record;
		var tags : array< CName >;
		var i : Int32;
		if( !( weapon ) )
		{
			return false;
		}
		record = TDB.GetItemRecord( ItemID.GetTDBID( weapon.GetItemID() ) );
		tags = record.Tags();
		for( i = 0; i < tags.Size(); i += 1 )
		{
			if( tags[ i ] == tag )
			{
				return true;
			}
		}
		return false;
	}

	public function ClearDamage()
	{
		if( HasFlag( hitFlag.CannotModifyDamage ) )
		{
			return;
		}
	}

	public function AddStatusEffect( effect : TweakDBID, stacks : Float )
	{
		var newStatusEffect : SHitStatusEffect;
		newStatusEffect.id = effect;
		newStatusEffect.stacks = stacks;
		statusEffects.PushBack( newStatusEffect );
	}

	public const function HasFlag( flag : hitFlag ) : Bool
	{
		var i : Int32;
		for( i = 0; i < flags.Size(); i += 1 )
		{
			if( flags[ i ].flag == flag )
			{
				return true;
			}
		}
		return false;
	}

	public static function HasFlag( const flags : ref< array< SHitFlag > >, flag : hitFlag ) : Bool
	{
		var i : Int32;
		for( i = 0; i < flags.Size(); i += 1 )
		{
			if( flags[ i ].flag == flag )
			{
				return true;
			}
		}
		return false;
	}

	public function AddFlag( flag : hitFlag, sourceName : CName )
	{
		var f : SHitFlag;
		var min, max, insertionIndex : Int32;
		if( HasFlag( flag ) )
		{
			return;
		}
		f.flag = flag;
		f.source = sourceName;
		insertionIndex = 0;
		min = 0;
		max = flags.Size() - 1;
		while( min < max )
		{
			insertionIndex = ( min + max ) / 2;
			if( flag == flags[ insertionIndex ].flag )
			{
				break;
			}
			else if( flag < flags[ insertionIndex ].flag )
			{
				max = insertionIndex - 1;
			}
			else
			{
				min = insertionIndex + 1;
				if( min > max )
				{
					insertionIndex += 1;
				}
			}
		}
		flags.Insert( insertionIndex, f );
	}

	public function RemoveFlag( flag : hitFlag, sourceName : CName )
	{
		var f : SHitFlag;
		var removeIndex : Int32;
		f.flag = flag;
		f.source = sourceName;
		removeIndex = flags.FindFirst( f );
		if( removeIndex >= 0 )
		{
			flags.Erase( removeIndex );
		}
	}

	public function RemoveFlag( flag : hitFlag )
	{
		var i : Int32;
		var index : Int32;
		for( i = 0; i < flags.Size(); i += 1 )
		{
			if( flags[ i ].flag == flag )
			{
				index = i;
				break;
			}
			else
			{
				index = -1;
			}
		}
		if( index >= 0 )
		{
			flags.EraseFast( index );
		}
	}

	public const function GetFlags() : array< SHitFlag >
	{
		return flags;
	}

}

