/*
* 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 <Landscape.h>
#include "TankItem.generated.h"

class UTankItemData;
class APhysicsTankPawn;
class APlayerTankPawn;

/// <summary>
/// Represents a category/type for an item. Vanity and Accessories are the same,
/// they are visual with no stat improvements and are "equipped" in acessory slots.
/// </summary>
UENUM(BlueprintType)
enum class EItemType : uint8
{
	VE_AMMO 			UMETA(DisplayName = "Ammunition"),
	VE_ARMOR			UMETA(DisplayName = "Armor"),
	VE_EQUIPMENT		UMETA(DisplayName = "Equipment"),
	VE_SPECIALARMOR		UMETA(DisplayName = "Special Armor"),
	VE_VANITY			UMETA(DisplayName = "Vanity")
};

/// <summary>
/// Represents a locaton on the player tank where an item is attached. 
/// For example, the gold tank cannon has the equpment slot VE_CANNON.
/// </summary>
UENUM(BlueprintType)
enum class EEquipmentSlot : uint8
{
	VE_NONE				UMETA(DisplayName = "None"),
	VE_BODY 			UMETA(DisplayName = "Tank Body Slot"),
	VE_CANNON			UMETA(DisplayName = "Tank Cannon Slot"),
	VE_GEAR				UMETA(DisplayName = "Tank Gear Slot"),
	VE_HITCH			UMETA(DisplayName = "Tank Body Hitch Slot"),
	VE_LEFTCHEST		UMETA(DisplayName = "Tank Turret Left Chest Slot"),
	VE_RIGHTCHEST		UMETA(DisplayName = "Tank Turret Right Chest Slot"),
	VE_TRACK			UMETA(DisplayName = "Tank Track Slot"),
	VE_TURRET			UMETA(DisplayName = "Tank Turret Slot"),
	VE_WHEEL			UMETA(DisplayName = "Tank Wheel Slot")
};

/// <summary>
/// The base class for all items in the game. This represents an item that can be equipped and/or
/// collected by a Player or Enemy tank.
/// </summary>
UCLASS()
class ATankItem : public AActor
{
	GENERATED_BODY()

protected:
	/// <summary>
	/// The amount of money this item cost to buy from the store.
	/// </summary>
	UPROPERTY(EditAnywhere, SaveGame, Category = "General")
	int myItemCost;

	/// <summary>
	/// The category type for this item.
	/// </summary>
	UPROPERTY(EditAnywhere, SaveGame, Category = "General")
	EItemType myItemType;

	/// <summary>
	/// Some items can have an equipment slot they can be equipped in.
	/// </summary>
	UPROPERTY(EditAnywhere, SaveGame, Category = "General")
	EEquipmentSlot myEquipmentSlot;

	/// <summary>
	/// The name of the item (typically used in the inventory).
	/// </summary>
	UPROPERTY(EditAnywhere, SaveGame, Category = "General")
	FString myItemName;

	/// <summary>
	/// The description of the item (typically used in the inventory).
	/// </summary>
	UPROPERTY(EditAnywhere, SaveGame, Category = "General")
	FText myItemDescription;

	/// <summary>
	/// The image to use for this item in the inventory.
	/// </summary>
	UPROPERTY(EditAnywhere, SaveGame, BlueprintReadWrite, Category = "General")
	UTexture2D* myInventoryTexture;

	/// <summary>
	/// The static mesh applied to the player pawn's corresponding component(s).
	/// This is septate mostly because myStaticMeshComponent is not available at the time 
	/// of equipment loading.
	/// </summary>
	UPROPERTY(EditAnywhere, SaveGame, BlueprintReadWrite, Category = "General")
	UStaticMesh* myWearableStaticMesh;

	/// <summary>
	/// The skeletal mesh applied to the player pawn's corresponding component(s).
	/// This is typically used for the Tank Boobs. 
	/// </summary>
	UPROPERTY(EditAnywhere, SaveGame, BlueprintReadWrite, Category = "General")
	USkeletalMesh* myWearableSkeletalMesh;

	/// <summary>
	/// The static mesh component of the item (if one exists). This is the game world representation.
	/// </summary>
	UPROPERTY(EditAnywhere, SaveGame, BlueprintReadWrite, Category = "Internal")
	UStaticMeshComponent* myStaticMeshComponent;

	/// <summary>
	/// The current landscape the item is on.
	/// </summary>
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Internal")
	ALandscape* myLandscape;

	/// <summary>
	/// Additional meshes associated with this item but are hidden so the player does not have to manually equip them.
	/// For example, the Left/Right LMG/LMG Mounts that need setting when the turret is changed.
	/// </summary>
	UPROPERTY(EditAnywhere, SaveGame, BlueprintReadWrite)
	TMap<FString, UStaticMesh*> myAdditionalStaticMeshes;

public:
	/// <summary>
	/// Default constructor for the Tank Item.
	/// </summary>
	ATankItem();

	/// <summary>
	/// Initializes this Tank Ammunition Item with the provided data.
	/// </summary>
	/// <param name="itemCost">The ammount of money the item costs in the shop. (TankAmmunitionItem is not available in the shop, only the world/level.)</param>
	/// <param name="itemType">The category of item such as Armor, Equipment, etc.</param>
	/// <param name="itemName">The unique name for this item.</param>
	/// <param name="itemDescription">The localized description of the name.</param>
	void Initialize(const int itemCost, const EItemType itemType, const EEquipmentSlot equipmentSlot, const FString& itemName, const FText& itemDescription);

	/// <summary>
	/// Called when this item first loads in the level/world.
	/// </summary>
	virtual void BeginPlay() override;

	/// <summary>
	/// Adds this item object to the provided player equipment slot.
	/// </summary>
	/// <param name="physPawnNeedingEquipment">The player to equip this item to.</param>
	/// <returns>True if successfully equipped, false otherwise.</returns>
	bool EquipToPawn(APhysicsTankPawn* physPawnNeedingEquipment);

	/// <summary>
	/// Adds the provided itemData to the provided player equipment slot. Since the UTankItemData class is 
	/// strictly data, I decided to use a static function here instead of adding functions the data class.
	/// </summary>
	/// <param name="itemData">The item to equip to the physPawnNeedingEquipment.</param>
	/// <param name="physPawnNeedingEquipment">The pawn to equip itemData to.</param>
	/// <returns>True if successfully equipped, false otherwise.</returns>
	static bool EquipToPawn(UTankItemData* itemData, APhysicsTankPawn* physPawnNeedingEquipment);
	
	/// <summary>
	/// Apply this items stats to the item. Stats are attributes given to the player
	/// while they are using/wearing a particular item. For example, gold tracks improve
	/// the traction the player has.
	/// </summary>
	/// <param name="playerTankPawnNeedingStats">The player to apply the stats to.</param>
	/// <returns>True if successfully applied, false otherwise.</returns>
	UFUNCTION(BlueprintImplementableEvent)
	bool ApplyItemStatsToPawn(APlayerTankPawn* playerTankPawnNeedingStats);

	/// <summary>
	/// Called when the a tank overlaps with this Tank Item.
	/// 
	/// Parent Comment: Event called when something starts to overlaps this component, for example a player walking into a trigger.
	/// For events when objects have a blocking collision, for example a player hitting a wall, see 'Hit' events.
	///	
	/// @note Both this component and the other one must have GetGenerateOverlapEvents() set to true to generate overlap events.
	/// @note When receiving an overlap from another object's movement, the directions of 'Hit.Normal' and 'Hit.ImpactNormal'
	/// will be adjusted to indicate force from the other object against this object.
	/// 
	/// </summary>
	/// <param name="overlappedComp">A pointer to the specific component in this Actor that was involved with the overlap.</param>
	/// <param name="otherActor">A pointer to the Actor whose component triggered the overlap.</param>
	/// <param name="otherComp">A pointer to the specific component on otherActor that caused the overlap</param>
	/// <param name="otherBodyIndex">The index of the body part that was overlapped. This is relevant for skeletal meshes or other objects with multiple physics bodies, and it is usually 0 for simple components.</param>
	/// <param name="bFromSweep">True if the overlap was the result of a sweep test, false otherwise.</param>
	/// <param name="sweepResult">Struct which contains detailed information about the overlap, such as the location and normal of the impact point.</param>
	UFUNCTION()
	virtual void OnTankItemBeginOverlap(UPrimitiveComponent* overlappedComp, AActor* other, UPrimitiveComponent* otherComp, int32 otherBodyIndex, bool bFromSweep, const FHitResult& sweepResult);
	
	/// <summary>
	/// Convert's the provided Tank Item actor to its serializeable (saveable) data equivilent.
	/// The <c>UTankItemData</c> class is a data holder for <c>ATankItem<c>.
	/// </summary>
	/// <param name="theTankItemActor">The Tank Item Actor to convert.</param>
	/// <returns>The converted <c>UTankItemData</c> that can be serialized (saved).</returns>
	static UTankItemData* CreateDataFromTankItemActor(const ATankItem* theTankItemActor);

	/// <summary>
	/// Returns the amount of money it cost for the item in the shop. 
	/// (Some items like TankAmmunition cannot be purchased).
	/// </summary>
	/// <returns>The amount of money it cost for the item in the shop.</returns>
	int GetItemCost() const;

	/// <summary>
	/// Returns the landscape height at a given X,Y location. If not using a landscape
	/// or failure, the current actor location's Z value is returned.
	/// </summary>
	/// <param name="location">The world X,Y location.</param>
	/// <returns>The landscape height at a given X,Y location. If not using a landscape or failure, the current actor location's Z value is returned.</returns>
	UFUNCTION(BlueprintCallable)
	float GetLandscapeHeightAtLocation(FVector location) const;

	/// <summary>
	/// Returns the category/type for this item.
	/// </summary>
	/// <returns>The category/type for this item.</returns>
	EItemType GetItemType() const;

	/// <summary>
	/// Returns the equipment slot/location that this item is attached to.
	/// </summary>
	/// <returns>The equipment slot/location that this item is attached to.</returns>
	EEquipmentSlot GetEquipmentSlot() const;

	/// <summary>
	/// Returns the name of the item.
	/// </summary>
	/// <returns>The name of the item.</returns>
	FString GetItemName() const;

	/// <summary>
	/// Returns the description of the item (typically used in the inventory).
	/// </summary>
	/// <returns>The description of the item (typically used in the inventory).</returns>
	FText GetItemDescription() const;

	/// <summary>
	/// Returns the image used to represent this item in the inventory.
	/// </summary>
	/// <returns>The image used to represent this item in the inventory.</returns>
	UTexture2D* GetInventoryTexture() const;

	/// <summary>
	/// Returns the static mesh component of the item (if one exists). This is the game world representation.
	/// </summary>
	/// <returns>The static mesh component of the item (if one exists). This is the game world representation.</returns>
	UStaticMeshComponent* GetStaticMeshComponent() const;

	/// <summary>
	/// Returns the static mesh applied to the player pawn's corresponding component(s).
	/// This is septate mostly because myStaticMeshComponent is not available at the time 
	/// of equipment loading.
	/// </summary>
	/// <returns>The static mesh applied to the player pawn's corresponding component(s).</returns>
	UStaticMesh* GetWearableStaticMesh() const;

	/// <summary>
	/// Returns the skeletal mesh applied to the player pawn's corresponding component(s).
	/// This is typically used for the Tank Boobs. 
	/// </summary>
	/// <returns>The skeletal mesh applied to the player pawn's corresponding component(s).</returns>
	USkeletalMesh* GetWearableSkeletalMesh() const;

	/// <summary>
	/// Returns the additional meshes associated with this item but are hidden so the player does not have to manually equip them.
	/// For example, the Left/Right LMG/LMG Mounts that need setting when the turret is changed.
	/// </summary>
	/// <returns>Additional meshes associated with this item but are hidden so the player does not have to manually equip them.</returns>
	TMap<FString, UStaticMesh*> GetAdditionalStaticMeshes() const;

	/// <summary>
	/// Changes this item's price/cost in the store to the provided itemCost.
	/// </summary>
	/// <param name="itemCost">The new price/cost of this item.</param>
	void SetItemCost(const int itemCost);

	/// <summary>
	/// Changes this item's category/type to the provided itemType.
	/// </summary>
	/// <param name="itemType">The new category/type for this item.</param>
	void SetItemType(const EItemType itemType);

	/// <summary>
	/// Changes this item's name to the provided itemName.
	/// </summary>
	/// <param name="itemName">The new item name for this item.</param>
	void SetItemName(const FString& itemName);

	/// <summary>
	/// Changes this item's description to the provided itemDescription.
	/// (This text is localized).
	/// </summary>
	/// <param name="itemDescription">The new item description for this item.</param>
	void SetItemDescription(const FText& itemDescription);
};
