//---------------------------------------------------------------------------------------
//  FILE:    X2GameRulesetVisibilityManager.uc
//  AUTHOR:  Ryan McFall  --  11/25/2013
//  PURPOSE: Manages visibility state for the X-Com 2 rules engine
//           
//---------------------------------------------------------------------------------------
//  Copyright (c) 2016 Firaxis Games, Inc. All rights reserved.
//---------------------------------------------------------------------------------------
class X2GameRulesetVisibilityManager extends Actor
	dependson(XComGameState, XComGameState_BaseObject, X2GameRulesetVisibilityDataStructures)
	native(Core);

// Represents visibility state for each history frame in which visibility could potentially have changed (so not every history frame has a Visibility frame)
// use GetVisibilityInfoFrame in script, and GetInfoFrameInternal for native code to do the conversion from history frame index into the corresponding 
// visibility frame info. When loading from a save, VisibilityFrames does not contain frames prior to the loaded state.
var private array<VisibilityInfoFrame> VisibilityFrames;
var private bool bProcessingInterrupts;

//Special cached variable so that when we evaluate X2Condition_Visibility inside the visibility mgr's native code we do not redundantly look up the vis info.
var GameRulesCache_VisibilityInfo X2ConditionCachedVisInfo; 

//This observer object is responsible for responding to new visibility info frames and keeping the model visibility state up to date
var X2VisibilityObserver ActorVisibilityMgr;

cpptext
{	
	bool BuildVisibilityInfoFromGameState(class UXComGameState& NewGameState, const FVisibilityInfoFrame* PreviousVisibilityInfo, FVisibilityInfoFrame& OutNewVisibilityInfo, UBOOL bFullUpdate);
	const FVisibilityInfoFrame* GetInfoFrameInternal(INT HistoryIndex = -1) const;
};

/// <summary>
/// Registers the visibility manager for new game state events
/// </summary>
function RegisterForNewGameStateEvent()
{
	local XComGameStateHistory History;

	History = `XCOMHISTORY;
	History.RegisterOnNewGameStateDelegate(OnNewGameState);
	History.RegisterOnObliteratedGameStateDelegate(OnObliterateGameState);

	ActorVisibilityMgr = new class'X2VisibilityObserver';
	`XCOMVISUALIZATIONMGR.RegisterObserver(ActorVisibilityMgr);
}

function UnRegisterForNewGameStateEvent()
{
	local XComGameStateHistory History;

	History = `XCOMHISTORY;
	History.UnRegisterOnNewGameStateDelegate(OnNewGameState);
	History.UnRegisterOnObliteratedGameStateDelegate(OnObliterateGameState);

	`XCOMVISUALIZATIONMGR.RemoveObserver(ActorVisibilityMgr);
}


/// <summary>
/// This method is registered with the history and is called whenever a new game state is added to the history. It is responsible for created new 
/// VisibilityFrame elements that reflect the visibility situation and deltas generated by the new game state
/// </summary>
native function OnNewGameState(XComGameState NewGameState);

/// <summary>
/// This method is registered with the history and is called whenever a game state is obliterated from the history. It is responsible for removing
/// the visibility frame associated with the obliterated game state
/// </summary>
native function OnObliterateGameState(XComGameState ObliteratedGameState);

/// <summary>
/// Sets bProcessingInterrupts to TRUE, which informs the visibility mgr that incoming states from OnNewGameState are from interrupt states
/// </summary>
function BeginInterruptProcessing()
{
	bProcessingInterrupts = true;
}

/// <summary>
/// Sets bProcessingInterrupts to FALSE, which informs the visibility mgr that incoming states from OnNewGameState are not from interrupt states
/// </summary>
function EndBeginInterruptProcessing()
{
	bProcessingInterrupts = false;
}

/// <summary>
/// This method instructs the visibility manager that visibility processing is starting. It creates the first element in VisibilityFrames and 
/// force updates all state object visualizers
/// </summary>
native function InitialUpdate();

/// <summary>
/// Returns a copy of the visibility info frame for the specified history index
/// </summary>
native function GetVisibilityInfoFrame(out VisibilityInfoFrame VisibilityInfo, optional int HistoryIndex = -1);

