/// <summary>
        /// Adds a node by selecting a random connection, disabling it and replacing it by 2 genes.
        /// The first gene goes from the original input node, to the new node with a weight of 1.
        /// The second gene goes from the the new node to the old output node with the same weight as the original connection.
        /// </summary>
        /// <param name="trainingRoomSettings">The training room settings.</param>
        /// <param name="getAndIncreaseNodeIdFunction">The get and increase node id function.</param>
        /// <param name="innovationFunction">The innovation function.</param>
        private void AddNodeMutation(TrainingRoomSettings trainingRoomSettings, Func <uint> getAndIncreaseNodeIdFunction, Func <uint, uint, uint> innovationFunction)
        {
            // If the connection genes count is 0, then there are not connection genes to mutate.
            if (ConnectionGenes.Count == 0)
            {
                return;
            }

            // Find a random connection.
            ConnectionGene theChosenOne = ConnectionGenes.ElementAt(trainingRoomSettings.Random.Next(ConnectionGenes.Count));

            // Disable the connection gene.
            theChosenOne.Enabled = false;

            // Generate the new node's id.
            uint newNodeId = getAndIncreaseNodeIdFunction();

            // Get the old in and out node identifiers.
            uint oldInId  = theChosenOne.InNodeIdentifier;
            uint oldOutId = theChosenOne.OutNodeIdentifier;

            // Create a connectionGene from oldIn (a) to new (c) and from new (c) to oldOut (b)
            ConnectionGene aToC = new ConnectionGene(Id, innovationFunction(oldInId, newNodeId), oldInId, newNodeId, 1);
            ConnectionGene cToB = new ConnectionGene(Id, innovationFunction(newNodeId, oldOutId), newNodeId, oldOutId, theChosenOne.Weight);

            // Add new connection genes.
            ConnectionGenes.Add(aToC);
            ConnectionGenes.Add(cToB);

            // Update the max innovation.
            _maxInnovation = Math.Max(Math.Max(cToB.InnovationNumber, aToC.InnovationNumber), _maxInnovation);
        }
        /// <summary>
        /// Adds a node by selecting a random connection, disabling it and replacing it by 2 genes.
        /// The first gene goes from the original input node, to the new node with a weight of 1.
        /// The second gene goes from the the new node to the old output node with the same weight as the original connection.
        /// </summary>
        private void AddNodeMutation()
        {
            if (ConnectionGenes.Count == 0)
            {
                return;
            }

            // Choose a random connection to add a node into
            ConnectionGene theChosenOne = ConnectionGenes.ElementAt(TrainingRoom.Random.Next(ConnectionGenes.Count));

            // Disable it
            theChosenOne.Enabled = false;

            // Generate the new node's id
            uint newNodeId = TrainingRoom.GetAndIncreaseNodeId();

            uint oldInId  = theChosenOne.InId;
            uint oldOutId = theChosenOne.OutId;

            // Create a connectionGene from oldIn (a) to new (c) and from new (c) to oldOut (b)
            ConnectionGene aToC = new ConnectionGene(Id, oldInId, newNodeId, 1, TrainingRoom.GetInnovationNumber(oldInId, newNodeId));
            ConnectionGene cToB = new ConnectionGene(Id, newNodeId, oldOutId, theChosenOne.Weight, TrainingRoom.GetInnovationNumber(newNodeId, oldOutId));

            ConnectionGenes.Add(aToC);
            ConnectionGenes.Add(cToB);
            _maxInnovation = Math.Max(Math.Max(cToB.InnovationNumber, aToC.InnovationNumber), _maxInnovation);
        }
        /// <summary>
        /// Mutates the the organism based on the training room settings.
        /// </summary>
        /// <param name="trainingRoomSettings">The training room settings.</param>
        /// <param name="getAndIncreaseNodeIdFunction">The get and increase node id function.</param>
        /// <param name="innovationFunction">The innovation function.</param>
        public void Mutate(TrainingRoomSettings trainingRoomSettings, Func <uint> getAndIncreaseNodeIdFunction, Func <uint, uint, uint> innovationFunction)
        {
            // If the random value is lower than the add connection chance add a connection mutation.
            if (trainingRoomSettings.Random.NextDouble() < trainingRoomSettings.AddConnectionChance)
            {
                AddConnectionMutation(trainingRoomSettings, innovationFunction);
            }

            // If the random value is lower than the add node chance add a node mutation.
            if (trainingRoomSettings.Random.NextDouble() < trainingRoomSettings.AddNodeChance)
            {
                AddNodeMutation(trainingRoomSettings, getAndIncreaseNodeIdFunction, innovationFunction);
            }

            // If the random value is higher than the mutate weight chance or the connection gene count is lower than 0 return early.
            if (trainingRoomSettings.Random.NextDouble() > trainingRoomSettings.MutateWeightChance || ConnectionGenes.Count <= 0)
            {
                return;
            }

            // Find a random connection gene.
            ConnectionGene connectionGene = ConnectionGenes.ElementAt(trainingRoomSettings.Random.Next(ConnectionGenes.Count));

            // If the random value is lower than the weight reassign chance set a new weight else add weight.
            if (trainingRoomSettings.Random.NextDouble() < trainingRoomSettings.WeightReassignChance)
            {
                connectionGene.Weight = trainingRoomSettings.Random.NextDouble() * 2 - 1;
            }
            else
            {
                connectionGene.Weight += (trainingRoomSettings.Random.NextDouble() * 2 - 1) * 0.1;
            }

            //TODO: Inputs mutate?
        }
        /// <summary>
        /// Mutates the brain, each time this is called there is a chance to:
        /// - Add a connection
        /// - Add a node
        /// - Change the weight of an existing gene, which can either randomly be reset to a random value or slightly change
        /// These changes can all happen in one call, but the chance that this happens depends on the training room settings.
        /// </summary>
        public void Mutate()
        {
            if (TrainingRoom.Random.NextDouble() < TrainingRoom.TrainingRoomSettings.AddConnectionChance)
            {
                AddConnectionMutation();
            }

            if (TrainingRoom.Random.NextDouble() < TrainingRoom.TrainingRoomSettings.AddNodeChance)
            {
                AddNodeMutation();
            }

            if ((TrainingRoom.Random.NextDouble() < TrainingRoom.TrainingRoomSettings.MutateWeightChance) && ConnectionGenes.Count > 0)
            {
                ConnectionGene connectionGene = ConnectionGenes.ElementAt(TrainingRoom.Random.Next(ConnectionGenes.Count));
                if (TrainingRoom.Random.NextDouble() < TrainingRoom.TrainingRoomSettings.WeightReassignChance)
                {
                    connectionGene.Weight = TrainingRoom.Random.NextDouble() * 2 - 1;
                }
                else
                {
                    connectionGene.Weight += (TrainingRoom.Random.NextDouble() * 2 - 1) * 0.1;
                }
            }
        }