//---------------------------------------------------------------------------------------
//  FILE:    XComGameState_Item.uc
//  AUTHOR:  Ryan McFall  --  10/10/2013
//  PURPOSE: This object represents the instance data for an item in the tactical game for
//           X-Com
//           
//---------------------------------------------------------------------------------------
//  Copyright (c) 2016 Firaxis Games, Inc. All rights reserved.
//---------------------------------------------------------------------------------------
class XComGameState_Item extends XComGameState_BaseObject 
	dependson(XGInventoryNativeBase)	
	implements(UIQueryInterfaceItem, X2VisualizedInterface)
	native(Core);

//Instance-only variables
var() int Quantity;
var int LastQuantityChange;
var() int MergedItemCount;
var() bool bMergedOut;
var() StateObjectReference LoadedAmmo;
var() StateObjectReference CosmeticUnitRef; //Specifies a 'unit' associated with this item. Used with the gremlin, which is technically an item but moves and acts like a unit
var() StateObjectReference AttachedUnitRef; //Certain items such as the gremlin can be temporarily attached to another unit. This field tracks that attachment
var() StateObjectReference TechRef; // Certain items act as requirements for specific tech states (Facility Leads)

//Templated variables
var() ELocation ItemLocation; //Items not located on a unit will have ELocation_None - visual attachment point
var() int Ammo;
var() EInventorySlot InventorySlot;     //  Gameplay location - item is pretty much in the templated slot or in a backpack

var() protected name            m_TemplateName;
var() protected X2ItemTemplate  m_ItemTemplate;

//  Weapon upgrade system
var() protected array<name>                     m_arrWeaponUpgradeNames;
var() protected array<X2WeaponUpgradeTemplate>  m_arrWeaponUpgradeTemplates;

var() StateObjectReference          OwnerStateObject;
var() array<StateObjectReference>   ContainedItems;         //  XComGameState_Items which are inside of this one.   
var() StateObjectReference          SoldierKitOwner;        //  if this item is a soldier kit, this is the soldier that died

var() array<StatBoost>				StatBoosts;  // Used for Personal Combat Sims to hold rolled on stat boost data

var() TWeaponAppearance WeaponAppearance; // Used for storing customization state for Primary Weapons
var() string Nickname; //Storage for the the name of the item

function SetInitialState(XGItem Visualizer)
{
	local XGInventoryItem Item;

	Item = XGInventoryItem(Visualizer);
	ItemLocation = Item.m_eSlot;	
}

function Actor FindOrCreateVisualizer( optional XComGameState Gamestate = none )
{
	local Actor ItemVisualizer;

	ItemVisualizer = `XCOMHISTORY.GetVisualizer( ObjectID );
	if (ItemVisualizer == none)
	{
		class'XGItem'.static.CreateVisualizer( self );
	}

	return ItemVisualizer;
}

function SyncVisualizer(optional XComGameState GameState = none)
{
}

function AppendAdditionalSyncActions( out VisualizationTrack BuildTrack )
{
}

static function X2ItemTemplateManager GetMyTemplateManager()
{
	return class'X2ItemTemplateManager'.static.GetItemTemplateManager();
}

simulated function name GetMyTemplateName()
{
	return m_TemplateName;
}

simulated native function X2ItemTemplate GetMyTemplate();

simulated native function TTile GetTileLocation();

/// <summary>
/// Called immediately prior to loading, this method is called on each state object so that its resources can be ready when the map completes loading. Request resources
/// should output an array of strings containing archetype paths to load
/// </summary>
event RequestResources(out array<string> ArchetypesToLoad)
{
	local X2EquipmentTemplate EquipmentTemplate;	

	super.RequestResources(ArchetypesToLoad);

	//Load our related resources
	EquipmentTemplate = X2EquipmentTemplate(GetMyTemplate());
	if(EquipmentTemplate != none)
	{
		if(EquipmentTemplate.GameArchetype != "")
		{
			ArchetypesToLoad.AddItem(EquipmentTemplate.GameArchetype);
		}

		if(EquipmentTemplate.AltGameArchetype != "")
		{
			ArchetypesToLoad.AddItem(EquipmentTemplate.AltGameArchetype);
		}

		if(EquipmentTemplate.CosmeticUnitTemplate != "")
		{
			ArchetypesToLoad.AddItem(EquipmentTemplate.CosmeticUnitTemplate);
		}
	}
}

function OnCreation(X2ItemTemplate ItemTemplate)
{	
	local X2ItemTemplateManager ItemManager;
	local X2EquipmentTemplate EquipmentTemplate;
	local X2WeaponTemplate WeaponTemplate;
	local StatBoost ItemStatBoost;
	local int idx;

	m_ItemTemplate = ItemTemplate;
	m_TemplateName = ItemTemplate.DataName;

	Ammo = GetClipSize();

	EquipmentTemplate = X2EquipmentTemplate(m_ItemTemplate);

	if(EquipmentTemplate != none)
	{
		ItemManager = GetMyTemplateManager();

		for(idx = 0; idx < EquipmentTemplate.StatsToBoost.Length; idx++)
		{
			if(ItemManager.GetItemStatBoost(EquipmentTemplate.StatBoostPowerLevel, EquipmentTemplate.StatsToBoost[idx], ItemStatBoost))
			{
				StatBoosts.AddItem(ItemStatBoost);
			}
		}
		WeaponTemplate = X2WeaponTemplate(m_ItemTemplate);

		if( WeaponTemplate != None )
		{
			//  primary weapon goes in the right hand, everything else is stowed
			if( WeaponTemplate.InventorySlot == eInvSlot_PrimaryWeapon )
			{
				ItemLocation = eSlot_RightHand;
			}
			else
			{
				ItemLocation = WeaponTemplate.StowedLocation;
			}
		}
	}
}

function OnBeginTacticalPlay()
{
	local X2EventManager EventManager;
	local Object ThisObj;

	super.OnBeginTacticalPlay();

	//Only items with cosmetic units need to listen for these. If you expand this conditional, make sure you need to as
	//having too many items respond to these events would be costly.
	EventManager = `XEVENTMGR;
	ThisObj = self;	
	if( CosmeticUnitRef.ObjectID > 0 )
	{	
		AttachedUnitRef = OwnerStateObject; //We default to being attached to our owner
		EventManager.RegisterForEvent( ThisObj, 'AbilityActivated', OnAbilityActivated, ELD_OnStateSubmitted,,); //Move if we're ordered to
		EventManager.RegisterForEvent( ThisObj, 'UnitDied', OnUnitDied, ELD_OnStateSubmitted,,); //Return to owner if target unit dies or play death anim if owner dies
		EventManager.RegisterForEvent( ThisObj, 'UnitEvacuated', OnUnitEvacuated, ELD_OnStateSubmitted,,); //For gremlin, to evacuate with its owner
		EventManager.RegisterForEvent( ThisObj, 'ItemRecalled', OnItemRecalled, ELD_OnStateSubmitted,,); //Return to owner when specifically requested 
		EventManager.RegisterForEvent( ThisObj, 'ForceItemRecalled', OnForceItemRecalled, ELD_OnStateSubmitted,,); //Return to owner when specifically told
	}
}