/// <summary>
/// GetAllVisibleToSource fills the out parameter VisibleToSource with a list of state objects that SourceStateObjectID can see as of the game state index specified by GameStateIndex.
/// If GameStateIndex is -1, the latest history frame is used. VisibleToSource is not cleared, and verifies that added items are unique, so this method can be used multiple times
/// with the same VisibleToSource array to accumulate visible objects for multiple sources.
/// </summary>
/// <param name="SourceStateObjectID">The viewer</param>
/// <param name="VisibleToSource">Out param filled out with a list of state objects that Source can see</param>
/// <param name="TypeOfStateObject">Allows the caller to filter the results by the type of game state object that is visible</param>
/// <param name="StartAtHistoryIndex">Allows a specific state frame's visibility state to be requested / queried</param>
/// <param name="RequiredConditions">An optional set of conditions that the source / target must meet to be added to the VisibleToSource list</param>
native function GetAllVisibleToSource(int SourceStateObjectID, 
									  out array<StateObjectReference> VisibleToSource, 
									  optional class TypeOfStateObject = class'XComGameState_BaseObject', 
									  optional int StartAtHistoryIndex = -1,
									  optional array<X2Condition> RequiredConditions);

/// <summary>
/// GetNumVisibleToSource returns the number of Units visible to SourceStateObjectID
/// </summary>
native function int GetNumVisibleToSource(int SourceStateObjectID, 
										  optional int StartAtHistoryIndex = -1,
										  optional array<X2Condition> RequiredConditions);

/// <summary>
/// GetAllViewersOfTarget fills the out parameter VisibleToSource with a list of state objects that can see TargetStateObjectID as of the game state index specified by GameStateIndex.
/// If GameStateIndex is -1, the latest history frame is used. ViewersOfTarget is not cleared, and verifies that added items are unique, so this method can be used multiple times
/// with the same ViewersOfTarget array to accumulate viewer objects for multiple targets.
/// </summary>
/// <param name="TargetStateObjectID">The target</param>
/// <param name="ViewersOfTarget">Out param filled out with a list of state objects that can see TargetStateObjectID</param>
/// <param name="TypeOfStateObject">Allows the caller to filter the results by the type of game state object that are viewing</param>
/// <param name="StartAtHistoryIndex">Allows a specific state frame's visibility state to be requested / queried</param>
/// <param name="RequiredConditions">An optional set of conditions that the viewer must meet to be added to the ViewersOfTarget list</param>
native function GetAllViewersOfTarget(int TargetStateObjectID, 
									  out array<StateObjectReference> ViewersOfTarget, 
									  optional class TypeOfStateObject = class'XComGameState_BaseObject', 
									  optional int StartAtHistoryIndex = -1,
									  optional array<X2Condition> RequiredConditions);

/// <summary>
/// GetNumViewersOfTarget returns the number of Units that can see SourceStateObjectID
/// </summary>
native function int GetNumViewersOfTarget(int SourceStateObjectID, 
										  optional int StartAtHistoryIndex = -1,
										  optional array<X2Condition> RequiredConditions);

/// <summary>
/// GetVisibilityInfo fills OutVisInfo with the cached visibility info for the specified source / target relationship. The method returns TRUE if a relationship
/// exists, FALSE if not.
/// </summary>
native function bool GetVisibilityInfo(int SourceStateObjectID, int TargetStateObjectID, out GameRulesCache_VisibilityInfo OutVisInfo, optional int HistoryIndex = -1);

/// <summary>
/// This method takes a HistoryIndex as a parameter and fills out an out param list of state objects that changed visibility state during that history frame.
/// </summary>
native function GetVisibilityStatusChangedObjects(int HistoryIndex, out array<int> ChangedObjectIDs);

/// <summary>
/// GetAllViewersOfLocation returns state object references to all viewer state objects that can see a given location
/// </summary>
/// <param name="TestLocation">A location from which to test visibility</param>
/// <param name="ViewerInfos">Out param filled out with a list of viewer infos giving information on which viewers can see </param>
/// <param name="TypeOfStateObject">Allows the caller to filter the results by the type of game state object that are viewing</param>
/// <param name="StartAtHistoryIndex">Allows a specific state frame's visibility state to be requested / queried</param>
/// <param name="RequiredConditions">An optional set of conditions that the viewer must meet to be added to the ViewerInfos list</param>
native function GetAllViewersOfLocation(const out TTile TestLocation, 
										out array<GameRulesCache_VisibilityInfo> ViewerInfos, 
										optional class TypeOfStateObject = class'XComGameState_BaseObject', 
										optional int StartAtHistoryIndex = -1,
										optional array<X2Condition> RequiredConditions);

/// <summary>
/// Returns information about the optimal configuration for Source to see Target
/// </summary>
native function bool GetDirectionInfoForTarget(XComGameState_Unit Source, 
											   XComGameState_Unit Target, 
											   out int Direction, 
											   out UnitPeekSide PeekSide, 
											   out int bCanSeeFromDefault, 
											   out int bRequiresLean,
											   optional bool bCanSeeConcealed=false,
											   optional int HistoryIndex = -1);