/*
* Neural Tanks is a game created by Eddie O'Hagan that leverages Neural Networking
* to use for AI. This was created in Unreal Engine 4 which is developed by Epic Games.
* (Copyright Epic Games, Inc. All Rights Reserved.)
*
* Smart Tanks was originally developed by Mat Buckland in 2002 as an example
* on how Neural Networking can be used to train a set of minesweepers
* to collect mines. I (Eddie O'Hagan) updated this project in 2024 to use more
* Object Oriented Programming and modern practices. To see the original tutorial;
* visit <see href="http://www.ai-junkie.com/ann/evolved/nnt1.html">Neural Networking Tutorial</see>
* and <see href="http://www.ai-junkie.com/ga/intro/gat1.html">Genetic Algorithm Tutorial</see>
*/
#pragma once
#include "SavingAndLoading/PlayerProgressionSaveGame.h"
#include "SavingAndLoading/SaveFileMetaData.h"
#include "SavingAndLoading/OptionsData.h"
#include "NeuralTanksGameInstance.generated.h"

class UNeuralTanksCheatManager;

/// <summary>
/// The Neural Tanks Game Instance is a game instance that exists during the 
/// entire run of the game. In other words, this class can manage things such
/// as loading screens for transitions between levels, and saving/loading data.
/// </summary>
UCLASS()
class UNeuralTanksGameInstance : public UGameInstance
{
	GENERATED_BODY()

protected:
	/// <summary>
	/// The unique identifier for the specific hardware input device. 
	/// You can use this to distinguish between different controllers 
	/// connected to the same machine (usually 0).
	/// </summary>
	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "General")
	int myUserIndex;
	
	/// <summary>
	/// The index of the current save game in the save game collection.
	/// This is used for generating a unique save file name.
	/// </summary>
	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Internal")
	int mySaveSlotIndex;

	/// <summary>
	/// The name to use for the save file that contains the meta data.
	/// </summary>
	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Saving and Loading")
	FString mySaveFileNameForMetaData;

	/// <summary>
	/// The name to use for the save file that contains the options data.
	/// </summary>
	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Saving and Loading")
	FString mySaveFileNameForOptionsData;

	/// <summary>
	/// A location the player can't see or access where we can spawn things.
	/// For example, Equipment has its own blueprint implemented function for applying
	/// its unique stats. In order for this function to be called, the blueprint actor
	/// must be spawned in the world. But The player is not actually using the spawned actor.
	/// </summary>
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "General")
	FTransform myOffscreenSpawnLocation;

	/// <summary>
	/// The save file containing the meta data such as the total number of save files.
	/// </summary>
	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Saving and Loading")
	USaveFileMetaData* mySaveFileMetaData;

	/// <summary>
	/// The options save file containing the options data such as audio and video settings.
	/// </summary>
	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Saving and Loading")
	UOptionsData* myOptionsData;

	/// <summary>
	/// The save file containing the player data such as money, levels completed, etc.
	/// </summary>
	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Saving and Loading")
	UPlayerProgressionSaveGame* myPlayerProgressionSaveGame;

	/// <summary>
	/// Used to keep track of what cheats the player has enabled/disabled.
	/// </summary>
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Internal")
	UNeuralTanksCheatManager* myCheatManager;

	/// <summary>
	/// A collection of tips to display to the player during the loading screen.
	/// </summary>
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "General")
	TArray<FText> myLoadingScreenTips;

	/// <summary>
	/// The collection of save files the player has created.
	/// </summary>
	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Saving and Loading")
	UPlayerProgressionSaveGame* myCurrentSaveGame;

	/// <summary>
	/// Contains a collection of ATankItem blueprints that are available to
	/// spawn for the player during gameplay.
	/// </summary>
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "General")
	TMap<FName, TSubclassOf<ATankItem>> myWorldItemRepository;