function OnEndTacticalPlay()
{
	local StateObjectReference EmptyReference;	
	local X2EventManager EventManager;
	local Object ThisObj;

	super.OnEndTacticalPlay();

	EventManager = `XEVENTMGR;
	ThisObj = self;
	if( CosmeticUnitRef.ObjectID > 0 )
	{
		EventManager.UnRegisterFromEvent( ThisObj, 'UnitMoveFinished' );
		EventManager.UnRegisterFromEvent( ThisObj, 'AbilityActivated' );
		EventManager.UnRegisterFromEvent( ThisObj, 'UnitDied' );
		EventManager.UnRegisterFromEvent( ThisObj, 'ItemRecalled' );
		EventManager.UnRegisterFromEvent( ThisObj, 'ForceItemRecalled' );
	}

	Ammo = GetClipSize();
	MergedItemCount = 0;
	bMergedOut = false;
	CosmeticUnitRef = EmptyReference;
	LoadedAmmo = EmptyReference;
}

function EventListenerReturn OnAbilityActivated(Object EventData, Object EventSource, XComGameState GameState, Name EventID)
{
	local XComGameStateContext_Ability AbilityContext;
	local X2GameRulesetVisibilityInterface TargetInterface;
	local XComGameStateHistory History;	
	local XComGameState NewGameState;
	local XComGameState_Item NewItemState;
	local X2AbilityTemplate AbilityTemplate;
	local XComGameState_Unit AttachedUnitState, NewCosmeticState;

	if( CosmeticUnitRef.ObjectID > 0 && GameState.GetContext().InterruptionStatus != eInterruptionStatus_Interrupt)
	{		
		History = `XCOMHISTORY;

		AbilityContext = XComGameStateContext_Ability(GameState.GetContext());
		if (AbilityContext != none && AbilityContext.InputContext.ItemObject.ObjectID == ObjectID) //If we were used in this ability, then process
		{
			AbilityTemplate = class'X2AbilityTemplateManager'.static.GetAbilityTemplateManager().FindAbilityTemplate(AbilityContext.InputContext.AbilityTemplateName);
			if (AbilityTemplate.bStationaryWeapon)
				return ELR_NoInterrupt;

			if( AbilityContext.InputContext.PrimaryTarget.ObjectID > 0 )
			{
				TargetInterface = X2GameRulesetVisibilityInterface(History.GetGameStateForObjectID(AbilityContext.InputContext.PrimaryTarget.ObjectID));
				if( TargetInterface != none )
				{
					NewGameState = class'XComGameStateContext_ChangeContainer'.static.CreateChangeState("Equipment reassigned");
					
					//Update the attached unit for this item
					NewItemState = XComGameState_Item(NewGameState.CreateStateObject(self.Class, ObjectID));
					NewItemState.AttachedUnitRef = AbilityContext.InputContext.PrimaryTarget;
					NewGameState.AddStateObject(NewItemState);

					AttachedUnitState = XComGameState_Unit(History.GetGameStateForObjectID(NewItemState.AttachedUnitRef.ObjectID));
					if (AttachedUnitState != none)
					{
						NewCosmeticState = XComGameState_Unit(NewGameState.CreateStateObject(class'XComGameState_Unit', CosmeticUnitRef.ObjectID));
						NewCosmeticState.SetVisibilityLocation( AttachedUnitState.TileLocation );
						NewGameState.AddStateObject(NewCosmeticState);
					}
					
					`GAMERULES.SubmitGameState(NewGameState);
				}
				else
				{
					`redscreen("Gremlin was unable to move to target for ability"@AbilityContext.InputContext.AbilityTemplateName@" with target object:"@AbilityContext.InputContext.PrimaryTarget.ObjectID);
				}
			}
			else if( AbilityContext.InputContext.TargetLocations.Length > 0 )
			{
				// don't do anything, but this case shouldn't cause a redscreen either
			}
			else
			{
				`redscreen("Gremlin was unable to find a target to move to for ability"@AbilityContext.InputContext.AbilityTemplateName);
			}
		}
	}

	return ELR_NoInterrupt;
}

function EventListenerReturn OnItemRecalled(Object EventData, Object EventSource, XComGameState GameState, Name EventID)
{
	local XComGameState_Ability AbilityState;
	local XComGameState_Item RecalledItem;
	
	if (CosmeticUnitRef.ObjectID > 0)
	{		
		AbilityState = XComGameState_Ability(EventData);
		RecalledItem = AbilityState.GetSourceWeapon();
		if (RecalledItem.ObjectID == ObjectID)
		{
			RecallItem(RecalledItem, GameState);
		}
	}
	return ELR_NoInterrupt;
}

function EventListenerReturn OnForceItemRecalled(Object EventData, Object EventSource, XComGameState GameState, Name EventID)
{
	local XComGameStateContext_Ability AbilityContext;
	local XComGameState_Item RecalledItem;
	local XComGameStateHistory History;

	if (CosmeticUnitRef.ObjectID > 0)
	{
		// Check if this unit is targeted by the ability
		AbilityContext = XComGameStateContext_Ability(GameState.GetContext());

		if( (AbilityContext.InputContext.PrimaryTarget.ObjectID == CosmeticUnitRef.ObjectID) ||
			(AbilityContext.InputContext.MultiTargets.Find('ObjectID', CosmeticUnitRef.ObjectID) != INDEX_NONE) )
		{
			// This item was a target of the ability
			History = `XCOMHISTORY;

			RecalledItem = XComGameState_Item(History.GetGameStateForObjectID(CosmeticUnitRef.ObjectID));
			RecallItem(RecalledItem, GameState);
		}
	}
	return ELR_NoInterrupt;
}

private function RecallItem(const XComGameState_Item RecalledItem, XComGameState GameState)
{
	local XComGameStateContext_Ability AbilityContext;
	local XComGameState_Unit OwnerState;
	local XComGameState_Item NewRecalledItem;
	local XComGameState NewGameState;
	local XGUnit Visualizer;
	local vector MoveToLoc;
	local XComGameStateHistory History;
	local XComGameState_Unit CosmeticUnitState;
	local bool MoveFromTarget;

	History = `XCOMHISTORY;
	AbilityContext = XComGameStateContext_Ability( GameState.GetContext( ) );

	OwnerState = XComGameState_Unit(History.GetGameStateForObjectID(OwnerStateObject.ObjectID));
	if (OwnerState.TileLocation != RecalledItem.GetTileLocation())
	{
		if (AttachedUnitRef != OwnerStateObject)
		{
			NewGameState = class'XComGameStateContext_ChangeContainer'.static.CreateChangeState("Equipment recalled");					
			//Update the attached unit for this item
			NewRecalledItem = XComGameState_Item(NewGameState.CreateStateObject(self.Class, ObjectID));
			NewRecalledItem.AttachedUnitRef = OwnerStateObject;
			NewGameState.AddStateObject(NewRecalledItem);					

			`GAMERULES.SubmitGameState(NewGameState);
		}

		CosmeticUnitState = XComGameState_Unit( History.GetGameStateForObjectID( RecalledItem.CosmeticUnitRef.ObjectID ) );

		MoveFromTarget = (OwnerState.TileLocation == RecalledItem.GetTileLocation()) && (AbilityContext != none) && (AbilityContext.InputContext.SourceObject.ObjectID == OwnerState.ObjectID);

		if (MoveFromTarget && AbilityContext.InputContext.TargetLocations.Length > 0)
		{
			MoveToLoc = AbilityContext.InputContext.TargetLocations[0];
			CosmeticUnitState.SetVisibilityLocationFromVector( MoveToLoc );
		}

		//  Now move it move it
		Visualizer = XGUnit(History.GetVisualizer(CosmeticUnitRef.ObjectID));
		MoveToLoc = `XWORLD.GetPositionFromTileCoordinates(OwnerState.TileLocation);
		Visualizer.MoveToLocation(MoveToLoc, CosmeticUnitState);
	}
}

function EventListenerReturn OnUnitDied(Object EventData, Object EventSource, XComGameState GameState, Name EventID)
{
	local XComGameStateHistory History;
	local XComGameState_Unit UnitState, CosmeticUnit;
	local XComGameState NewGameState;
	local XComGameState_Item ItemState;
	local vector NewLocation;
	local XComGameStateContext_ChangeContainer ChangeContext;

	UnitState = XComGameState_Unit(EventData);
	//  was this the unit we are attached to or our owner?
	if (UnitState.ObjectID == AttachedUnitRef.ObjectID || UnitState.ObjectID == OwnerStateObject.ObjectID)
	{
		History = `XCOMHISTORY;
		UnitState = XComGameState_Unit(History.GetGameStateForObjectID(OwnerStateObject.ObjectID));
		CosmeticUnit = XComGameState_Unit(History.GetGameStateForObjectID(CosmeticUnitRef.ObjectID));
		//  if it was not our owner, reattach to them
		if ( ( UnitState.ObjectID != OwnerStateObject.ObjectID ) || (OwnerStateObject.ObjectID != AttachedUnitRef.ObjectID) )
		{
			NewGameState = class'XComGameStateContext_ChangeContainer'.static.CreateChangeState("Attached Unit Died");
			ItemState = XComGameState_Item(NewGameState.CreateStateObject(Class, ObjectID));
			ItemState.AttachedUnitRef = OwnerStateObject;
			NewGameState.AddStateObject(ItemState);
			`GAMERULES.SubmitGameState(NewGameState);
						
			if (UnitState != none && CosmeticUnit != none && CosmeticUnit.TileLocation != UnitState.TileLocation)
			{
				NewLocation = `XWORLD.GetPositionFromTileCoordinates(UnitState.TileLocation);
				XGUnit(CosmeticUnit.GetVisualizer()).MoveToLocation(NewLocation);
			}
		}
		//  if it was our owner, we have to die too
		else
		{
			if (CosmeticUnit != none)
			{
				NewGameState = class'XComGameStateContext_ChangeContainer'.static.CreateChangeState("Owner Unit Died");
				ChangeContext = XComGameStateContext_ChangeContainer(NewGameState.GetContext());
				ChangeContext.BuildVisualizationFn = ItemOwnerDeathVisualization;
				CosmeticUnit = XComGameState_Unit(NewGameState.CreateStateObject(CosmeticUnit.Class, CosmeticUnit.ObjectID));
				CosmeticUnit.SetCurrentStat(eStat_HP, 0);
				NewGameState.AddStateObject(CosmeticUnit);
				`GAMERULES.SubmitGameState(NewGameState);
			}				
		}
	}

	return ELR_NoInterrupt;
}

function ItemOwnerDeathVisualization(XComGameState VisualizeGameState, out array<VisualizationTrack> OutVisualizationTracks)
{
	local VisualizationTrack DeathTrack;
	local XComGameState_Unit UnitState;
	local XComGameStateHistory History;

	//  This game state should contain just the cosmetic unit state having its HP set to 0.
	foreach VisualizeGameState.IterateByClassType(class'XComGameState_Unit', UnitState)
	{
		break;
	}
	History = `XCOMHISTORY;
	DeathTrack.StateObject_OldState = History.GetGameStateForObjectID(UnitState.ObjectID, , VisualizeGameState.HistoryIndex - 1);
	DeathTrack.StateObject_NewState = UnitState;
	DeathTrack.TrackActor = UnitState.GetVisualizer();

	class'X2Action_Death'.static.AddToVisualizationTrack(DeathTrack, VisualizeGameState.GetContext());
	OutVisualizationTracks.AddItem(DeathTrack);
}

function EventListenerReturn OnUnitEvacuated(Object EventData, Object EventSource, XComGameState GameState, Name EventID)
{
	local XComGameStateHistory History;
	local XComGameState_Unit UnitState, CosmeticUnit;
	local XComGameState NewGameState;
	local XComGameStateContext_ChangeContainer ChangeContext;


	UnitState = XComGameState_Unit(EventData);

	// If the unit that evacuated was our owner, then we must also evacuate.
	if (UnitState.ObjectID == OwnerStateObject.ObjectID)
	{
		History = `XCOMHISTORY;
		CosmeticUnit = XComGameState_Unit(History.GetGameStateForObjectID(CosmeticUnitRef.ObjectID));

		if (CosmeticUnit != none)
		{
			NewGameState = class'XComGameStateContext_ChangeContainer'.static.CreateChangeState("Owner Unit Evacuated");
			ChangeContext = XComGameStateContext_ChangeContainer(NewGameState.GetContext());
			ChangeContext.BuildVisualizationFn = ItemOwnerEvacVisualization;

			// Visualize this context in the same visblock as the evacuating unit.
			ChangeContext.SetDesiredVisualizationBlockIndex(GameState.HistoryIndex);

			CosmeticUnit = XComGameState_Unit(NewGameState.CreateStateObject(CosmeticUnit.Class, CosmeticUnit.ObjectID));
			CosmeticUnit.EvacuateUnit(NewGameState);
			NewGameState.AddStateObject(CosmeticUnit);
			`GAMERULES.SubmitGameState(NewGameState);
		}
	}

	return ELR_NoInterrupt;
}

