/// <summary>
 /// Retourne le nom complet du type se trouvant dans un des assemblys donnés, ainsi que
 /// l'assembly dans lequel il se trouve.
 /// </summary>
 /// <param name="loadedAssemblies">Assemblies chargés</param>
 /// <param name="loadedNamespaces">Namespaces chargés</param>
 /// <param name="incompleteTypeName">Nom incomplet du type à retrouver.</param>
 /// <returns></returns>
 public static Tuple <Assembly, string> FindFullName(Dictionary <string, Assembly> loadedAssemblies, List <string> loadedNamespaces, string incompleteTypeName)
 {
     ExpressionTree.GlobalContext glob    = new ExpressionTree.GlobalContext();
     ExpressionTree.Context       context = new ExpressionTree.Context(glob);
     glob.LoadedAssemblies = loadedAssemblies;
     glob.LoadedNamespaces = loadedNamespaces;
     return(FindFullName(context, incompleteTypeName));
 }
        /// <summary>
        /// Retourne le nom complet du type dans l'assembly donné.
        /// En gros cela rajoute le nom de l'assembly au nom du type donné.
        /// </summary>
        /// <param name="context">Contexte actuel.</param>
        /// <param name="assembly">Assembly contenant le type.</param>
        /// <param name="incompleteTypeName">Nom incomplet du type à retrouver.</param>
        /// <returns></returns>
        public static string FindFullName(ExpressionTree.Context context, Assembly assembly, string incompleteTypeName, bool throwOnError)
        {
            // Vérifie d'abord parmi les types système.
            var sysType = SystemType(incompleteTypeName);

            if (sysType != null)
            {
                return(sysType.FullName);
            }

            string namespaceName = null;

            // Vérifie que le nom du type ne soit pas déjà complet :
            if (assembly.GetType(incompleteTypeName) != null)
            {
                return(incompleteTypeName);
            }

            // Si ce n'est pas le cas :
            foreach (string _namespace in context.GlobalContext.LoadedNamespaces)
            {
                // On ajoute Namespace+Type
                string temp = String.Format("{0}.{1}", _namespace, incompleteTypeName);
                // Si le type existe dans cet assembly.
                if (assembly.GetType(temp) != null)
                {
                    if (namespaceName == null)
                    {
                        namespaceName = _namespace;
                    }
                    else
                    {
                        // Balance l'exception même si throwOnError vaut true.
                        throw new InterpreterException(String.Format("Le type {0} existe dans les namespaces {1} et {2}",
                                                                     incompleteTypeName, namespaceName, _namespace));
                    }
                }
            }
            if (namespaceName == null)
            {
                if (throwOnError)
                {
                    throw new InterpreterException(String.Format("Le type {0} ne peut pas être reconnu dans l'assembly {1}",
                                                                 incompleteTypeName, assembly.FullName));
                }
                else
                {
                    return(null);
                }
            }
            else
            {
                return(String.Format("{0}.{1}", namespaceName, incompleteTypeName));
            }
        }
        /// <summary>
        /// Retourne le nom complet du type se trouvant dans un des assemblys donnés, ainsi que
        /// l'assembly dans lequel il se trouve.
        /// </summary>
        /// <param name="context">Contexte actuel.</param>
        /// <param name="incompleteTypeName">Nom incomplet du type à retrouver.</param>
        /// <returns></returns>
        public static Tuple <Assembly, string> FindFullName(ExpressionTree.Context context, string incompleteTypeName)
        {
            // Vérifie d'abord parmi les types système.
            var sysType = SystemType(incompleteTypeName);

            if (sysType != null)
            {
                return(new Tuple <Assembly, string>(Assembly.GetAssembly(sysType), sysType.FullName));
            }

            string fullName = null;

            // Assemblies du contexte.
            IEnumerable <Assembly> assemblies = context.GlobalContext.LoadedAssemblies.Values;
            Assembly assembly = null;

            foreach (Assembly a in assemblies)
            {
                string temp = FindFullName(context, a, incompleteTypeName, false);
                if (temp != null)
                {
                    // Vérifie qu'on a pas déja un résultat
                    // Si c'est le cas, le type existe dans 2 assemblies.
                    if (fullName == null)
                    {
                        fullName = temp;
                        assembly = a;
                    }
                    else
                    {
                        throw new InterpreterException(String.Format("Le type {0} existe dans les assemblys {1} et {2}",
                                                                     incompleteTypeName, a.FullName, assembly.FullName));
                    }
                }
            }
            if (fullName == null)
            {
                throw new InterpreterException(String.Format("Le type {0} n'a pas été trouvé.",
                                                             incompleteTypeName));
            }
            else
            {
                return(new Tuple <Assembly, string>(assembly, fullName));
            }
        }
        /// <summary>
        /// Recherche un type défini par un nom donné.
        /// Cette méthode n'est pas faîte pour être exécutée un trop grand nombre de fois.
        /// Il est préférable de mettre en cache les résultats.
        /// </summary>
        /// <param name="context">Contexte où se trouve le type.</param>
        /// <param name="typename">Nom complet du type. Ex : Machin.Truc.Bidule.
        /// Si le nom n'est pas complet, le paramètre isFullTypeName doit valoir false.</param>
        /// <param name="assemblyNamme">Nom de l'assembly contenant le type.
        /// Null ou "default" signifie qu'il s'agit de l'assembly en cours d'exécution.</param>
        /// <returns>Un tuple contenant : le type de l'objet, le type des arguments génériques si le type
        /// concerné est générique, sinon, null.</returns>
        public static Type FindType(ExpressionTree.Context context,
                                    string assemblyName,
                                    string typename,
                                    List <ExpressionTree.IGettable> genericParameters,
                                    List <List <ExpressionTree.IGettable> > indexingParameters,
                                    bool isFullTypeName)
        {
            // Assembly content le type
            Assembly assembly;
            bool     isGeneric = genericParameters.Count != 0;
            bool     isArray   = indexingParameters.Count != 0;

            // Build le vrai nom complet du type.
            // ----------------------------------
            StringBuilder fullTypeNameBuilder = new StringBuilder();

            fullTypeNameBuilder.Append(typename);
            if (isGeneric)
            {
                fullTypeNameBuilder.Append('`');
                fullTypeNameBuilder.Append(genericParameters.Count);
            }
            if (isArray)
            {
                foreach (List <ExpressionTree.IGettable> parameters in indexingParameters)
                {
                    fullTypeNameBuilder.Append('[');
                    for (int i = 0; i < parameters.Count - 1; i++)
                    {
                        fullTypeNameBuilder.Append(',');
                    }
                    fullTypeNameBuilder.Append(']');
                }
            }

            typename = fullTypeNameBuilder.ToString();

            // Trouve l'assembly contenant le nom du type.
            if (assemblyName == null)
            {
                assembly = null;
            }
            else if (assemblyName == "default")
            {
                // méthode sale pour obtenir l'assembly contenant les types systèmes.
                assembly = Assembly.GetAssembly(typeof(char));
            }
            else
            {
                assembly = context.GlobalContext.LoadedAssemblies[assemblyName];
            }

            // Maintenant il faut juste trouver le type dans un assembly.
            // -----------------------------------------------------------

            // Si le nom du type n'est pas complet, on essaie de retrouver le nom complet :
            if (!isFullTypeName)
            {
                if (assembly == null)
                {
                    // Assembly / nom complet du type
                    var tup = FindFullName(context, typename);
                    assembly = tup.Item1;
                    typename = tup.Item2;
                }
                else
                {
                    typename = FindFullName(context, assembly, typename, true);
                }
            }
            else
            {
                // Si on a pas le nom de l'assembly : on va chercher !
                if (assembly == null)
                {
                    assembly = SeekAssemblyContainingType(context.GlobalContext.LoadedAssemblies.Values, typename);
                }
            }
            if (genericParameters.Count == 0)
            {
                // On n'a qu'à chercher le type dans l'assembly.
                return(assembly.GetType(typename));
            }
            else
            {
                Type[] genericParams = new Type[genericParameters.Count];
                for (int i = 0; i < genericParameters.Count; i++)
                {
                    genericParams[i] = ((ExpressionTree.InternalTypeRepresentation)genericParameters[i].GetValue(context)).T;
                }
                return(assembly.GetType(typename).MakeGenericType(genericParams));
            }
        }