using CoreLibrary.Exceptions;
using System.Security.Cryptography;

namespace CoreLibrary
{
    /// <summary>
    /// Représente un plateau de jeu composer de code
    /// </summary>
    public class Plateau
    {
        private readonly Code codeSecret;
        private readonly Code?[] grille;
        private readonly IEnumerable<Indicateur>[] indicateurs;

        private readonly int tailleCode;

        /// <summary>
        /// Obtient le numéro de tour actuel.
        /// </summary>
        public int Tour { get; private set; } = 1;

        /// <summary>
        /// Obtient True si le joueur a gagné sinon False
        /// </summary>
        public bool Victoire { get; private set; } = false;

        /// <summary>
        /// Initialise une nouvelle instance de la classe <see cref="Plateau"/>.
        /// </summary>
        /// <param name="tailleCode">Taille des codes du plateau</param>
        /// <param name="tailleGrille">Taille du plateau de jeu</param>
        /// <exception cref="TailleCodeException">Levée lorsque la tailleCode est inférieur ou égal à 0.</exception>
        /// <exception cref="TailleGrilleException">Levée lorsque la tailleGrille est inférieur ou égal à 0.</exception>
        public Plateau(int tailleCode, int tailleGrille)
        {
            if(tailleCode <= 0)
            {
                throw new TailleCodeException(tailleCode);
            }

            if (tailleGrille <= 0)
            {
                throw new TailleGrilleException(tailleGrille);
            }

            codeSecret = new Code(tailleCode);
            grille = new Code?[tailleGrille];
            indicateurs = new IEnumerable<Indicateur>[tailleGrille];

            this.tailleCode = tailleCode;

            GenererCodeAleatoire();
        }

        /// <summary>
        /// Génère un code secret aléatoire en utilisant des jetons de couleurs aléatoire. 
        /// </summary>
        private void GenererCodeAleatoire()
        {
            Couleur[] couleurs = (Couleur[])Enum.GetValues(typeof(Couleur));

            for (int i = 0; i < tailleCode; ++i)
            {
                codeSecret.AjouterJeton(new Jeton(couleurs[RandomNumberGenerator.GetInt32(0, couleurs.Length)]));
            }
        }

        /// <summary>
        /// Vérifie si le plateau de jeu est plein.
        /// </summary>
        /// <returns>True si la tableau est plein, sinon False.</returns>
        public bool EstComplet()
        {
            return Tour - 1 == grille.Length;
        }

        /// <summary>
        ///  Ajoute le code fourni au plateau de jeu.
        /// </summary>
        /// <param name="code">Le code à ajouter au plateau de jeu.</param>
        /// <exception cref="CodeInvalideException">Levée lorsque le code fourni à une taille invalide.</exception>
        /// <exception cref="CodeIncompletException">Levée lorsque le code fourni est incomplet.</exception>
        public void AjouterCode(Code code)
        {
            if (code.TailleMaximale() != tailleCode) 
            {
                throw new CodeInvalideException(code.TailleMaximale(), tailleCode);
            } 
            
            if (!code.EstComplet()) 
            { 
                throw new CodeIncompletException(); 
            }

            indicateurs[Tour - 1] = codeSecret.Comparer(code);
            grille[Tour - 1] = code;
            ++Tour;

            if (EstBonCode(code))
            {
                Victoire = true;
            }
        }

        /// <summary>
        /// Vérifie si le code fourni correspond au code secret.
        /// </summary>
        /// <param name="code">Le code à vérifier.</param>
        /// <returns>True si le code fourni correspond au code secret, sinon False </returns>
        /// <exception cref="CodeInvalideException">Levée lorsque le code fourni à une taille invalide.</exception>
        /// <exception cref="CodeIncompletException">Levée lorsque le code fourni est incomplet.</exception>
        public bool EstBonCode(Code code)
        {
            if (code.TailleMaximale() != tailleCode)
            {
                throw new CodeInvalideException(code.TailleMaximale(), tailleCode);
            }

            if (!code.EstComplet())
            {
                throw new CodeIncompletException();
            }

            IEnumerable<Indicateur> indicateursCode = codeSecret.Comparer(code);

            if (indicateursCode.Count() != tailleCode)
            {
                return false;
            }

            foreach (Indicateur indicateur in indicateursCode)
            {
                if (indicateur != Indicateur.BONNEPLACE)
                {
                    return false;
                }
            }

            return true;
        }

        /// <summary>
        /// Récupère le plateau de jeu.
        /// </summary>
        /// <returns>Le plateau de jeu représenté sous forme d'une collections de jetons.</returns>
        public IEnumerable<IEnumerable<Jeton?>> Grille()
        {
            IEnumerable<Jeton?>[] grilleJetons = new IEnumerable<Jeton?>[grille.Length];

            for (int i = 0; i < grille.Length; ++i)
            {
                grilleJetons[i] = grille[i]?.Jetons() ?? new Code(tailleCode).Jetons();
            }

            return grilleJetons;
        }

        /// <summary>
        /// Récupère les indicateurs pour chaque tour.
        /// </summary>
        /// <returns>Les indicateurs pour chaque tour représentés sous forme d'une collections d'indicateurs.</returns>
        public IEnumerable<IEnumerable<Indicateur>> Indicateurs()
        {
            return indicateurs;
        }
    }
}