function ItemOwnerEvacVisualization(XComGameState VisualizeGameState, out array<VisualizationTrack> OutVisualizationTracks)
{
	local VisualizationTrack EvacTrack;
	local XComGameState_Unit UnitState;
	local XComGameStateHistory History;
	local X2Action_Evac EvacAction;

	//  This game state should contain just the cosmetic unit state that's evacuating.
	foreach VisualizeGameState.IterateByClassType(class'XComGameState_Unit', UnitState)
	{
		break;
	}

	History = `XCOMHISTORY;
	EvacTrack.StateObject_OldState = History.GetGameStateForObjectID(UnitState.ObjectID, , VisualizeGameState.HistoryIndex - 1);
	EvacTrack.StateObject_NewState = UnitState;
	EvacTrack.TrackActor = UnitState.GetVisualizer();

	EvacAction = X2Action_Evac(class'X2Action_Evac'.static.AddToVisualizationTrack(EvacTrack, VisualizeGameState.GetContext()));
	EvacAction.bIsVisualizingGremlin = true;

	//Hide the pawn explicitly now - in case the vis block doesn't complete immediately to trigger an update
	class'X2Action_RemoveUnit'.static.AddToVisualizationTrack(EvacTrack, VisualizeGameState.GetContext());

	OutVisualizationTracks.AddItem(EvacTrack);
}

simulated function array<name> GetMyWeaponUpgradeTemplateNames()
{
	return m_arrWeaponUpgradeNames;
}

simulated function array<X2WeaponUpgradeTemplate> GetMyWeaponUpgradeTemplates()
{
	local X2ItemTemplateManager ItemMgr;
	local int i;

	ItemMgr = class'X2ItemTemplateManager'.static.GetItemTemplateManager();
	if (m_arrWeaponUpgradeTemplates.Length > m_arrWeaponUpgradeNames.Length)
		m_arrWeaponUpgradeTemplates.Length = 0;

	for (i = 0; i < m_arrWeaponUpgradeNames.Length; ++i)
	{
		if (m_arrWeaponUpgradeTemplates[i] == none || m_arrWeaponUpgradeTemplates[i].DataName != m_arrWeaponUpgradeNames[i])
			m_arrWeaponUpgradeTemplates[i] = X2WeaponUpgradeTemplate(ItemMgr.FindItemTemplate(m_arrWeaponUpgradeNames[i]));
	}

	return m_arrWeaponUpgradeTemplates;
}

simulated function array<string> GetMyWeaponUpgradeTemplatesCategoryIcons()
{
	local array<X2WeaponUpgradeTemplate> Templates;
	local int TemplateIdx, LocalIdx;
	local array<string> LocalIcons, FinalIcons; 

	Templates = GetMyWeaponUpgradeTemplates();
	for( TemplateIdx = 0; TemplateIdx < Templates.length; TemplateIdx++ )
	{
		LocalIcons = Templates[TemplateIdx].GetAttachmentInventoryCategoryImages(self);
		
		for( LocalIdx = 0; LocalIdx < LocalIcons.length; LocalIdx++ )
			FinalIcons.AddItem(LocalIcons[LocalIdx]);
	}
	return FinalIcons; 
}

//  Note: this should only be called after verifying the upgrade can be applied successfully, there is no error checking here.
simulated function ApplyWeaponUpgradeTemplate(X2WeaponUpgradeTemplate UpgradeTemplate, optional int SlotIndex = -1)
{
	// If a specific slot was not provided or the slot is past any equipped upgrades, add it to the end of the upgrade list
	if (SlotIndex == -1 || SlotIndex >= m_arrWeaponUpgradeNames.Length)
	{
		m_arrWeaponUpgradeNames.AddItem(UpgradeTemplate.DataName);
		m_arrWeaponUpgradeTemplates.AddItem(UpgradeTemplate);
	}
	else // Otherwise replace the specific slot index
	{
		m_arrWeaponUpgradeNames[SlotIndex] = UpgradeTemplate.DataName;
		m_arrWeaponUpgradeTemplates[SlotIndex] = UpgradeTemplate;
	}

	//  adjust anything upgrades could affect
	Ammo = GetClipSize();
}

simulated function X2WeaponUpgradeTemplate DeleteWeaponUpgradeTemplate(int SlotIndex)
{
	local X2WeaponUpgradeTemplate UpgradeTemplate;
	
	// If an upgrade template exists at the slot index, delete it
	if (SlotIndex < m_arrWeaponUpgradeNames.Length && m_arrWeaponUpgradeNames[SlotIndex] != '')
	{
		UpgradeTemplate = m_arrWeaponUpgradeTemplates[SlotIndex];
		
		m_arrWeaponUpgradeNames[SlotIndex] = '';
		m_arrWeaponUpgradeTemplates[SlotIndex] = none;
	}

	return UpgradeTemplate;
}

simulated function WipeUpgradeTemplates()
{
	m_arrWeaponUpgradeNames.Length = 0;
	m_arrWeaponUpgradeTemplates.Length = 0;

	//  adjust anything upgrades could affect
	Ammo = GetClipSize();
}

simulated function bool HasBeenModified()
{
	return Nickname != "" || GetMyWeaponUpgradeTemplateNames().Length > 0;
}

simulated function bool IsStartingItem()
{
	return GetMyTemplate().StartingItem && !HasBeenModified();
}

simulated function bool HasLoadedAmmo()
{
	return LoadedAmmo.ObjectID != 0;
}

simulated native function X2ItemTemplate GetLoadedAmmoTemplate(const XComGameState_Ability AbilityState);

//If the template for this item is of type X2EquipmentTemplate or derived, this will return / load
//the archetypes for that template
simulated function Object GetGameArchetype(optional bool bAlt = false)
{
	local Object GameArchetype;
	local string strArchetype;

	GetMyTemplate();
	
	if( bAlt )
	{
		strArchetype = X2EquipmentTemplate(m_ItemTemplate).AltGameArchetype;
	}
	else
	{
		strArchetype = X2EquipmentTemplate(m_ItemTemplate).GameArchetype;
	}
	
	GameArchetype = `CONTENT.RequestGameArchetype(strArchetype);		
	
	return GameArchetype;	
}