public:
	/// <summary>
	/// Default constructor.
	/// </summary>
	UNeuralTanksGameInstance();

	/// <summary>
	/// Initializes the Neural Tanks Game Instance. This handles registering 
	/// callbacks for loading screen, initializing achievements, applying options 
	/// data, and saving/loading.
	/// </summary>
	virtual void Init() override;

	/// <summary>
	/// Called when the game is loading something (like a level) and requires a loading screen.
	/// 
	/// The way loading screens work in UE4 is a little funky, we have to make a simple SLATE
	/// GUI for the loading screen (see <c>SNeuralTanksLoadingScreen<c>) and then pass it to
	/// the engine's movie player. This allows the game to detect when it is loading something
	/// and then place our loading screen in while its doing the loading.
	/// </summary>
	/// <param name="inMapName">The name of the level/scene/map we are loading into.</param>
	UFUNCTION()
	void OnBeginLoadingScreen(const FString& inMapName);

	/// <summary>
	/// Called once loading has finished and the loading screen is unloaded.
	/// </summary>
	/// <param name="inLoadedWorld">A reference to the world pointer of the level/scene/map we just loaded into.</param>
	UFUNCTION()
	void OnEndLoadingScreen(UWorld* inLoadedWorld);

	/// <summary>
	/// Called when a cheat is turned on or off in the cheats menu.
	/// </summary>
	/// <param name="cheatName">The cheat that was enabled/disabled</param>
	/// <param name="isEnabled">true if we are enabling the provided cheat, false if we are disabling it.</param>
	UFUNCTION()
	void OnCheatToggled(const ECheatName cheatName, const bool isEnabled);

	/// <summary>
	/// Finds a specified tank item that is in the world item repository.
	/// The repository is used as a collection of items that can potentially
	/// be spawned into the level/scene/map.
	/// </summary>
	/// <param name="tankItemName">The name of the tank item you are looking for.</param>
	/// <returns>A pointer to the CDO of the item in the repository.</returns>
	UFUNCTION(BlueprintCallable)
	AActor* RetrieveTankItemFromRepository(const FName tankItemName);

	/// <summary>
	/// Finds a specified tank item that is in the world item repository
	/// (this uses the ItemName from the provided item data).
	/// The repository is used as a collection of items that can potentially
	/// be spawned into the level/scene/map.
	/// </summary>
	/// <param name="theItemData">A pointer to the item data.</param>
	/// <returns>A pointer to the CDO of the item in the repository.</returns>
	AActor* RetrieveTankItemFromData(const UTankItemData* theItemData);

	/// <summary>
	/// Instead of spawning the item into the world, we get the item's data using the default object,
	/// then create a UTankItemData object containing that data. This allows us to (for example) add
	/// items to the inventory without actually spawning them in the current world.
	/// </summary>
	/// <param name="tankItemName">The item name that corresponds to the ATankItem blueprint.</param>
	/// <returns>The corresponding UTankItemData containing the data </returns>
	UFUNCTION(BlueprintCallable)
	UTankItemData* FindTankItemDataFromRepository(const FName tankItemName);

	/// <summary>
	/// The SaveGame function always creates a new save game, if you want to overwrite an existing one, see OverwriteGame().
	/// Also saves meta data.
	/// </summary>
	/// <param name="playerProgressionToSave">The provided save file that needs to be serialized/saved.</param>
	/// <param name="isAutoSave">true if this save was automatically made by the game, false if user created.</param>
	/// <returns>true if successful, false otherwise.</returns>
	UFUNCTION(BlueprintCallable)
	bool SaveGame(UPlayerProgressionSaveGame* playerProgressionToSave, const bool isAutoSave);

	/// <summary>
	/// Overwrites (erases and replaces) a selected save file with the provided save game file.
	/// </summary>
	/// <param name="nameOfSaveFileToOverwrite">The name of the save file (See SaveFileNamesAndDates)</param>
	/// <param name="playerProgressionToSave">The save file to overwrite with.</param>
	/// <returns>true if successful, false otherwise.</returns>
	UFUNCTION(BlueprintCallable)
	bool OverwriteGame(const FName nameOfSaveFileToOverwrite, UPlayerProgressionSaveGame* playerProgressionToSave);

	/// <summary>
	/// Loads the corresponding save file for the provided <c>saveSlotName</c>.
	/// </summary>
	/// <param name="saveSlotName">The name of the save file to load.</param>
	/// <returns>The corresponding save file.</returns>
	UFUNCTION(BlueprintCallable)
	UPlayerProgressionSaveGame* LoadGameBySlotName(const FName& saveSlotName);

	/// <summary>
	/// Serializes and saves the options data to it's save game slot.
	/// </summary>
	/// <returns>true on success, false otherwise.</returns>
	UFUNCTION(BlueprintCallable)
	bool SaveOptions();

	/// <summary>
	/// Loads the options data from it's save game slot. If no data is found,
	/// a new <c>UOptionsData</c> object is created.
	/// </summary>
	/// <returns>The saved options data.</returns>
	UFUNCTION(BlueprintCallable)
	UOptionsData* LoadOptions();

	/// <summary>
	/// Saves the save file meta data to it's save game slot.
	/// </summary>
	/// <returns>true on success, false otherwise.</returns>
	UFUNCTION(BlueprintCallable)
	bool SaveMetaData();

	/// <summary>
	/// Loads the meta data from it's save game slot.
	/// </summary>
	/// <returns>The loaded meta data.</returns>
	UFUNCTION(BlueprintCallable)
	USaveFileMetaData* LoadMetaData();

	/// <summary>
	/// Creates an empty save game to store the players progression
	/// and uses it as the current save game (for player progression).
	/// This also effectively unloads the previous save game.
	/// </summary>
	UFUNCTION(BlueprintCallable)
	void CreateDefaultSaveGameData();

	/// <summary>
	/// Deletes a corresponding save file based on the provided <c>theSaveGameFileNameToDelete</c>.
	/// </summary>
	/// <param name="theSaveGameFileNameToDelete">The name of the save file in the meta data collection.</param>
	/// <returns>true if successfully deleted, false otherwise.</returns>
	UFUNCTION(BlueprintCallable)
	bool DeleteSaveGame(FName theSaveGameFileNameToDelete);

	/// <summary>
	/// Attach the players equipment and accessories based on what is saved in the save file.
	/// </summary>
	/// <param name="pawnNeedingEquipment">The tank needing its equipment (the player).</param>
	/// <returns>true on success, false otherwise.</returns>
	UFUNCTION(BlueprintCallable)
	bool EquipLoadout(ATankPawn* pawnNeedingEquipment);

	/// <summary>
	/// Apply the effects of the attached equipment to the player (such as increased traction).
	/// </summary>
	/// <param name="pawnNeedingEquipmentStats">The tank needing the stats from its equipment (the player).</param>
	/// <returns>true on success, false otherwise.</returns>
	UFUNCTION(BlueprintCallable)
	bool ApplyEquipmentStats(ATankPawn* pawnNeedingEquipmentStats);

	/// <summary>
	/// Returns the currently loaded player progression save file.
	/// </summary>
	/// <returns>The currently loaded player progression save file.</returns>
	UFUNCTION(BlueprintCallable)
	UPlayerProgressionSaveGame* GetPlayerProgressionSaveGame();

	/// <summary>
	/// Returns the currently loaded options data.
	/// </summary>
	/// <returns>The currently loaded options data.</returns>
	UFUNCTION(BlueprintCallable)
	UOptionsData* GetOptionsData();

	/// <summary>
	/// Returns a pointer to the cheat manager. Used for enabling/disabling
	/// cheats.
	/// </summary>
	/// <returns>A pointer to the cheat manager.</returns>
	UFUNCTION(BlueprintCallable)
	UNeuralTanksCheatManager* GetCheatManager();

	/// <summary>
	/// Updates/Changes the current player progression save file.
	/// </summary>
	/// <param name="saveGame">The new save file to start using.</param>
	UFUNCTION(BlueprintCallable)
	void SetPlayerProgressionSaveGame(UPlayerProgressionSaveGame* saveGame);

	/// <summary>
	/// Updates/Changes the master volume in the options menu.
	/// </summary>
	/// <param name="optionsMasterVolume">The new master volume to use.</param>
	UFUNCTION(BlueprintCallable)
	void SetOptionsMasterVolume(const float optionsMasterVolume);

	/// <summary>
	/// Updates/Changes the music volume in the options menu.
	/// </summary>
	/// <param name="optionsMusicVolume">The new volume to use for music.</param>
	UFUNCTION(BlueprintCallable)
	void SetOptionsMusicVolume(const float optionsMusicVolume);

	/// <summary>
	/// Updates/Changes the sound effects volume in the options menu.
	/// </summary>
	/// <param name="optionsSFXVolume">The new volume to use for sound effects.</param>
	UFUNCTION(BlueprintCallable)
	void SetOptionsSFXVolume(const float optionsSFXVolume);

	/// <summary>
	/// Updates/Changes the voice volume in the options menu.
	/// </summary>
	/// <param name="optionsVoiceVolume">The new volume to use for voice lines.</param>
	UFUNCTION(BlueprintCallable)
	void SetOptionsVoiceVolume(const float optionsVoiceVolume);
};