/// <summary>
        /// Creates an object of the specified base type, registering the type if necessary
        /// </summary>
        /// <param name="BaseType">The base type</param>
        /// <returns>Returns an object of the specified base type</returns>
        public virtual object Create(Type BaseType)
        {
            if (!Classes.ContainsKey(BaseType))
            {
                Setup(BaseType);
            }
            object ReturnObject = Classes[BaseType].Assembly.CreateInstance(Classes[BaseType].FullName);

            Aspects.ForEach(x => x.Setup(ReturnObject));
            return(ReturnObject);
        }
        private void SetupMethod(Type BaseType, IMethodBuilder Method, IPropertyBuilder AspectusStarting,
                                 IPropertyBuilder AspectusEnding, IPropertyBuilder AspectusException, MethodInfo BaseMethod)
        {
            if (BaseMethod == null)
            {
                BaseMethod = BaseType.GetMethod(Method.Name);
            }
            Method.SetCurrentMethod();
            System.Reflection.Emit.Label EndLabel = Method.Generator.DefineLabel();
            VariableBase ReturnValue = Method.ReturnType != typeof(void) ? Method.CreateLocal("FinalReturnValue", Method.ReturnType) : null;

            Utilities.Reflection.Emit.Commands.Try Try = Method.Try();
            {
                SetupStart(Method, EndLabel, ReturnValue, AspectusStarting);
                Aspects.ForEach(x => x.SetupStartMethod(Method, BaseType));
                List <ParameterBuilder> Parameters = new List <ParameterBuilder>();
                Method.Parameters.For(1, Method.Parameters.Count - 1, x => Parameters.Add(x));
                if (Method.ReturnType != typeof(void) && BaseMethod != null)
                {
                    ReturnValue.Assign(Method.This.Call(BaseMethod, Parameters.ToArray()));
                }
                else if (BaseMethod != null)
                {
                    Method.This.Call(BaseMethod, Parameters.ToArray());
                }
                SetupEnd(Method, ReturnValue, AspectusEnding);
                Aspects.ForEach(x => x.SetupEndMethod(Method, BaseType, ReturnValue));
                Method.Generator.MarkLabel(EndLabel);
            }
            Utilities.Reflection.Emit.Commands.Catch Catch = Try.StartCatchBlock(typeof(System.Exception));
            {
                SetupException(Method, Catch, AspectusException);
                Aspects.ForEach(x => x.SetupExceptionMethod(Method, BaseType));
                Catch.Rethrow();
            }
            Try.EndTryBlock();

            if (Method.ReturnType != typeof(void))
            {
                Method.Return(ReturnValue);
            }
            else
            {
                Method.Return();
            }
        }
        /// <summary>
        /// Generates the specified type.
        /// </summary>
        /// <param name="namespace">The namespace.</param>
        /// <param name="className">Name of the class.</param>
        /// <param name="usings">The usings.</param>
        /// <param name="interfaces">The interfaces.</param>
        /// <param name="assembliesUsing">The assemblies using.</param>
        /// <returns>The string representation of the generated class</returns>
        public string Generate(string @namespace, string className, List <string> usings, List <Type> interfaces, List <Assembly> assembliesUsing)
        {
            var Builder = new StringBuilder();

            Builder.AppendLineFormat(@"namespace {1}
{{
    {0}

    public class {2} : {3}{4} {5}
    {{
", usings.ToString(x => "using " + x + ";", "\r\n"),
                                     @namespace,
                                     className,
                                     DeclaringType.FullName.Replace("+", "."),
                                     interfaces.Count > 0 ? "," : "", interfaces.ToString(x => x.Name));
            if (DeclaringType.HasDefaultConstructor() || DeclaringType.IsInterface)
            {
                Builder.AppendLine(new ConstructorGenerator(DeclaringType.GetConstructors(BindingFlags.Public | BindingFlags.Instance)
                                                            .FirstOrDefault(x => x.GetParameters().Length == 0),
                                                            DeclaringType)
                                   .Generate(assembliesUsing, Aspects));
            }

            Aspects.ForEach(x => Builder.AppendLine(x.SetupInterfaces(DeclaringType)));

            Type TempType           = DeclaringType;
            var  MethodsAlreadyDone = new List <string>();

            while (TempType != null)
            {
                foreach (PropertyInfo Property in TempType.GetProperties(BindingFlags.Public | BindingFlags.DeclaredOnly | BindingFlags.Instance))
                {
                    MethodInfo GetMethodInfo = Property.GetGetMethod();
                    MethodInfo SetMethodInfo = Property.GetSetMethod();
                    if (!MethodsAlreadyDone.Contains("get_" + Property.Name) &&
                        !MethodsAlreadyDone.Contains("set_" + Property.Name) &&
                        GetMethodInfo != null &&
                        GetMethodInfo.IsVirtual &&
                        SetMethodInfo != null &&
                        SetMethodInfo.IsPublic &&
                        !GetMethodInfo.IsFinal &&
                        Property.GetIndexParameters().Length == 0)
                    {
                        Builder.AppendLine(new PropertyGenerator(Property).Generate(assembliesUsing, Aspects));
                        MethodsAlreadyDone.Add(GetMethodInfo.Name);
                        MethodsAlreadyDone.Add(SetMethodInfo.Name);
                    }
                    else if (!MethodsAlreadyDone.Contains("get_" + Property.Name) &&
                             GetMethodInfo != null &&
                             GetMethodInfo.IsVirtual &&
                             SetMethodInfo == null &&
                             !GetMethodInfo.IsFinal &&
                             Property.GetIndexParameters().Length == 0)
                    {
                        Builder.AppendLine(new PropertyGenerator(Property).Generate(assembliesUsing, Aspects));
                        MethodsAlreadyDone.Add(GetMethodInfo.Name);
                    }
                    else
                    {
                        if (GetMethodInfo != null)
                        {
                            MethodsAlreadyDone.Add(GetMethodInfo.Name);
                        }
                        if (SetMethodInfo != null)
                        {
                            MethodsAlreadyDone.Add(SetMethodInfo.Name);
                        }
                    }
                }
                foreach (MethodInfo Method in TempType.GetMethods(BindingFlags.Public | BindingFlags.DeclaredOnly | BindingFlags.Instance)
                         .Where(x => !MethodsAlreadyDone.Contains(x.Name) &&
                                x.IsVirtual &&
                                !x.IsFinal &&
                                !x.IsPrivate &&
                                !x.Name.StartsWith("add_", StringComparison.InvariantCultureIgnoreCase) &&
                                !x.Name.StartsWith("remove_", StringComparison.InvariantCultureIgnoreCase) &&
                                !x.IsGenericMethod))
                {
                    Builder.AppendLine(new MethodGenerator(Method).Generate(assembliesUsing, Aspects));
                    MethodsAlreadyDone.Add(Method.Name);
                }
                TempType = TempType.BaseType;
                if (TempType == typeof(object))
                {
                    break;
                }
            }
            Builder.AppendLine(@"   }
}");
            return(Builder.ToString());
        }
        /// <summary>
        /// Sets up a type so it can be used in the system later
        /// </summary>
        /// <param name="Type">Type to set up</param>
        public virtual void Setup(Type Type)
        {
            if (Classes.ContainsKey(Type))
            {
                return;
            }
            if (new FileInfo(AssemblyDirectory + AssemblyName + ".dll").Exists &&
                !RegenerateAssembly)
            {
                throw new ArgumentException("Type specified not found and can't be generated due to being in 'GoDaddy' mode. Delete already generated DLL to add new types or set RegenerateAssembly to true.");
            }
            List <Type> Interfaces = new List <Type>();

            Aspects.ForEach(x => Interfaces.AddRange(x.InterfacesUsing == null ? new List <Type>() : x.InterfacesUsing));
            Interfaces.Add(typeof(IEvents));
            Utilities.Reflection.Emit.TypeBuilder Builder = AssemblyBuilder.CreateType(AssemblyName + "." + Type.Name + "Derived",
                                                                                       System.Reflection.TypeAttributes.Class | System.Reflection.TypeAttributes.Public,
                                                                                       Type,
                                                                                       Interfaces);
            {
                IPropertyBuilder AspectusStarting  = Builder.CreateDefaultProperty("Aspectus_Starting", typeof(EventHandler <Starting>));
                IPropertyBuilder AspectusEnding    = Builder.CreateDefaultProperty("Aspectus_Ending", typeof(EventHandler <Ending>));
                IPropertyBuilder AspectusException = Builder.CreateDefaultProperty("Aspectus_Exception", typeof(EventHandler <Utilities.Reflection.AOP.EventArgs.Exception>));

                Aspects.ForEach(x => x.SetupInterfaces(Builder));

                Type          TempType           = Type;
                List <string> MethodsAlreadyDone = new List <string>();
                while (TempType != null)
                {
                    foreach (PropertyInfo Property in TempType.GetProperties(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.DeclaredOnly | BindingFlags.Instance))
                    {
                        MethodInfo GetMethodInfo = Property.GetGetMethod();
                        if (!MethodsAlreadyDone.Contains("get_" + Property.Name) &&
                            !MethodsAlreadyDone.Contains("set_" + Property.Name) &&
                            GetMethodInfo != null &&
                            GetMethodInfo.IsVirtual &&
                            !GetMethodInfo.IsFinal)
                        {
                            IPropertyBuilder OverrideProperty = Builder.CreateProperty(Property.Name, Property.PropertyType,
                                                                                       System.Reflection.PropertyAttributes.SpecialName,
                                                                                       MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig | MethodAttributes.Virtual,
                                                                                       MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig | MethodAttributes.Virtual);
                            {
                                IMethodBuilder Get = OverrideProperty.GetMethod;
                                {
                                    SetupMethod(Type, Get, AspectusStarting, AspectusEnding, AspectusException, null);
                                    MethodsAlreadyDone.Add(Get.Name);
                                }
                                IMethodBuilder Set = OverrideProperty.SetMethod;
                                {
                                    SetupMethod(Type, Set, AspectusStarting, AspectusEnding, AspectusException, null);
                                    MethodsAlreadyDone.Add(Set.Name);
                                }
                            }
                        }
                    }
                    foreach (MethodInfo Method in TempType.GetMethods(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.DeclaredOnly | BindingFlags.Instance))
                    {
                        if (!MethodsAlreadyDone.Contains(Method.Name) && Method.IsVirtual && !Method.IsFinal)
                        {
                            MethodAttributes MethodAttribute = MethodAttributes.Virtual | MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.Public;
                            if (Method.IsStatic)
                            {
                                MethodAttribute |= MethodAttributes.Static;
                            }
                            List <Type> ParameterTypes = new List <Type>();
                            Method.GetParameters().ForEach(x => ParameterTypes.Add(x.ParameterType));
                            IMethodBuilder OverrideMethod = Builder.CreateMethod(Method.Name,
                                                                                 MethodAttribute,
                                                                                 Method.ReturnType,
                                                                                 ParameterTypes);
                            SetupMethod(Type, OverrideMethod, AspectusStarting, AspectusEnding, AspectusException, Method);
                            MethodsAlreadyDone.Add(Method.Name);
                        }
                    }

                    TempType = TempType.BaseType;
                    if (TempType == typeof(object))
                    {
                        break;
                    }
                }

                Classes.Add(Type, Builder.Create());
            }
        }