simulated function array<WeaponAttachment> GetWeaponAttachments(optional bool bGetContent=true)
{
	local X2WeaponTemplate WeaponTemplate;
	local array<WeaponAttachment> Attachments;
	local array<X2WeaponUpgradeTemplate> UpgradeTemplates;
	local int i, j, k;
	local bool bReplaced;
	local delegate<X2TacticalGameRulesetDataStructures.CheckUpgradeStatus> ValidateAttachmentFn;

	WeaponTemplate = X2WeaponTemplate(GetMyTemplate());
	if (WeaponTemplate != none)
	{
		//  copy all default attachments from the weapon template
		for (i = 0; i < WeaponTemplate.DefaultAttachments.Length; ++i)
		{
			Attachments.AddItem(WeaponTemplate.DefaultAttachments[i]);
		}
		UpgradeTemplates = GetMyWeaponUpgradeTemplates();
		for (i = 0; i < UpgradeTemplates.Length; ++i)
		{
			for (j = 0; j < UpgradeTemplates[i].UpgradeAttachments.Length; ++j)
			{
				if (UpgradeTemplates[i].UpgradeAttachments[j].ApplyToWeaponTemplate != WeaponTemplate.DataName)
					continue;

				ValidateAttachmentFn = UpgradeTemplates[i].UpgradeAttachments[j].ValidateAttachmentFn;
				if( ValidateAttachmentFn != None && !ValidateAttachmentFn(UpgradeTemplates) )
				{
					continue;
				}

				bReplaced = false;
				//  look for sockets already known that an upgrade will replace
				for (k = 0; k < Attachments.Length; ++k)
				{
					if( Attachments[k].AttachSocket == UpgradeTemplates[i].UpgradeAttachments[j].AttachSocket )
					{
						Attachments[k].AttachMeshName = UpgradeTemplates[i].UpgradeAttachments[j].AttachMeshName;
						bReplaced = true;
						break;
					}
				}
				//  if not replacing an existing mesh, add the upgrade attachment as a new one
				if( !bReplaced )
				{
					Attachments.AddItem(UpgradeTemplates[i].UpgradeAttachments[j]);
				}
			}
		}
		if (bGetContent)
		{
			for (i = 0; i < Attachments.Length; ++i)
			{
				Attachments[i].LoadedObject = `CONTENT.RequestGameArchetype(Attachments[i].AttachMeshName);
				Attachments[i].LoadedProjectileTemplate = `CONTENT.RequestGameArchetype(Attachments[i].AttachProjectileName);
			}
		}
	}
	return Attachments;
}

