/*
* 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 "Chromosome.h"
#include "NeuronInput.h"
#include "Neuron.generated.h"

/// <summary>
/// <b>Neuron:</b> Holds a collection of values and their corresponding weights. Depending on the product
/// of value * weight, the neuron will be "activated" if that product is above a specific threshold. 
/// </summary>
UCLASS(config = Game)
class UNeuron : public UObject
{
	GENERATED_BODY()

protected:
	/// <summary>
	/// The threshold used for determining if this Neuron is activated or not.
	/// </summary>
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "General")
	float myActivatonThesholdBias;

	/// <summary>
	/// A collection of corresponding values and weights for this Neuron.
	/// </summary>
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "General")
	TArray<FNeuronInput> myNeuronInputs;

public:

	/// <summary>
	/// Default Constructor.
	/// </summary>
	UNeuron();

	/// <summary>
	/// Adds a specificed number of random weights to the neuron.
	/// </summary>
	/// <param name="numberOfInputs">The number of values (default 0) and weights (random float) to add to this Neuron.</param>
	void Initialize(const int numberOfInputs);

	/// <summary>
	/// Updates the inputs and weights to the provided ones.
	/// </summary>
	/// <param name="inputValues">The values to add to this Neuron.</param>
	/// <param name="inputWeights">The corresponding weights to add to this Neuron.</param>
	void Initialize(const TArray<float>& inputValues, const TArray<float>& inputWeights);

	/// <summary>
	/// Adds the provided NeuronInput to the collection of neuron inputs in this Neuron.
	/// </summary>
	/// <param name="neuronInput">The NeuronInput to add.</param>
	void AddInput(const FNeuronInput& neuronInput);

	/// <summary>
	/// Adds the provided value and corresponding weight to this Neuron.
	/// </summary>
	/// <param name="neuronInputValue">The input value to add to this Neuron.</param>
	/// <param name="neuronInputWeight">The corresponding weight to add to this Neuron.</param>
	void AddInput(const float neuronInputValue, const float neuronInputWeight);

	/// <summary>
	/// Calculates the total Activation value of this Neuron by going though
	/// each NeuronInput, multiply its value and weight, accumulate those products,
	/// and then add the threshold to it. Depending on the if this activation value is
	/// above a particular threshold, the value will 0 or 1 (ignore or use).
	/// </summary>
	/// <returns>The activation value of this neuron based on the collection of NeuronInputs.</returns>
	float CalculateActivation();

	/// <summary>
	/// Returns the total number of NeuronInputs in this Neuron's collection.
	/// </summary>
	/// <returns>The total number of NeuronInputs in this Neuron.</returns>
	int GetNumberOfInputs() const;

	/// <summary>
	/// Returns the input value at the provided index.
	/// </summary>
	/// <param name="index">The index of the NeuronInput's value in the collection.</param>
	/// <returns>The value portion of the specified NeuronInput based on the index.</returns>
	float GetInputValueAtIndex(const int index) const;

	/// <summary>
	/// Returns the input weight at the provided index.
	/// </summary>
	/// <param name="index">The index of the NeuronInput's weight in the collection.</param>
	/// <returns>The weight portion of the specified NeuronInput based on the index.</returns>
	float GetInputWeightAtIndex(const int index) const;

	/// <summary>
	/// Assigns the provided input values to this Neurons collection of NeuronInputs.
	/// </summary>
	/// <param name="inputValues">The input values to use for this Neuron.</param>
	void SetInputValues(const TArray<float>& inputValues);

	/// <summary>
	/// Assigns the provided weight values to this Neurons collection of NeuronInputs.
	/// </summary>
	/// <param name="inputValues">The weight values to use for this Neuron.</param>
	void SetInputWeights(const TArray<float>& inputWeights);

	/// <summary>
	/// Assigns a single value in the NeuronInput collection based on the provided index.
	/// </summary>
	/// <param name="index">The index in the NeuronInput collection.</param>
	/// <param name="neuronInputValue">The value to assign to the provided neuron input.</param>
	void SetInputValueAtIndex(const int index, const float neuronInputValue);

	/// <summary>
	/// Assigns a single weight in the NeuronInput collection based on the provided index.
	/// </summary>
	/// <param name="index">The index in the NeuronInput collection.</param>
	/// <param name="neuronInputWeight">The value to assign to the provided neuron input.</param>
	void SetInputWeightAtIndex(const int index, const float neuronInputWeight);
};