/**
 * Copyright 1998-2013 Epic Games, Inc. All Rights Reserved.
 */

/**
 * This class is responsible for mapping properties in an OnlineGameSearch
 * object to something that the UI system can consume. It exposes two things
 * DesiredSettings and SearchResults. DesiredSettings is just publishes the
 * properties/string settings of an online game settings and SearchResults is
 * the set of games found by the search.
 *
 * NOTE: Each game needs to derive at least one class from this one in
 * order to expose the game's specific search class(es)
 */
class UIDataStore_OnlineGameSearch extends UIDataStore_Remote
	native(inherit)
	abstract
	dependson(OnlineGameSearch)
	transient;

/** Cached FName for faster compares */
var const name SearchResultsName;

/** Cached online subsystem pointer */
var OnlineSubsystem OnlineSub;

/** Cached game interface pointer */
var OnlineGameInterface GameInterface;

/** Holds the items needed for keeping a list of game searches around */
struct native GameSearchCfg
{
	/** The OnlineGameSeach derived class to load and populate the UI with */
	var class<OnlineGameSearch> GameSearchClass;
	/** The OnlineGameSettings derived class to use as the default data */
	var class<OnlineGameSettings> DefaultGameSettingsClass;
	/**
	 * The data provider to use for each search result that is returned. Useful when
	 * a game wishes to create "meta" properties from search results.
	 */
	var class<UIDataProvider_Settings> SearchResultsProviderClass;
	/** Publishes the desired settings from the game search object */
	var UIDataProvider_Settings DesiredSettingsProvider;
	/** OnlineGameSearch object that will be exposed to the UI */
	var OnlineGameSearch Search;
	/** For finding via name */
	var name SearchName;
};

/** The set of game searches and results */
var const array<GameSearchCfg> GameSearchCfgList;

/** The index into the set of providers/searches for the query that the user most recently requested */
var int SelectedIndex;

/** the index into the set of providers/searches for the query that is currently active */
var	int	ActiveSearchIndex;

cpptext
{
protected:
// UIDataStore interface

	/**
	 * Loads and creates an instance of the registered provider objects for each
	 * registered OnlineGameSettings class
	 */
	virtual void InitializeDataStore(void);
}

/**
 * Registers the delegate with the online subsystem
 */
event Init()
{
	// Figure out if we have an online subsystem registered
	OnlineSub = class'GameEngine'.static.GetOnlineSubsystem();
	if (OnlineSub != None)
	{
		// Grab the game interface to verify the subsystem supports it
		GameInterface = OnlineSub.GameInterface;

//@HSL_BEGIN - BWJ - 4-12-16 - Playfab support
		//@SABER_EGS IsEosBuild() case added
		if( (class'WorldInfo'.static.IsConsoleBuild() || class'WorldInfo'.static.IsEosBuild()) && !class'WorldInfo'.static.IsE3Build()  )
		{
			// ToDo delete
			`Log("AddFindOnlineGamesCompleteDelegate(OnSearchComplete)");
			class'GameEngine'.static.GetPlayfabInterface().AddFindOnlineGamesCompleteDelegate(OnSearchComplete);
		}
		else if (GameInterface != None)
		{				// Set the function to call when the search is done
			GameInterface.AddFindOnlineGamesCompleteDelegate(OnSearchComplete);
		}
//@HSL_END
	}
}

/**
 * Attempts to free the results from the last search that was submitted.
 */
function bool InvalidateCurrentSearchResults()
{
	local OnlineGameSearch ActiveSearch;
	local bool bResult;

	ActiveSearch = GetActiveGameSearch();
	if ( ActiveSearch != None )
	{
		// Free any previous results and tell the list to refresh
		if ( GameInterface.FreeSearchResults(ActiveSearch) )
		{
			// notify subscribers that the value has been invalidated.
			RefreshSubscribers(SearchResultsName, true, GameSearchCfgList[SelectedIndex].DesiredSettingsProvider);
			bResult = true;
		}
	}
	return bResult;
}

/**
 * Called to kick off an online game search and set up all of the delegates needed
 *
 * @param ControllerIndex the ControllerId for the player to perform the search for
 * @param bInvalidateExistingSearchResults	specify FALSE to keep previous searches (i.e. for other gametypes) in memory; default
 *											behavior is to clear all search results when switching to a different item in the game search list
 *
 * @return TRUE if the search call works, FALSE otherwise
 */
event bool SubmitGameSearch(byte ControllerIndex, optional bool bInvalidateExistingSearchResults=true)
{
	if (OnlineSub != None)
	{
		if (GameInterface != None)
		{
			if ( bInvalidateExistingSearchResults || ActiveSearchIndex == SelectedIndex )
			{
				InvalidateCurrentSearchResults();
			}

			// Do not change the value of ActiveSearchIndex as long as we have a search in progress.
			if ( ActiveSearchIndex == INDEX_NONE || !GameSearchCfgList[ActiveSearchIndex].Search.bIsSearchInProgress )
			{
				ActiveSearchIndex = SelectedIndex;
			}

			if ( OverrideQuerySubmission(ControllerIndex, GameSearchCfgList[ActiveSearchIndex].Search) )
			{
				return true;
			}

			// invalidate the results for this search
			InvalidateCurrentSearchResults();

//@HSL_BEGIN - BWJ - 4-12-16 - Playfab support
			if( class'WorldInfo'.static.IsConsoleBuild() && !class'WorldInfo'.static.IsE3Build() )
			{
				// ToDo delete
				`Log("GetPlayfabInterface().FindOnlineGames( GameSearchCfgList[ActiveSearchIndex].Search )");
				return class'GameEngine'.static.GetPlayfabInterface().FindOnlineGames( GameSearchCfgList[ActiveSearchIndex].Search );
			}
			else
			{
				return GameInterface.FindOnlineGames(ControllerIndex,GameSearchCfgList[ActiveSearchIndex].Search);
			}
//@HSL_END
		}
		else
		{
			`warn("OnlineSubsystem does not support the game interface. Can't search for games");
		}
	}
	else
	{
		`warn("No OnlineSubsystem present. Can't search for games");
	}
	return false;
}