simulated function int GetClipSize()
{
	local XComGameStateHistory History;
	local XComGameState_Item SpecialAmmo;
	local int i, ClipSize, AdjustedClipSize;

	ClipSize = -1;
	GetMyTemplate();
	GetMyWeaponUpgradeTemplates();

	if (m_ItemTemplate.IsA('X2WeaponTemplate'))
	{
		ClipSize = X2WeaponTemplate(m_ItemTemplate).iClipSize;
		History = `XCOMHISTORY;
		for (i = 0; i < m_arrWeaponUpgradeTemplates.Length; ++i)
		{
			if (m_arrWeaponUpgradeTemplates[i].AdjustClipSizeFn != none)
			{
				if (m_arrWeaponUpgradeTemplates[i].AdjustClipSizeFn(m_arrWeaponUpgradeTemplates[i], self, ClipSize, AdjustedClipSize))
					ClipSize = AdjustedClipSize;
			}
		}
		if (LoadedAmmo.ObjectID != 0)
		{
			SpecialAmmo = XComGameState_Item(History.GetGameStateForObjectID(LoadedAmmo.ObjectID));
			if (SpecialAmmo != none)
			{
				ClipSize += SpecialAmmo.GetClipSize();
			}
		}
	}
	else if (m_ItemTemplate.IsA('X2AmmoTemplate'))
	{
		ClipSize = X2AmmoTemplate(m_ItemTemplate).ModClipSize;
	}

	return ClipSize;
}

simulated function bool HasInfiniteAmmo()
{
	GetMyTemplate();
	if (m_ItemTemplate.IsA('X2WeaponTemplate'))
		return X2WeaponTemplate(m_ItemTemplate).InfiniteAmmo;

	return false;
}

simulated function int GetItemSize()
{
	return GetMyTemplate().iItemSize;
}

simulated native function int GetItemRange(const XComGameState_Ability AbilityState);
simulated native function int GetItemRadius(const XComGameState_Ability AbilityState);
simulated native function float GetItemCoverage(const XComGameState_Ability AbilityState);

native function bool Validate(XComGameState HistoryGameState, INT GameStateIndex) const;

simulated function GetBaseWeaponDamageValue(XComGameState_BaseObject TargetObjectState, out WeaponDamageValue DamageValue)
{
	local X2WeaponTemplate WeaponTemplate;

	WeaponTemplate = X2WeaponTemplate(GetMyTemplate());
	if (WeaponTemplate != none)
	{
		DamageValue = WeaponTemplate.BaseDamage;
	}
}

simulated function GetWeaponDamageValue(XComGameState_BaseObject TargetObjectState, name Tag, out WeaponDamageValue DamageValue)
{
	local X2WeaponTemplate WeaponTemplate;
	local int i;

	WeaponTemplate = X2WeaponTemplate(GetMyTemplate());
	if (WeaponTemplate != none && Tag != '')
	{
		for (i = 0; i < WeaponTemplate.ExtraDamage.Length; ++i)
		{
			if (WeaponTemplate.ExtraDamage[i].Tag == Tag)
			{
				DamageValue = WeaponTemplate.ExtraDamage[i];
				return;
			}
		}
	}
}

simulated function int GetItemEnvironmentDamage()
{
	GetMyTemplate();
	if (m_ItemTemplate.IsA('X2WeaponTemplate'))
		return X2WeaponTemplate(m_ItemTemplate).iEnvironmentDamage;

	return class'X2WeaponTemplate'.default.iEnvironmentDamage;
}

simulated function int GetItemSoundRange()
{
	local int iSoundRange;

	iSoundRange = class'X2WeaponTemplate'.default.iSoundRange;
	GetMyTemplate();
	
	if (m_ItemTemplate.IsA('X2WeaponTemplate'))
	{
		iSoundRange = X2WeaponTemplate(m_ItemTemplate).iSoundRange;
	}

	return iSoundRange;
}

// Used only for stat displays. GetClipSize() is functional method for tactical.
simulated function int GetItemClipSize()
{
	GetMyTemplate();
	if (m_ItemTemplate.IsA('X2WeaponTemplate'))
		return X2WeaponTemplate(m_ItemTemplate).iClipSize;

	return class'X2WeaponTemplate'.default.iClipSize;
}

simulated function int GetItemAimModifier()
{
	GetMyTemplate();
	if (m_ItemTemplate.IsA('X2WeaponTemplate'))
		return X2WeaponTemplate(m_ItemTemplate).Aim;

	return class'X2WeaponTemplate'.default.Aim;
}

simulated function int GetItemCritChance()
{
	GetMyTemplate();
	if (m_ItemTemplate.IsA('X2WeaponTemplate'))
		return X2WeaponTemplate(m_ItemTemplate).CritChance;

	return class'X2WeaponTemplate'.default.CritChance;
}

simulated function int GetItemPierceValue()
{
	GetMyTemplate();
	if (m_ItemTemplate.IsA('X2WeaponTemplate'))
		return X2WeaponTemplate(m_ItemTemplate).BaseDamage.Pierce;

	return class'X2WeaponTemplate'.default.BaseDamage.Pierce;
}

simulated function bool SoundOriginatesFromOwnerLocation()
{
	GetMyTemplate();
	if (m_ItemTemplate.IsA('X2WeaponTemplate'))
		return X2WeaponTemplate(m_ItemTemplate).bSoundOriginatesFromOwnerLocation;
	return true;
}

simulated function name GetWeaponCategory()
{
	local X2WeaponTemplate Template;

	Template = X2WeaponTemplate(GetMyTemplate());
	if (Template != none)
		return Template.WeaponCat;

	return '';
}

simulated function name GetWeaponTech()
{
	local X2WeaponTemplate Template;

	Template = X2WeaponTemplate(GetMyTemplate());
	if (Template != none)
		return Template.WeaponTech;

	return '';
}

simulated function array<string> GetWeaponPanelImages()
{
	local array<X2WeaponUpgradeTemplate> Upgrades;
	local X2WeaponUpgradeTemplate UpgradeTemplate;
	local array<WeaponAttachment> UpgradeAttachments;
	local array<string> Images; 
	local X2WeaponTemplate WeaponTemplate; 
	local int i; 
	local int iUpgrade;
	local delegate<X2TacticalGameRulesetDataStructures.CheckUpgradeStatus> ValidateAttachmentFn;
	local bool bUpgradeImageFound;

	GetMyTemplate();
	if (m_ItemTemplate.IsA('X2WeaponTemplate'))
	{
		WeaponTemplate = X2WeaponTemplate(m_ItemTemplate); 

		if (m_ItemTemplate.strImage != "")
			Images.AddItem(m_ItemTemplate.strImage);
		else if( X2WeaponTemplate(m_ItemTemplate).WeaponPanelImage != "" )
			Images.AddItem(X2WeaponTemplate(m_ItemTemplate).WeaponPanelImage);

		//First check all Upgrade Images to find valid ones
		Upgrades = GetMyWeaponUpgradeTemplates();
		for (iUpgrade = 0; iUpgrade < Upgrades.length; iUpgrade++)
		{
			UpgradeTemplate = Upgrades[iUpgrade];
			for (i = 0; i < UpgradeTemplate.UpgradeAttachments.length; i++)
			{
				ValidateAttachmentFn = UpgradeTemplate.UpgradeAttachments[i].ValidateAttachmentFn;
				if (ValidateAttachmentFn != None && !ValidateAttachmentFn(Upgrades))
				{
					continue;
				}

				if (UpgradeTemplate.UpgradeAttachments[i].ApplyToWeaponTemplate == GetMyTemplateName())
				{
					UpgradeAttachments.AddItem(UpgradeTemplate.UpgradeAttachments[i]);
					if (UpgradeTemplate.UpgradeAttachments[i].AttachIconName != "")
						Images.AddItem(UpgradeTemplate.UpgradeAttachments[i].AttachIconName);
				}
			}
		}

		//Cycle through base images
		for( i = 0; i < WeaponTemplate.DefaultAttachments.length; i++ )
		{
			bUpgradeImageFound = false;
			if (UpgradeAttachments.length > 0)
			{
				// Look for a replacement image among the attached upgrades
				for (iUpgrade = 0; iUpgrade < UpgradeAttachments.length; iUpgrade++)
				{
					if (UpgradeAttachments[iUpgrade].AttachSocket == WeaponTemplate.DefaultAttachments[i].AttachSocket)
					{
						bUpgradeImageFound = true;
						break;
					}
				}
			}
			
			// If an upgrade image has already been added for that slot, don't add it to the stack
			if (!bUpgradeImageFound && WeaponTemplate.DefaultAttachments[i].AttachIconName != "")
				Images.AddItem(WeaponTemplate.DefaultAttachments[i].AttachIconName);			
		}
	}
	else
	{
		if (m_ItemTemplate.strImage != "")
			Images.AddItem(m_ItemTemplate.strImage);
		else if( X2WeaponTemplate(m_ItemTemplate).WeaponPanelImage != "" )
			Images.AddItem(X2WeaponTemplate(m_ItemTemplate).WeaponPanelImage);
	}

	return Images; 
}

//Items can receive nicknames in certain situations.
function String GenerateNickname()
{	
	local int iGenderChoice;
	local int iNumChoices, iChoice;
	local XComGameState_Unit OwnerState;
	local X2SoldierClassTemplate Template;
		
	OwnerState = XComGameState_Unit(`XCOMHISTORY.GetGameStateForObjectID(OwnerStateObject.ObjectID));

	Template = OwnerState.GetSoldierClassTemplate();
	iNumChoices = Template.RandomNickNames.Length;

	//Randomly choose a gender
	iGenderChoice = `SYNC_RAND(2);
	if (iGenderChoice == 0)
	{
		iNumChoices += Template.RandomNickNames_Female.Length;
	}
	else
	{
		iNumChoices += Template.RandomNickNames_Male.Length;
	}
	
	iChoice = `SYNC_RAND(iNumChoices);

	if(iChoice < Template.RandomNickNames.Length)
	{
		return Template.RandomNickNames[iChoice];
	}
	else
	{
		iChoice -= Template.RandomNickNames.Length;
	}

	if (iGenderChoice == 0)
	{
		return Template.RandomNickNames_Female[iChoice];
	}
	else
	{
		return Template.RandomNickNames_Male[iChoice];
	}

	return "";
}

simulated function bool AllowsHeavyWeapon()
{
	local X2ArmorTemplate ArmorTemplate;

	ArmorTemplate = X2ArmorTemplate(GetMyTemplate());
	return ArmorTemplate != None && ArmorTemplate.bHeavyWeapon;
}

simulated function EUISummary_WeaponStats GetWeaponStatsForUI()
{
	local EUISummary_WeaponStats Summary; 
	local int BaseClipSize;
	local WeaponDamageValue DamageValue;
	local array<X2WeaponUpgradeTemplate> WeaponUpgradeTemplates;
	local X2WeaponUpgradeTemplate UpgradeTemplate;

	GetMyTemplate();
	GetBaseWeaponDamageValue(none, DamageValue);

	Summary.Damage		= DamageValue.Damage; 
	Summary.Crit		= GetItemCritChance();
	Summary.Aim			= GetItemAimModifier();
	Summary.ClipSize	= GetClipSize();
	Summary.Range       = GetItemRange(none);

	if (m_ItemTemplate.IsA('X2WeaponTemplate'))
		BaseClipSize = X2WeaponTemplate(m_ItemTemplate).iClipSize;
	Summary.bIsClipSizeModified = BaseClipSize != Summary.ClipSize;

	Summary.SpecialAmmo = GetLoadedAmmoTemplate(none);
	Summary.bIsAmmoModified = Summary.SpecialAmmo != none;

	WeaponUpgradeTemplates = GetMyWeaponUpgradeTemplates();
	foreach WeaponUpgradeTemplates(UpgradeTemplate)
	{
		if (UpgradeTemplate.AddHitChanceModifierFn != none)
			Summary.bIsAimModified = true;

		if (UpgradeTemplate.AddCritChanceModifierFn != none)
			Summary.bIsCritModified = true;

		//  Upgrades cannot modify damage, or range -jbouscher
		Summary.bIsDamageModified = false;
		Summary.bIsRangeModified = false;
	}

	return Summary;
}

simulated function array<EUISummary_WeaponUpgrade> GetWeaponUpgradesForTooltipUI()
{
	local array<X2WeaponUpgradeTemplate> Upgrades; 
	local array<EUISummary_WeaponUpgrade> Entries; 
	local EUISummary_WeaponUpgrade SummaryEntry;
	local int iUpgrade; 

	Upgrades = GetMyWeaponUpgradeTemplates(); 

	for( iUpgrade = 0; iUpgrade < Upgrades.length; iUpgrade++ )
	{
		SummaryEntry.UpgradeTemplate = Upgrades[iUpgrade];

		//TODO: @gameplay: Add label/values for things that this upgrade modifies on the weapon. 
		//foreach modifiedStats(stat) ... 
		//{
			SummaryEntry.Labels.AddItem("STAT"); //TEMP
			SummaryEntry.Values.AddItem("-1");	 //TEMP 
		//}

		Entries.AddItem(SummaryEntry);
	}
	
	return Entries; 
}

