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

class UArrowComponent;
class ATankProjectile;
class ATankItem;

/// <summary>
/// The base class for all Tanks in the game. This class handles
/// generic functionality that is common in all tanks such as if 
/// this tank is destroyed or has started. In addition to that,
/// generic projectile and cannon functionality is handled here.
/// </summary>
UCLASS(config=Game)
class ATankPawn : public APawn
{
	GENERATED_BODY()

protected:
	/// <summary>
	/// true if this tank is destroyed and out of commission, false if
	/// the tank is alive and usable. 
	/// </summary>
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "General")
	bool myIsDestroyed;

	/// <summary>
	/// true if the player cannot lose health or armor, false in normal operation.
	/// </summary>
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Internal")
	bool myIsGodModeEnabled;

	/// <summary>
	/// true if the tank engine has started (and the tank can be controlled),
	/// false otherwise. 
	/// </summary>
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "General")
	bool myHasStarted;

	/// <summary>
	/// The initial speed of the projectile obtained from the CDO of the TankProjectile.
	/// </summary>
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "General")
	float myCannonProjectileInitialSpeed;

	/// <summary>
	/// The current time since the last cannon fire.
	/// </summary>
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "General")
	float myCannonCurrentReloadTime;

	/// <summary>
	/// The amount of time (in seconds) it takes to reload the main cannon.
	/// </summary>
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Progression")
	float myCannonReloadTime;

	/// <summary>
	/// The velocity magnitude of this pawn. 
	/// </summary>
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Engine")
	float mySpeed;

	/// <summary>
	/// The current health points of the player.
	/// </summary>
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Progression")
	float myCurrentHealthPoints;

	/// <summary>
	/// The current armor points of the player.
	/// </summary>
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Progression")
	float myCurrentArmorPoints;

	/// <summary>
	/// The maximum health points of the player.
	/// </summary>
	UPROPERTY(EditAnywhere, BlueprintReadWrite, SaveGame, Category = "Progression")
	float myMaximumHealthPoints;

	/// <summary>
	/// The maximum armor points of the player.
	/// </summary>
	UPROPERTY(EditAnywhere, BlueprintReadWrite, SaveGame, Category = "Progression")
	float myMaximumArmorPoints;

	/// <summary>
	/// The amount of armor to give the player every second.
	/// </summary>
	UPROPERTY(EditAnywhere, BlueprintReadWrite, SaveGame, Category = "Progression")
	float myArmorRegenerationAmountPerSecond;

	/// <summary>
	/// Used as the spawn location for the fired cannon shell.
	/// </summary>
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Internal")
	UArrowComponent* myMuzzleArrow;

	/// <summary>
	/// The static mesh for the cannon portion of this tank.
	/// </summary>
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Internal")
	UStaticMeshComponent* myTankCannon;

	/// <summary>
	/// The spawned tank cannon static mesh.
	/// </summary>
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Mesh")
	UStaticMesh* myTankCannonMesh;

	/// <summary>
	/// The effects to use when this tank is destroyed. 
	/// </summary>
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Particles")
	UParticleSystemComponent* myDestroyedTankParticles;

	/// <summary>
	/// Particle effect(s) used when firing the cannon.
	/// </summary>
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Particles")
	UParticleSystem* myMuzzleFlashParticles;

	/// <summary>
	/// Particle effect(s) used when firing the cannon.
	/// </summary>
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Particles")
	UParticleSystem* myMuzzleSmokeParticles;

	/// <summary>
	/// The audio sound effect to play when the main cannon is fired.
	/// </summary>
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Audio")
	USoundBase* myTankCannonFireAudio;

	/// <summary>
	/// The audio sound effect to play when the cannon is reloading.
	/// </summary>
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Audio")
	UAudioComponent* myTankCannonReloadAudio;

	/// <summary>
	/// Allows us to get the properties of the projectile without spawning it. 
	/// </summary>
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Internal")
	ATankProjectile* myDefaultProjectileObject;

	/// <summary>
	/// The most recently spawned cannon projectile.
	/// </summary>
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Internal")
	ATankProjectile* myRecentlySpawnedProjectile;

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

	/// <summary>
	/// The cannon bullet/projectile to spawn when firing. 
	/// </summary>
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "General")
	TSubclassOf<ATankProjectile> myCannonProjectile;

	/// <summary>
	/// A Collection of all this tank's components with their corresponding names as keys.
	/// </summary>
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Internal")
	TMap<FName, UActorComponent*> myComponents;

	/// <summary>
	/// Finds this tank's blueprint components and initializes them.
	/// </summary>
	virtual void FindComponents();

	/// <summary>
	/// Finds and returns the corresponding tank component based on its name.
	/// </summary>
	/// <param name="name">The component name.</param>
	/// <returns>The corresponding tank component.</returns>
	UActorComponent* GetComponentByName(const FName& name);

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

	/// <summary>
	/// Called when this blueprint is first opened or initialized. This is equivalent to a 
	/// constructor but for Unreal Engine. 
	/// </summary>
	/// <param name="transform">The transform the actor was constructed at.</param>
	virtual void OnConstruction(const FTransform& transform) override;

	/// <summary>
	/// Called when this tank first spawns in the world/level.
	/// </summary>
	UFUNCTION(BlueprintCallable)
	virtual void BeginPlay() override;

	/// <summary>
	/// Called once per frame to repair the tank over time. 
	/// Keeps track of the current cannon reload time (the 
	/// time between shots in seconds).
	/// </summary>
	/// <param name="deltaTime">Time in milliseconds between frames.</param>
	virtual void Tick(float deltaTime) override;

	/// <summary>
	/// Called when the tank overlaps with a <c>TankAmmunition</c> in the game world.
	/// Depending on the tank, this has different outcomes. If it's an <c>AAITank</c>, 
	/// the fitness of that Neural Networked tank gets increased. If it's a <c>APlayerTank</c>,
	/// the player earns some money.
	/// </summary>
	virtual void OnTankOverlappedItem(ATankItem* theItem);

	/// <summary>
	/// Called when this tank has been hit with a projectile.
	/// Typically implemented in children.
	/// </summary>
	/// <param name="theAttackingPawn">The tank that hit this tank/player.</param>
	/// <param name="damage">The amount of damage caused by the hit.</param>
	virtual void OnTankHit(ATankPawn* theAttackingPawn, float damage);

	/// <summary>
	/// Called when this player has successfully hit an enemy tank.
	/// Typically implemented in children.
	/// </summary>
	/// <param name="theHitTankPawn">The tank that the player hit.</param>
	virtual void OnSuccessfullyHitTank(ATankPawn* theHitTankPawn);

	/// <summary>
	/// Used to notify the blueprint side that this tank was hit.
	/// </summary>
	/// <param name="theAttackingPawn">The tank that hit this tank/player.</param>
	/// <param name="damage">The amount of damage caused by the hit.</param>
	UFUNCTION(BlueprintImplementableEvent)
	void OnTankHitBlueprint(ATankPawn* theAttackingPawn, float damage);

	/// <summary>
	/// Spawns and fires this tank's projectile. Also plays sounds and
	/// spawns particle effects. Impulse is also applied to the tank cannon.
	/// </summary>
	/// <param name="cannonReloadTime">The amount of time in seconds before this Tank can fire again.</param>
	/// <returns>true if a projectile was spawned and fired, false otherwise.</returns>
	UFUNCTION(BlueprintCallable)
	virtual bool FireCannon(const float cannonReloadTime);

	/// <summary>
	/// Determines how fast the reload sound effect (approximate) should play based on the amount
	/// of time it takes to actually reload the cannon.
	/// 
	/// Since the multiplier is 1 when the reload time is 3 seconds (original playback time), and 
	/// when the multiplier is 2, the reload time is 1.5 seconds. Using this info we can derive a 
	/// linear formula using y=mx+b so we know the multiplier for almost any provided reload time.
	/// </summary>
	/// <param name="cannonReloadTime">The amount of time in seconds for the cannon to reload.</param>
	float CalculateReloadAudioSpeedBasedOnReloadTime(const float cannonReloadTime);

	/// <summary>
	/// Applies damage to this tank by subtracting the provided <c>damageAmount</c>
	/// from first the armor points, and then the health points.
	/// </summary>
	/// <param name="damageAmount">The amount of damage to apply to the tank.</param>
	void DamageTank(const float damageAmount);

	/// <summary>
	/// Removes damage from this tank by adding the provided <c>healAmount</c> to the health points.
	/// </summary>
	/// <param name="healAmount">The amount of health points to add back.</param>
	void HealTank(const float healAmount);

	/// <summary>
	/// Removes damage from this tank by adding the provided <c>repairAmount</c> to the armor points.
	/// </summary>
	/// <param name="repairAmount">The amount of armor points to add back.</param>
	void RepairTank(const float repairAmount);

	/// <summary>
	/// Returns true if this tank has been destroyed and is immobile,false if it is alive and moving.
	/// </summary>
	/// <returns>true if this tank has been destroyed and is immobile, false if it is alive and moving.</returns>
	UFUNCTION(BlueprintCallable)
	bool GetIsTankDestroyed();

	/// <summary>
	/// If a landscape exists, returns the landscape height provided location.
	/// If no landscape is used, then the original provided location's Z component is returned.
	/// </summary>
	/// <returns>The landscape height at the provided world/level location.</returns>
	UFUNCTION(BlueprintCallable)
	float GetLandscapeHeightAtLocation(const FVector& location);

	/// <summary>
	/// If a landscape exists, returns the landscape height at the tank's actor location.
	/// If no landscape is used, then the original actor's location Z component is returned.
	/// </summary>
	/// <returns>The landscape height at this tank's world/level location.</returns>
	UFUNCTION(BlueprintCallable)
	float GetLandscapeHeightAtTankLocation();

	/// <summary>
	/// Returns the amount of armor (AP) that is regenerated every second for this tank.
	/// </summary>
	/// <returns>The amount of armor (AP) that is regenerated every second for this tank.</returns>
	float GetArmorRegenerationAmountPerSecond();

	/// <summary>
	/// Returns the current amount of health points (HP) this tank has.
	/// </summary>
	/// <returns>The current amount of health points (HP) this tank has.</returns>
	float GetCurrentHealthPoints();

	/// <summary>
	/// Returns the current amount of armor points (AP) this tank has.
	/// </summary>
	/// <returns>The current amount of armor points (AP) this tank has.</returns>
	float GetCurrentArmorPoints();

	/// <summary>
	/// Returns the total amount of health points (HP) this tank can have.
	/// </summary>
	/// <returns>The total amount of health points (HP) this tank can have.</returns>
	float GetMaximumHealthPoints();

	/// <summary>
	/// Returns the total amount of armor points (AP) this tank can have.
	/// </summary>
	/// <returns>The total amount of armor points (AP) this tank can have.</returns>
	float GetMaximumArmorPoints();

	/// <summary>
	/// Returns the amount of time (in seconds) the cannon can fire another projectile.
	/// </summary>
	/// <returns>The amount of time (in seconds) the cannon can fire another projectile.</returns>
	float GetCannonReloadTime();

	/// <summary>
	/// Returns true if the invincibility (god mode) cheat is enabled for this tank.
	/// </summary>
	/// <returns>true if the invincibility (god mode) cheat is enabled for this tank, false otherwise.</returns>
	bool GetIsGodModeEnabled();

	/// <summary>
	/// Updates/Changes the amount of armor points (AP) given back every second to the tank.
	/// </summary>
	/// <param name="armorRegenerationAmount">The new amount of armor given back every second to the tank.</param>
	void SetArmorRegenerationAmountPerSecond(const float armorRegenerationAmount);

	/// <summary>
	/// Updates/Changes the maximum amount of health points (HP) the tank has.
	/// </summary>
	/// <param name="maximumHealthPoints">The new amount of health points (HP) the tank has.</param>
	void SetMaximumHealthPoints(const float maximumHealthPoints);

	/// <summary>
	/// Updates/Changes the maximum amount of armor points (AP) the tank has.
	/// </summary>
	/// <param name="maximumArmorPoints">The new amount of armor points (AP) the tank has.</param>
	void SetMaximumArmorPoints(const float maximumArmorPoints);

	/// <summary>
	/// Updates/Changes the amount of time (in seconds) it takes the cannon
	/// to reload and fire another projectile.
	/// </summary>
	/// <param name="cannonReloadTime">The new amount of time (in seconds) it takes for the cannon to reload.</param>
	void SetCannonReloadTime(const float cannonReloadTime);

	/// <summary>
	/// Updates/Changes this tank to be invulnerable (god mode).
	/// This is normally set to false unless the cheat is enabled.
	/// </summary>
	/// <param name="isGodModeEnabled">true to enable god mode, false to disable it.</param>
	void SetIsGodModeEnabled(const bool isGodModeEnabled);
};