/**
 * Worker for SubmitGameSeach; allows child classes to perform additional work before the query is submitted.
 *
 * @param	ControllerId	the index of the controller for the player to perform the search for.
 * @param	Search			the search object that will be used to generate the query.
 *
 * @return	TRUE to prevent SubmitGameSeach from submitting the search (such as when you do this step yourself).
 */
protected function bool OverrideQuerySubmission( byte ControllerId, OnlineGameSearch Search )
{
	return false;
}

/**
 * Called by the online subsystem when the game search has completed
 *
 * @param bWasSuccessful true if the async action completed without error, false if there was an error
 */
function OnSearchComplete(bool bWasSuccessful)
{
	if (bWasSuccessful == true)
	{
		// notify any subscribers that we have new data
		RefreshSubscribers(SearchResultsName, false, GameSearchCfgList[ActiveSearchIndex].DesiredSettingsProvider);

		// now we leave ActiveSearchIndex at its current value so that we know which search we performed last - this way
		// when SetCurrentByName, SubmitGameSearch, etc. is called with bInvalidateSearchResults=TRUE, we know which one
		// to invalidate.
	}
	else
	{
		`Log("Failed to search for online games");
	}
}

/**
 * Returns the search result for the list index specified
 *
 * @param ListIndex the index to find the result for
 *
 * @return the search results (empty if out of bounds)
 */
event bool GetSearchResultFromIndex(int ListIndex,out OnlineGameSearchResult Result)
{
	if (ListIndex >= 0 && ListIndex < GameSearchCfgList[SelectedIndex].Search.Results.Length)
	{
		Result = GameSearchCfgList[SelectedIndex].Search.Results[ListIndex];
		return true;
	}
	return false;
}

/**
 * Displays the gamercard for the specified host
 *
 * @param ControllerIndex	the ControllerId for the player displaying the gamercard
 * @param ListIndex			the item in the list to display the gamercard for
 */
event bool ShowHostGamercard(byte ControllerIndex,int ListIndex)
{
	local OnlinePlayerInterfaceEx PlayerExt;
	local OnlineGameSettings Game;

	// Validate the specified index is within the search results
	if (ListIndex >= 0 && ListIndex < GameSearchCfgList[SelectedIndex].Search.Results.Length)
	{
		if (OnlineSub != None)
		{
			PlayerExt = OnlineSub.PlayerInterfaceEx;
			if (PlayerExt != None)
			{
				Game = GameSearchCfgList[SelectedIndex].Search.Results[ListIndex].GameSettings;
				return PlayerExt.ShowGamerCardUI(ControllerIndex,Game.OwningPlayerId);
			}
			else
			{
				`warn("OnlineSubsystem does not support the extended player interface. Can't show gamercard");
			}
		}
		else
		{
			`warn("No OnlineSubsystem present. Can't show gamercard");
		}
	}
	else
	{
		`warn("Invalid index ("$ListIndex$") specified for online game to show the gamercard of");
	}
}

/** Returns the game search object that is currently selected */
event OnlineGameSearch GetCurrentGameSearch()
{
	if ( SelectedIndex >= 0 && SelectedIndex < GameSearchCfgList.Length )
	{
		return GameSearchCfgList[SelectedIndex].Search;
	}

	return None;
}

/** returns the game search object that last submitted a server query */
event OnlineGameSearch GetActiveGameSearch()
{
	if ( ActiveSearchIndex >= 0 && ActiveSearchIndex < GameSearchCfgList.Length )
	{
		return GameSearchCfgList[ActiveSearchIndex].Search;
	}

	return None;
}