simulated function string GetUpgradeEffectForUI(X2WeaponUpgradeTemplate UpgradeTemplate)
{
	local string StatModifiers;
	local EUISummary_WeaponStats UpgradeStats;

	if(UpgradeTemplate.TinySummary != "")
	{
		return UpgradeTemplate.TinySummary;
	}
	else
	{
		UpgradeStats = GetUpgradeModifiersForUI(UpgradeTemplate);

		if(UpgradeStats.bIsDamageModified)
			StatModifiers $= AddStatModifier(StatModifiers != "", class'XLocalizedData'.default.DamageLabel, UpgradeStats.Damage);
		if(UpgradeStats.bIsAimModified)
			StatModifiers $= AddStatModifier(StatModifiers != "", class'XLocalizedData'.default.AimLabel, UpgradeStats.Aim);
		if(UpgradeStats.bIsCritModified)
			StatModifiers $= AddStatModifier(StatModifiers != "", class'XLocalizedData'.default.CritChanceLabel, UpgradeStats.Crit);
		if(UpgradeStats.bIsClipSizeModified)
			StatModifiers $= AddStatModifier(StatModifiers != "", class'XLocalizedData'.default.ClipSizeLabel, UpgradeStats.ClipSize);
		if (UpgradeStats.bIsFreeFirePctModified)
			StatModifiers $= AddStatModifier(StatModifiers != "", class'XLocalizedData'.default.FreeFireLabel, UpgradeStats.FreeFirePct);
		if (UpgradeStats.bIsFreeFirePctModified)
			StatModifiers $= AddStatModifier(StatModifiers != "", class'XLocalizedData'.default.FreeReloadLabel, UpgradeStats.FreeReloads);
		if (UpgradeStats.bIsMissDamageModified)
			StatModifiers $= AddStatModifier(StatModifiers != "", class'XLocalizedData'.default.MissDamageLabel, UpgradeStats.MissDamage);
		if (UpgradeStats.bIsFreeKillPctModified)
			StatModifiers $= AddStatModifier(StatModifiers != "", class'XLocalizedData'.default.FreeKillLabel, UpgradeStats.FreeKillPct);

		return StatModifiers;
	}
}

simulated function string AddStatModifier(bool bAddCommaSeparator, string Label, int Value, optional int ColorState = eUIState_Normal, optional string PostFix, optional bool bSymbolOnRight)
{
	local string Result;
	if(bAddCommaSeparator) Result $= ", ";
	Result $= Label;
	if(bSymbolOnRight)
		Result @= Value $ (Value < 0 ? "-" : "+");
	else
		Result @= (Value < 0 ? "-" : "+") $ Value;
	return class'UIUtilities_Text'.static.GetColoredText(Result $ PostFix, ColorState);
}

simulated function EUISummary_WeaponStats GetUpgradeModifiersForUI(X2WeaponUpgradeTemplate UpgradeTemplate)
{
	local int i, tmp;
	local GameRulesCache_VisibilityInfo VisInfo; // Aim Upgrades require VisibilityInfo since the aim benefit changes based on current cover state of the target
	local EUISummary_WeaponStats UpgradeSummary, TotalUpgradeSummary;
	local array<X2WeaponUpgradeTemplate> WeaponUpgrades;

	WeaponUpgrades = GetMyWeaponUpgradeTemplates();

	if(UpgradeTemplate != none)
		WeaponUpgrades.AddItem(UpgradeTemplate);

	for(i = 0; i < WeaponUpgrades.Length; ++i)
	{
		UpgradeTemplate = WeaponUpgrades[i];

		//TODO: @gameplay: ensure all upgrade stat modifications are accounted for here
		if(UpgradeTemplate == none)
			continue;
		
		if(UpgradeTemplate.AddHitChanceModifierFn != none)
		{
			VisInfo.TargetCover = CT_MAX;
			TotalUpgradeSummary.bIsAimModified = true;
			UpgradeTemplate.AddHitChanceModifierFn(UpgradeTemplate, VisInfo, tmp); // we only want the modifier for max cover
			TotalUpgradeSummary.Aim = tmp;
		}
		if (UpgradeTemplate.AddCritChanceModifierFn != None)
		{
			TotalUpgradeSummary.bIsCritModified = true;
			UpgradeTemplate.AddCritChanceModifierFn(UpgradeTemplate, tmp);
			TotalUpgradeSummary.Crit = tmp;
		}
		if(UpgradeTemplate.AdjustClipSizeFn != none)
		{
			TotalUpgradeSummary.bIsClipSizeModified = true;
			UpgradeTemplate.AdjustClipSizeFn(UpgradeTemplate, self, 0, tmp); // we only want the modifier, so pass 0 for current
			TotalUpgradeSummary.ClipSize = tmp;
		}
		if (UpgradeTemplate.FreeFireChance > 0)
		{
			TotalUpgradeSummary.bIsFreeFirePctModified = true;
			TotalUpgradeSummary.FreeFirePct = UpgradeTemplate.FreeFireChance;
		}
		if (UpgradeTemplate.NumFreeReloads > 0)
		{
			TotalUpgradeSummary.bIsFreeReloadsModified = true;
			TotalUpgradeSummary.FreeReloads = UpgradeTemplate.NumFreeReloads;
		}
		if (UpgradeTemplate.BonusDamage.Damage > 0)
		{
			TotalUpgradeSummary.bIsMissDamageModified = true;
			TotalUpgradeSummary.MissDamage = UpgradeTemplate.BonusDamage.Damage;
		}
		if (UpgradeTemplate.FreeKillChance > 0)
		{
			TotalUpgradeSummary.bIsFreeKillPctModified = true;
			TotalUpgradeSummary.FreeKillPct = UpgradeTemplate.FreeKillChance;
		}
		
		TotalUpgradeSummary.bIsAimModified = TotalUpgradeSummary.bIsAimModified || UpgradeSummary.bIsAimModified;
		TotalUpgradeSummary.bIsCritModified = TotalUpgradeSummary.bIsCritModified || UpgradeSummary.bIsCritModified;
		TotalUpgradeSummary.bIsClipSizeModified = TotalUpgradeSummary.bIsClipSizeModified || UpgradeSummary.bIsClipSizeModified;
		TotalUpgradeSummary.bIsFreeFirePctModified = TotalUpgradeSummary.bIsFreeFirePctModified || UpgradeSummary.bIsFreeFirePctModified;
		TotalUpgradeSummary.bIsFreeReloadsModified = TotalUpgradeSummary.bIsFreeReloadsModified || UpgradeSummary.bIsFreeReloadsModified;
		TotalUpgradeSummary.bIsMissDamageModified = TotalUpgradeSummary.bIsMissDamageModified || UpgradeSummary.bIsMissDamageModified;
		TotalUpgradeSummary.bIsFreeKillPctModified = TotalUpgradeSummary.bIsFreeKillPctModified || UpgradeSummary.bIsFreeKillPctModified;

		TotalUpgradeSummary.Aim += UpgradeSummary.Aim;
		TotalUpgradeSummary.Crit += UpgradeSummary.Crit;
		TotalUpgradeSummary.ClipSize += UpgradeSummary.ClipSize;
		TotalUpgradeSummary.FreeFirePct += UpgradeSummary.FreeFirePct;
		TotalUpgradeSummary.FreeReloads += UpgradeSummary.FreeReloads;
		TotalUpgradeSummary.MissDamage += UpgradeSummary.MissDamage;
		TotalUpgradeSummary.FreeKillPct += UpgradeSummary.FreeKillPct;
	}

	return TotalUpgradeSummary;
}

simulated function array<UISummary_ItemStat> GetUISummary_ItemBasicStats()
{
	local array<UISummary_ItemStat> Result;

	// TODO: @gameplay: Other stat functions and types 
	if (m_ItemTemplate.IsA('X2WeaponTemplate'))
	{
		Result = GetUISummary_WeaponStats();
	}
	else
	{
		Result = GetUISummary_DefaultStats();
	}
	
	return Result;
}

simulated function array<UISummary_ItemStat> GetUISummary_ItemSecondaryStats()
{
	// TODO: @gameplay: Other stat functions and types 
	if (m_ItemTemplate.IsA('X2WeaponTemplate'))
		return GetUISummary_WeaponUpgradeStats();
	else
		return GetUISummary_DefaultStats(); 
}

