/// <summary>
        /// Chooses 4 moves for a pokemon.
        /// </summary>
        /// <param name="info">The parameters to use when choosing a move.</param>
        /// <returns>An Id for the move that was chosen.</returns>
        /// <exception cref="ArgumentException">If there are not enough moves to choose from.</exception>
        private int ChooseMove(PokemonAndMoveInfo info)
        {
            // Try with current damage setting
            if (!PickOneMove(info, out var chosenMoveId))
            {
                // Give up
                throw new ArgumentException("Not enough moves to choose from!");
            }
            info.AlreadyPicked.Add(chosenMoveId);
            var move1Obj = info.AllPossibleMovesOrig.First(m => m.MoveId == chosenMoveId);

            info.AlreadyPickedEffects.Add(move1Obj.Effect);
            return(chosenMoveId);
        }
        /// <summary>
        /// Fills in all the params needed to calculate 4 moves for a pokmeon. Treats special pokemon uniquely <see cref="SPECIALPOKEMON"/>.
        /// </summary>
        /// <param name="poke">Pokemon to choose moves for</param>
        /// <param name="allPossibleMoves">A list of all possible moves available for the pokemon to learn</param>
        /// <param name="TMBank">The curreent bank of TM's available</param>
        /// <returns>A pokemon fulley equipped with moves.</returns>
        private Pokemon AssignMovestoPokemon(Pokemon poke, IList <PokemonMoveSetResult> allPossibleMoves, IList <int> TMBank)
        {
            var chosenMoves = new Stack <int>();
            var info        = new PokemonAndMoveInfo
            {
                Pokemon             = poke,
                PokeTypes           = poke.Types,
                PreferredDamageType = poke.SpAttack > poke.Attack ? DamageType.Special : DamageType.Physical
            };

            info.PreferredDamageType = Math.Abs(poke.SpAttack - poke.Attack) < _config.Value.Configuration.DamageTypeDelta ? DamageType.Both : info.PreferredDamageType;
            var enemiesWeakAgainst = _pokemonRepository.GetWeaknesses(string.Join(",", info.PokeTypes ?? new List <string>(0)));

            info.AttackTypesToFavor = _pokemonRepository.GetWeaknesses(string.Join(",", enemiesWeakAgainst ?? new List <string>(0))).ToList();

            // Prune moves removing and replacing as needed
            var screenedMoves = new List <PokemonMoveSetResult>();

            foreach (var move in allPossibleMoves)
            {
                // Generate random move for sketch
                if (move.MoveId == 166)
                {
                    var randos = _pokemonRepository.GetRandomMoves(_config.Value.Configuration.RandomMoveMinPower, _config.Value.Configuration.RandomMoveMaxPower).ToList();
                    screenedMoves.Add(randos[_random.Next(0, randos.Count)]);
                    continue;
                }

                // remove duplicates
                if (screenedMoves.Any(m => m.Equals(move)))
                {
                    continue;
                }

                // Remove if unavailable in TM bank and HM bank
                if (string.Equals(move.LearnType, "machine", StringComparison.OrdinalIgnoreCase) &&
                    !_config.Value.Configuration.HMBank.Contains(move.MoveId) && !TMBank.Contains(move.MoveId))
                {
                    continue;
                }

                screenedMoves.Add(move);
            }

            allPossibleMoves = screenedMoves;

            // Choose moves
            if (_config.Value.Configuration.SpecialPokemon.Contains(poke.SpeciesId))
            {
                foreach (var m in allPossibleMoves)
                {
                    chosenMoves.Push(m.MoveId);
                }
            }
            else
            {
                info.AllPossibleMovesOrig = new List <PokemonMoveSetResult>(allPossibleMoves);
                info.DoSomeDamageFlag     = false;

                for (var i = 0; i < 4; i++)
                {
                    var move = ChooseMove(info);
                    chosenMoves.Push(move);
                    info.DoSomeDamageFlag = !info.DoSomeDamageFlag;
                    if (TMBank.Contains(move))
                    {
                        TMBank.Remove(move);
                    }
                }
            }

            // We aren't using these
            poke.Move1PowerPointsUps = 0;
            poke.Move2PowerPointsUps = 0;
            poke.Move3PowerPointsUps = 0;
            poke.Move4PowerPointsUps = 0;

            // Set moves
            poke.MoveIndex1 = (byte)(chosenMoves.Count > 0 ? chosenMoves.Pop() : 0);
            poke.MoveIndex2 = (byte)(chosenMoves.Count > 0 ? chosenMoves.Pop() : 0);
            poke.MoveIndex3 = (byte)(chosenMoves.Count > 0 ? chosenMoves.Pop() : 0);
            poke.MoveIndex4 = (byte)(chosenMoves.Count > 0 ? chosenMoves.Pop() : 0);

            // Set move names
            poke.Move1Name = allPossibleMoves.ToList().FirstOrDefault(m => m.MoveId == poke.MoveIndex1)?.MoveName ?? "";
            poke.Move2Name = allPossibleMoves.ToList().FirstOrDefault(m => m.MoveId == poke.MoveIndex2)?.MoveName ?? "";
            poke.Move3Name = allPossibleMoves.ToList().FirstOrDefault(m => m.MoveId == poke.MoveIndex3)?.MoveName ?? "";
            poke.Move4Name = allPossibleMoves.ToList().FirstOrDefault(m => m.MoveId == poke.MoveIndex4)?.MoveName ?? "";

            // Set pp
            poke.Move1PowerPointsCurrent = (byte)(allPossibleMoves.ToList().FirstOrDefault(m => m.MoveId == poke.MoveIndex1)?.Pp ?? 0);
            poke.Move2PowerPointsCurrent = (byte)(allPossibleMoves.ToList().FirstOrDefault(m => m.MoveId == poke.MoveIndex2)?.Pp ?? 0);
            poke.Move3PowerPointsCurrent = (byte)(allPossibleMoves.ToList().FirstOrDefault(m => m.MoveId == poke.MoveIndex3)?.Pp ?? 0);
            poke.Move4PowerPointsCurrent = (byte)(allPossibleMoves.ToList().FirstOrDefault(m => m.MoveId == poke.MoveIndex4)?.Pp ?? 0);

            return(poke);
        }
        /// <summary>
        /// By far the most complex method here.
        /// Uses the parameters to choose one move for a pokemon by assigning a probability value to each possible move.
        /// The higher this value is compared to all of the other moves, the more likely this move is to be chosen.
        /// (See: <see cref="GeneratorConfig"/>, <see cref="PokemonAndMoveInfo"/>)
        /// </summary>
        private bool PickOneMove(PokemonAndMoveInfo info, out int chosenMoveId)
        {
            chosenMoveId = 0;
            var allPossibleMoves = new List <PokemonMoveSetResult>(info.AllPossibleMovesOrig);

            // Calculate probabilities
            var moveProbabilities = allPossibleMoves.Select(m =>
            {
                var moveChoice = new MoveChoice
                {
                    Probability = Likeliness.Full,
                    Move        = m
                };

                // filter out all non-damaging/damaging attacks depending on the flag
                if (info.DoSomeDamageFlag && (m.Power ?? 0) <= 0)
                {
                    moveChoice.Probability *= Likeliness.Extremely_Low;
                }

                if (!info.DoSomeDamageFlag && (m.Power ?? 0) > 0)
                {
                    moveChoice.Probability *= Likeliness.Extremely_Low;
                }

                // Apply weight on damage type
                // if damage type does not mesh with the pokemon, make the likelyhood VERY unlikely
                if ((info.PreferredDamageType == DamageType.Special && (m.DamageType ?? "special").Equals("physical")) ||
                    (info.PreferredDamageType == DamageType.Physical && (m.DamageType ?? "special").Equals("special")))
                {
                    moveChoice.Probability *= _config.Value.Configuration.DamageTypeModifier;
                }

                // Apply weight on effect
                foreach (var key in _config.Value.Configuration.MoveEffectFilters.Keys)
                {
                    if (m.Effect.Contains(key))
                    {
                        moveChoice.Probability *= _config.Value.Configuration.MoveEffectFilters[key];
                    }
                }

                // Apply weight on type
                if ((info.PokeTypes?.Contains(m.Type, StringComparer.CurrentCultureIgnoreCase) ?? false) ||
                    (info.AttackTypesToFavor.Contains(m.Type, StringComparer.CurrentCultureIgnoreCase)))
                {
                    moveChoice.Probability *= _config.Value.Configuration.SameTypeModifier;
                }

                // Apply weight on damage
                if (info.DoSomeDamageFlag)
                {
                    moveChoice.Probability *= Likeliness.Full + (m.Power ?? 0) / _config.Value.Configuration.DamageModifier;
                }

                // Apply special weight for paired moves
                if (info.AlreadyPicked.Any(id => _config.Value.Configuration.PairedMoves.ContainsKey(id) && _config.Value.Configuration.PairedMoves[id].Contains(m.MoveId)))
                {
                    moveChoice.Probability = Likeliness.Full * _config.Value.Configuration.PairedModifier;
                }

                // Filter out dependant moves which do not already have their dependency picked
                if (_config.Value.Configuration.DependantMoves.ContainsKey(m.MoveId) && !_config.Value.Configuration.DependantMoves[m.MoveId].Intersect(info.AlreadyPicked).Any())
                {
                    moveChoice.Probability = Likeliness.None;
                }

                // Apply weight on similar moves to already picked
                if (!string.IsNullOrWhiteSpace(m.Effect) && info.AlreadyPickedEffects.Contains(m.Effect, StringComparer.CurrentCultureIgnoreCase))
                {
                    moveChoice.Probability *= _config.Value.Configuration.AlreadyPickedMoveEffectsModifier;
                }

                // Finally, apply weight on moves already picked
                if (info.AlreadyPicked.Contains(m.MoveId))
                {
                    moveChoice.Probability *= _config.Value.Configuration.AlreadyPickedMoveModifier;
                }

                return(moveChoice);
            }).ToList();

            // Return false if we filtered out all of our choices
            if (!moveProbabilities.Any() || moveProbabilities.All(choice => choice.Probability == 0))
            {
                return(false);
            }

            // Choose with probabilities
            var chosen = _probabilityUtility.ChooseWithProbability(moveProbabilities.Cast <IChoice>().ToList());

            if (chosen != null)
            {
                chosenMoveId = moveProbabilities[(int)chosen].Move.MoveId;
                return(true);
            }
            return(false);
        }