/**
 * Find the index of the search configuration element which has the specified tag.
 *
 * @param	SearchTag	the name of the search configuration to find
 *
 * @return	the index of the search configuration with a tag matching the input value or INDEX_NONE if none were found.
 */
function int FindSearchConfigurationIndex( name SearchTag )
{
	local int Index;

	for (Index = 0; Index < GameSearchCfgList.Length; Index++)
	{
		if (GameSearchCfgList[Index].SearchName == SearchTag)
		{
			return Index;
		}
	}

	return INDEX_NONE;
}


/**
 * Sets the index into the list of game search to use
 *
 * @param NewIndex the new index to use
 * @param bInvalidateExistingSearchResults	specify FALSE to keep previous searches (i.e. for other gametypes) in memory; default
 *											behavior is to clear all search results when switching to a different item in the game search list
 */
event SetCurrentByIndex(int NewIndex, optional bool bInvalidateExistingSearchResults=true)
{
	// Range check to prevent accessed nones
	if (NewIndex >= 0 && NewIndex < GameSearchCfgList.Length)
	{
		SelectedIndex = NewIndex;

		if ( !bInvalidateExistingSearchResults
		|| !InvalidateCurrentSearchResults() )
		{
			RefreshSubscribers(SearchResultsName, true, GameSearchCfgList[SelectedIndex].DesiredSettingsProvider);
		}
	}
	else
	{
		`Log("Invalid index ("$NewIndex$") specified to SetCurrentByIndex() on "$Self);
	}
}

/**
 * Sets the index into the list of game settings to use
 *
 * @param SearchName the name of the search to find
 * @param bInvalidateExistingSearchResults	specify FALSE to keep previous searches (i.e. for other gametypes) in memory; default
 *											behavior is to clear all search results when switching to a different item in the game search list
 */
event SetCurrentByName(name SearchName, optional bool bInvalidateExistingSearchResults=true)
{
	local int Index;

	Index = FindSearchConfigurationIndex(SearchName);
	if ( Index != INDEX_NONE )
	{
		// If this is the one we want, set it and refresh
		SelectedIndex = Index;

		if ( !bInvalidateExistingSearchResults
		|| !InvalidateCurrentSearchResults() )
		{
			RefreshSubscribers(SearchResultsName, true, GameSearchCfgList[SelectedIndex].DesiredSettingsProvider);
		}
	}
	else
	{
		`Log("Invalid name ("$SearchName$") specified to SetCurrentByName() on "$Self);
	}
}

/**
 * Moves to the next item in the list
 *
 * @param bInvalidateExistingSearchResults	specify FALSE to keep previous searches (i.e. for other gametypes) in memory; default
 *											behavior is to clear all search results when switching to a different item in the game search list
 */
event MoveToNext(optional bool bInvalidateExistingSearchResults=true)
{
	SelectedIndex = Min(SelectedIndex + 1,GameSearchCfgList.Length - 1);

	if ( !bInvalidateExistingSearchResults
	|| !InvalidateCurrentSearchResults() )
	{
		RefreshSubscribers(SearchResultsName, true, GameSearchCfgList[SelectedIndex].DesiredSettingsProvider);
	}
}

/**
 * Moves to the previous item in the list
 *
 * @param bInvalidateExistingSearchResults	specify FALSE to keep previous searches (i.e. for other gametypes) in memory; default
 *											behavior is to clear all search results when switching to a different item in the game search list
 */
event MoveToPrevious(optional bool bInvalidateExistingSearchResults=true)
{
	SelectedIndex = Max(SelectedIndex - 1,0);

	if ( !bInvalidateExistingSearchResults
	|| !InvalidateCurrentSearchResults() )
	{
		RefreshSubscribers(SearchResultsName, true, GameSearchCfgList[SelectedIndex].DesiredSettingsProvider);
	}
}

/**
 * Attempts to clear the server query results for all gametypes
 */
function ClearAllSearchResults()
{
	local int OriginalActiveIndex, GameTypeIndex;

	OriginalActiveIndex = ActiveSearchIndex;
	if ( GameInterface != None )
	{
		for ( GameTypeIndex = 0; GameTypeIndex < GameSearchCfgList.Length; GameTypeIndex++ )
		{
			ActiveSearchIndex = GameTypeIndex;
			if (!GameInterface.FreeSearchResults(GameSearchCfgList[GameTypeIndex].Search) )
			{
				`warn(Name $ ".ClearAllSearchResults: Failed to free search results for" @ GameSearchCfgList[GameTypeIndex].SearchName @ "(" $ GameTypeIndex $ ") - search is still in progress");
			}
		}
	}

	ActiveSearchIndex = OriginalActiveIndex;
}

defaultproperties
{
	// Change this value in the derived class
	Tag=OnlineGameSearch
	SearchResultsName=SearchResults

	ActiveSearchIndex=INDEX_NONE
}