simulated function array<UISummary_ItemStat> GetUISummary_DefaultStats()
{
	local array<UISummary_ItemStat> Stats; 
	local UISummary_ItemStat		Item; 
	local int Index;
	local X2EquipmentTemplate EquipmentTemplate;
	local delegate<X2StrategyGameRulesetDataStructures.SpecialRequirementsDelegate> ShouldStatDisplayFn;

	EquipmentTemplate = X2EquipmentTemplate(m_ItemTemplate);

	if( EquipmentTemplate != None )
	{
		for( Index = 0; Index < EquipmentTemplate.UIStatMarkups.Length; ++Index )
		{
			ShouldStatDisplayFn = EquipmentTemplate.UIStatMarkups[Index].ShouldStatDisplayFn;
			if (ShouldStatDisplayFn != None && !ShouldStatDisplayFn())
			{
				continue;
			}

			if( EquipmentTemplate.UIStatMarkups[Index].StatModifier != 0 || EquipmentTemplate.UIStatMarkups[Index].bForceShow )
			{
				Item.Label = EquipmentTemplate.UIStatMarkups[Index].StatLabel;
				Item.Value = string(EquipmentTemplate.UIStatMarkups[Index].StatModifier);
				Stats.AddItem(Item);
			}
		}
	}

	return Stats; 
}
simulated function array<UISummary_ItemStat> GetUISummary_WeaponStats(optional X2WeaponUpgradeTemplate PreviewUpgradeStats)
{
	local array<UISummary_ItemStat> Stats; 
	local UISummary_ItemStat		Item; 
	local WeaponDamageValue         DamageValue;
	local EUISummary_WeaponStats    UpgradeStats;
	local X2WeaponTemplate WeaponTemplate;
	local delegate<X2StrategyGameRulesetDataStructures.SpecialRequirementsDelegate> ShouldStatDisplayFn;
	local int Index;

	// Safety check: you need to be a weapon to use this. 
	WeaponTemplate = X2WeaponTemplate(m_ItemTemplate);
	if( WeaponTemplate == none ) 
		return Stats; 

	if(PreviewUpgradeStats != none) 
		UpgradeStats = GetUpgradeModifiersForUI(PreviewUpgradeStats);
	else
		UpgradeStats = GetUpgradeModifiersForUI(X2WeaponUpgradeTemplate(m_ItemTemplate));

	// Damage-----------------------------------------------------------------------
	if (!WeaponTemplate.bHideDamageStat)
	{
		Item.Label = class'XLocalizedData'.default.DamageLabel;
		GetBaseWeaponDamageValue(none, DamageValue);
		if (DamageValue.Damage == 0 && UpgradeStats.bIsDamageModified)
		{
			Item.Value = AddStatModifier(false, "", UpgradeStats.Damage, eUIState_Good);
			Stats.AddItem(Item);
		}
		else if (DamageValue.Damage > 0)
		{
			if (DamageValue.Spread > 0 || DamageValue.PlusOne > 0)
				Item.Value = string(DamageValue.Damage - DamageValue.Spread) $ "-" $ string(DamageValue.Damage + DamageValue.Spread + (DamageValue.PlusOne > 0) ? 1 : 0);
			else
				Item.Value = string(DamageValue.Damage);

			if (UpgradeStats.bIsDamageModified)
				Item.Value $= AddStatModifier(false, "", UpgradeStats.Damage, eUIState_Good);
			Stats.AddItem(Item);
		}
	}
	//TODO: Item.ValueState = bIsDamageModified ? eUIState_Good : eUIState_Normal;
			
	// Clip Size --------------------------------------------------------------------
	if (m_ItemTemplate.ItemCat == 'weapon' && !WeaponTemplate.bHideClipSizeStat)
	{
		Item.Label = class'XLocalizedData'.default.ClipSizeLabel;
		if (PopulateWeaponStat(GetItemClipSize(), UpgradeStats.bIsClipSizeModified, UpgradeStats.ClipSize, Item))
			Stats.AddItem(Item);
	}

	// Crit -------------------------------------------------------------------------
	Item.Label = class'XLocalizedData'.default.CriticalChanceLabel;
	if (PopulateWeaponStat(GetItemCritChance(), UpgradeStats.bIsCritModified, UpgradeStats.Crit, Item, true))
		Stats.AddItem(Item);

	// Ensure that any items which are excluded from stat boosts show values that show up in the Soldier Header
	if (class'UISoldierHeader'.default.EquipmentExcludedFromStatBoosts.Find(m_ItemTemplate.DataName) == INDEX_NONE)
	{
		// Aim -------------------------------------------------------------------------
		Item.Label = class'XLocalizedData'.default.AimLabel;
		if (PopulateWeaponStat(GetItemAimModifier(), UpgradeStats.bIsAimModified, UpgradeStats.Aim, Item, true))
			Stats.AddItem(Item);
	}

	// Free Fire
	Item.Label = class'XLocalizedData'.default.FreeFireLabel;
	if (PopulateWeaponStat(0, UpgradeStats.bIsFreeFirePctModified, UpgradeStats.FreeFirePct, Item, true))
		Stats.AddItem(Item);

	// Free Reloads
	Item.Label = class'XLocalizedData'.default.FreeReloadLabel;
	if (PopulateWeaponStat(0, UpgradeStats.bIsFreeReloadsModified, UpgradeStats.FreeReloads, Item))
		Stats.AddItem(Item);

	// Miss Damage
	Item.Label = class'XLocalizedData'.default.MissDamageLabel;
	if (PopulateWeaponStat(0, UpgradeStats.bIsMissDamageModified, UpgradeStats.MissDamage, Item))
		Stats.AddItem(Item);

	// Free Kill
	Item.Label = class'XLocalizedData'.default.FreeKillLabel;
	if (PopulateWeaponStat(0, UpgradeStats.bIsFreeKillPctModified, UpgradeStats.FreeKillPct, Item, true))
		Stats.AddItem(Item);

	// Add any extra stats and benefits
	for (Index = 0; Index < WeaponTemplate.UIStatMarkups.Length; ++Index)
	{
		ShouldStatDisplayFn = WeaponTemplate.UIStatMarkups[Index].ShouldStatDisplayFn;
		if (ShouldStatDisplayFn != None && !ShouldStatDisplayFn())
		{
			continue;
		}

		if (WeaponTemplate.UIStatMarkups[Index].StatModifier != 0 || WeaponTemplate.UIStatMarkups[Index].bForceShow)
		{
			Item.Label = WeaponTemplate.UIStatMarkups[Index].StatLabel;
			Item.Value = string(WeaponTemplate.UIStatMarkups[Index].StatModifier);
			Stats.AddItem(Item);
		}
	}

	return Stats;
}

simulated function bool PopulateWeaponStat(int Value, bool bIsStatModified, int UpgradeValue, out UISummary_ItemStat Item, optional bool bIsPercent)
{
	if (Value > 0)
	{
		if (bIsStatModified)
		{
			Item.Value = AddStatModifier(false, "", UpgradeValue, eUIState_Good, "", true);
			Item.Value $= string(Value) $ (bIsPercent ? "%" : "");
		}
		else
		{
			Item.Value = string(Value) $ (bIsPercent ? "%" : "");
		}
		return true;
	}
	else if (bIsStatModified)
	{
		Item.Value = AddStatModifier(false, "", UpgradeValue, eUIState_Good, (bIsPercent ? "%" : ""), false);
		return true;
	}

	return false;
}

simulated function array<UISummary_ItemStat> GetUISummary_AmmoStats()
{
	local X2ItemTemplate			SpecialAmmo;
	local array<UISummary_ItemStat> Stats;
	local UISummary_ItemStat		Item;

	// Safety check: you need to be a weapon to use this. 
	if( !m_ItemTemplate.IsA('X2WeaponTemplate') ) return Stats;

	// Ammo Type ----------------------------------------------------------------
	SpecialAmmo = GetLoadedAmmoTemplate(none);

	if( SpecialAmmo != none )
	{
		Item.Label = class'XLocalizedData'.default.AmmoTypeHeader;
		Item.LabelStyle = eUITextStyle_Tooltip_H1;
		Stats.AddItem(Item);

		Item.Label = SpecialAmmo.GetItemFriendlyName();
		Item.LabelStyle = eUITextStyle_Tooltip_H2; 

		Item.Value = SpecialAmmo.GetItemBriefSummary();
		Item.ValueStyle = eUITextStyle_Tooltip_Body; 

		Stats.AddItem(Item);
	}
	// -------------------------------------------------------------------------

	return Stats;
}

simulated function array<UISummary_ItemStat> GetUISummary_WeaponUpgradeStats()
{
	local array<X2WeaponUpgradeTemplate> Upgrades; 
	local X2WeaponUpgradeTemplate UpgradeTemplate; 
	local array<UISummary_ItemStat> Stats; 
	local UISummary_ItemStat Item;
	local int iUpgrade; 

	Upgrades = GetMyWeaponUpgradeTemplates(); 

	if( Upgrades.length > 0 )
	{
		Item.Label = class'XLocalizedData'.default.UpgradesHeader; 
		Item.LabelStyle = eUITextStyle_Tooltip_H1; 
		Stats.AddItem(Item); 
	}

	for( iUpgrade = 0; iUpgrade < Upgrades.length; iUpgrade++ )
	{
		UpgradeTemplate = Upgrades[iUpgrade];
		
		Item.Label = UpgradeTemplate.GetItemFriendlyName();
		Item.LabelStyle = eUITextStyle_Tooltip_H2; 

		Item.Value = GetUpgradeEffectForUI(UpgradeTemplate);
		Item.ValueStyle = eUITextStyle_Tooltip_Body; 

		Stats.AddItem(Item);
	}

	return Stats; 
}

simulated function array<UISummary_TacaticalText> GetUISummary_TacticalTextAbilities()
{
	local bool bIsIn3D;
	local X2EquipmentTemplate       EquipmentTemplate; 
	local X2AbilityTemplateManager  AbilityTemplateManager;
	local X2AbilityTemplate         AbilityTemplate; 
	local name                      AbilityName;
	local UISummary_Ability        UISummaryAbility; 
	local UISummary_TacaticalText  Data; 
	local array<UISummary_TacaticalText> Items; 

	EquipmentTemplate = X2EquipmentTemplate(m_ItemTemplate);
	if( EquipmentTemplate == none ) return Items;  //Empty.

	bIsIn3D = `SCREENSTACK.GetCurrentScreen().bIsIn3D;

	AbilityTemplateManager = class'X2AbilityTemplateManager'.static.GetAbilityTemplateManager();
	foreach EquipmentTemplate.Abilities(AbilityName)
	{
		AbilityTemplate = AbilityTemplateManager.FindAbilityTemplate(AbilityName);
		if( AbilityTemplate != none  && AbilityTemplate.bDisplayInUITacticalText )
		{
			UISummaryAbility = AbilityTemplate.GetUISummary_Ability();
			Data.Name = class'UIUtilities_Text'.static.AddFontInfo(UISummaryAbility.Name, bIsIn3D, true, true);
			Data.Description = class'UIUtilities_Text'.static.AddFontInfo(UISummaryAbility.Description, bIsIn3D, false);
			Data.Icon = UISummaryAbility.Icon;
			Items.AddItem(Data);
		}
	}

	return Items; 
}

simulated function array<UISummary_TacaticalText> GetUISummary_TacticalTextUpgrades()
{
	local array<X2WeaponUpgradeTemplate> Upgrades; 
	local X2WeaponUpgradeTemplate UpgradeTemplate; 
	local UISummary_TacaticalText Data; 
	local array<UISummary_TacaticalText> Items;
	local int iUpgrade; 
	local array<string> UpgradeIcons; 

	Upgrades = GetMyWeaponUpgradeTemplates(); 

	for( iUpgrade = 0; iUpgrade < Upgrades.length; iUpgrade++ )
	{
		UpgradeTemplate = Upgrades[iUpgrade];
		
		Data.Name = UpgradeTemplate.GetItemFriendlyName(); 
		Data.Description = UpgradeTemplate.GetItemBriefSummary(); 

		UpgradeIcons = UpgradeTemplate.GetAttachmentInventoryCategoryImages(self);
		if( UpgradeIcons.length > 0 )
			Data.Icon = UpgradeIcons[0];
		else
			Data.Icon = "";

		Items.AddItem(Data);
	}

	return Items; 
}


simulated function array<UISummary_TacaticalText> GetUISummary_TacticalText()
{
	local bool bIsIn3D;
	local int FontSize;
	local string TacticalText;
	local EUIState ColorState;
	local UISummary_TacaticalText Data; 
	local array<UISummary_TacaticalText> Items;

	ColorState = eUIState_Normal;
	bIsIn3D = `SCREENSTACK.GetCurrentScreen().bIsIn3D;
	FontSize = bIsIn3D ? class'UIUtilities_Text'.const.BODY_FONT_SIZE_3D : class'UIUtilities_Text'.const.BODY_FONT_SIZE_2D;

	if(GetMyTemplate() != none)
		TacticalText = GetMyTemplate().GetItemTacticalText();

	if( TacticalText == "" )
	{
		ColorState = eUIState_Bad;
		TacticalText = "DEBUG: @Design: Missing TacticalText in '" $ GetMyTemplateName() $ "' template."; 
	}

	Data.Description = class'UIUtilities_Text'.static.GetColoredText(TacticalText, ColorState, FontSize);
	Items.AddItem(Data);

	return Items; 
}

//---------------------------------------------------------------------------------------
function OnItemBuilt(XComGameState NewGameState)
{
	if (GetMyTemplate().OnBuiltFn != none)
	{
		GetMyTemplate().OnBuiltFn(NewGameState, self);
	}
}

//---------------------------------------------------------------------------------------
function bool IsNeededForGoldenPath()
{
	local XComGameStateHistory History;
	local XComGameState_HeadquartersXCom XComHQ;
	local XComGameState_Objective ObjectiveState;
	local XComGameState_Tech TechState;
	local X2ObjectiveTemplate ObjTemplate;
	local name TechName;

	History = `XCOMHISTORY;
	XComHQ = XComGameState_HeadquartersXCom(History.GetSingleGameStateObjectForClass(class'XComGameState_HeadquartersXCom'));

	foreach History.IterateByClassType(class'XComGameState_Objective', ObjectiveState)
	{
		if(ObjectiveState.ObjState < eObjectiveState_InProgress)
		{
			ObjTemplate = ObjectiveState.GetMyTemplate();
			if (ObjTemplate != none)
			{
				if (ObjTemplate.AssignmentRequirements.RequiredItems.Find(GetMyTemplateName()) != INDEX_NONE)
				{
					return true;
				}

				foreach ObjTemplate.AssignmentRequirements.RequiredTechs(TechName)
				{
					foreach History.IterateByClassType(class'XComGameState_Tech', TechState)
					{
						if (!XComHQ.TechIsResearched(TechState.GetReference()) && TechState.GetMyTemplateName() == TechName &&
							TechState.GetMyTemplate().Requirements.RequiredItems.Find(GetMyTemplateName()) != INDEX_NONE)
						{
							return true;
						}
					}
				}
			}
		}

		if(ObjectiveState.ObjState < eObjectiveState_Completed)
		{
			ObjTemplate = ObjectiveState.GetMyTemplate();
			if (ObjTemplate != none)
			{
				if (ObjTemplate.CompletionRequirements.RequiredItems.Find(GetMyTemplateName()) != INDEX_NONE)
				{
					return true;
				}

				foreach ObjTemplate.CompletionRequirements.RequiredTechs(TechName)
				{
					foreach History.IterateByClassType(class'XComGameState_Tech', TechState)
					{
						if (!XComHQ.TechIsResearched(TechState.GetReference()) && TechState.GetMyTemplateName() == TechName &&
							TechState.GetMyTemplate().Requirements.RequiredItems.Find(GetMyTemplateName()) != INDEX_NONE)
						{
							return true;
						}
					}
				}
			}
		}
	}


	return false;
}

function bool ShouldDisplayWeaponAndAmmo()
{
	local X2WeaponTemplate WeaponTemplate;
	local bool bDisplayWeaponAndAmmo;

	bDisplayWeaponAndAmmo = false;
	WeaponTemplate = X2WeaponTemplate(m_ItemTemplate);

	if( WeaponTemplate != None )
	{
		bDisplayWeaponAndAmmo = WeaponTemplate.bDisplayWeaponAndAmmo;
	}

	return bDisplayWeaponAndAmmo;
}

DefaultProperties
{
	Quantity=1
}
