// TODO override with attribute ? e.g. [ObjC.Selector ("foo")]
        string ImplementMethod(MethodInfo info, string name, bool isExtension = false, PropertyInfo pi = null, bool useTypeNames = false)
        {
            var type = info.DeclaringType;
            var managed_type_name = NameGenerator.GetObjCName(type);

            string objcsig;
            string monosig;
            var    managed_name   = info.Name;
            var    parametersInfo = info.GetParameters();

            GetSignatures(name, managed_name, (MemberInfo)pi ?? info, parametersInfo, useTypeNames, isExtension, out objcsig, out monosig);

            var builder = new MethodHelper(headers, implementation)
            {
                AssemblySafeName = type.Assembly.GetName().Name.Sanitize(),
                IsStatic         = info.IsStatic,
                IsExtension      = isExtension,
                ReturnType       = GetReturnType(type, info.ReturnType),
                ManagedTypeName  = type.FullName,
                MetadataToken    = info.MetadataToken,
                MonoSignature    = monosig,
                ObjCSignature    = objcsig,
                ObjCTypeName     = managed_type_name,
                IsValueType      = type.IsValueType,
                IsVirtual        = info.IsVirtual,
            };

            if (pi == null)
            {
                builder.WriteHeaders();
            }

            builder.BeginImplementation();
            builder.WriteMethodLookup();

            string postInvoke = String.Empty;
            var    args       = "nil";

            if (parametersInfo.Length > 0)
            {
                Generate(parametersInfo, isExtension, out postInvoke);
                args = "__args";
            }

            builder.WriteInvoke(args);

            // ref and out parameters might need to be converted back
            implementation.Write(postInvoke);
            ReturnValue(info.ReturnType);
            builder.EndImplementation();
            return(objcsig);
        }
        void GenerateDefaultValuesWrapper(string name, MethodBase mb, ParameterInfo[] parameters, int start)
        {
            MethodInfo    mi = mb as MethodInfo;
            string        objcsig;
            string        monosig;
            var           parametersInfo = parameters;
            var           plist          = new List <ParameterInfo> ();
            StringBuilder arguments      = new StringBuilder();

            headers.WriteLine("/** This is an helper method that inlines the following default values:");
            foreach (var p in parameters)
            {
                string pName = NameGenerator.GetExtendedParameterName(p, parameters);
                if (arguments.Length == 0)
                {
                    arguments.Append(p.Name.PascalCase()).Append(':');
                }
                else
                {
                    arguments.Append(' ').Append(p.Name.CamelCase()).Append(':');
                }
                if (p.Position >= start && p.HasDefaultValue)
                {
                    var raw = FormatRawValue(p.ParameterType, p.RawDefaultValue);
                    headers.WriteLine($" *     ({NameGenerator.GetTypeName (p.ParameterType)}) {pName} = {raw};");
                    arguments.Append(raw);
                }
                else
                {
                    arguments.Append(pName);
                    plist.Add(p);
                }
            }
            headers.WriteLine(" *");
            headers.WriteLine($" *  @see {name}");
            headers.WriteLine(" */");

            if (mi == null)
            {
                name = start == 0 ? "init" : "initWith";
            }
            else
            {
                name = mb.Name.CamelCase();
            }

            GetSignatures(name, mb.Name, mb, plist.ToArray(), false, false, out objcsig, out monosig);
            var type    = mb.DeclaringType;
            var builder = new MethodHelper(headers, implementation)
            {
                IsStatic      = mb.IsStatic,
                ReturnType    = mi == null ? "nullable instancetype" : GetReturnType(type, mi.ReturnType),
                ObjCSignature = objcsig,
            };

            builder.WriteHeaders();
            headers.WriteLine();

            builder.BeginImplementation();
            if (mi == null || !mi.ReturnType.Is("System", "Void"))
            {
                implementation.Write("return [");
            }
            if (mb.IsStatic)
            {
                implementation.Write(NameGenerator.GetObjCName(mi.DeclaringType));
                implementation.Write(' ');
            }
            else
            {
                implementation.Write("self ");
            }
            if (mi == null)
            {
                name = "initWith";
            }
            implementation.WriteLine($"{name}{arguments}];");
            builder.EndImplementation();
        }
        protected override void Generate(ProcessedType type)
        {
            Type t           = type.Type;
            var  aname       = t.Assembly.GetName().Name.Sanitize();
            var  static_type = t.IsSealed && t.IsAbstract;

            var managed_name = NameGenerator.GetObjCName(t);

            List <string> conformed_protocols = new List <string> ();

            foreach (var i in t.GetInterfaces())
            {
                if (protocols.Contains(i))
                {
                    conformed_protocols.Add(NameGenerator.GetObjCName(i));
                }
            }

            var tbuilder = new ClassHelper(headers, implementation)
            {
                AssemblyQualifiedName = t.AssemblyQualifiedName,
                AssemblyName          = aname,
                BaseTypeName          = NameGenerator.GetTypeName(t.BaseType),
                Name            = NameGenerator.GetTypeName(t),
                Namespace       = t.Namespace,
                ManagedName     = t.Name,
                Protocols       = conformed_protocols,
                IsBaseTypeBound = types.Contains(t.BaseType),
                IsStatic        = t.IsSealed && t.IsAbstract,
                MetadataToken   = t.MetadataToken,
            };

            tbuilder.BeginHeaders();
            tbuilder.BeginImplementation();

            var default_init = false;
            List <ProcessedConstructor> constructors;

            if (ctors.TryGetValue(t, out constructors))
            {
                // First get the unavailable init ctor selectors in parent class
                var unavailableCtors = GetUnavailableParentCtors(t, constructors);
                if (unavailableCtors.Count() > 0)
                {
                    // TODO: Print a #pragma mark once we have a well defined header structure http://nshipster.com/pragma/
                    foreach (var uctor in unavailableCtors)
                    {
                        var    ctorparams = uctor.Constructor.GetParameters();
                        string name       = "init";
                        string signature  = ".ctor()";
                        if (ctorparams.Length > 0)
                        {
                            GetSignatures("initWith", uctor.Constructor.Name, uctor.Constructor, ctorparams, uctor.FallBackToTypeName, false, out name, out signature);
                        }
                        headers.WriteLine("/** This initializer is not available as it was not re-exposed from the base type");
                        headers.WriteLine(" *  For more details consult https://github.com/mono/Embeddinator-4000/blob/master/docs/ObjC.md#constructors-vs-initializers");
                        headers.WriteLine(" */");
                        headers.WriteLine($"- (nullable instancetype){name} NS_UNAVAILABLE;");
                        headers.WriteLine();
                    }
                }

                foreach (var ctor in constructors)
                {
                    var pcount = ctor.Constructor.ParameterCount;
                    default_init |= pcount == 0;

                    var    parameters = ctor.Constructor.GetParameters();
                    string name       = "init";
                    string signature  = ".ctor()";
                    if (parameters.Length > 0)
                    {
                        GetSignatures("initWith", ctor.Constructor.Name, ctor.Constructor, parameters, ctor.FallBackToTypeName, false, out name, out signature);
                    }

                    var builder = new MethodHelper(headers, implementation)
                    {
                        AssemblySafeName = aname,
                        ReturnType       = "nullable instancetype",
                        ManagedTypeName  = t.FullName,
                        MetadataToken    = ctor.Constructor.MetadataToken,
                        MonoSignature    = signature,
                        ObjCSignature    = name,
                        ObjCTypeName     = managed_name,
                        IsConstructor    = true,
                        IsValueType      = t.IsValueType,
                        IgnoreException  = true,
                    };

                    builder.WriteHeaders();

                    builder.BeginImplementation();
                    builder.WriteMethodLookup();

                    // TODO: this logic will need to be update for managed NSObject types (e.g. from XI / XM) not to call [super init]
                    implementation.WriteLine("if (!_object) {");
                    implementation.Indent++;
                    implementation.WriteLine($"MonoObject* __instance = mono_object_new (__mono_context.domain, {managed_name}_class);");

                    string postInvoke = String.Empty;
                    var    args       = "nil";
                    if (pcount > 0)
                    {
                        Generate(parameters, false, out postInvoke);
                        args = "__args";
                    }
                    builder.WriteInvoke(args);
                    implementation.Write(postInvoke);
                    implementation.WriteLine("_object = mono_embeddinator_create_object (__instance);");
                    implementation.Indent--;
                    implementation.WriteLine("}");
                    if (types.Contains(t.BaseType))
                    {
                        implementation.WriteLine("return self = [super initForSuper];");
                    }
                    else
                    {
                        implementation.WriteLine("return self = [super init];");
                    }
                    builder.EndImplementation();

                    headers.WriteLine();

                    if (members_with_default_values.Contains(ctor.Constructor))
                    {
                        default_init |= GenerateDefaultValuesWrappers(name, ctor.Constructor);
                    }
                }
            }

            // generate an `init` for a value type (even if none was defined, the default one is usable)
            if (!default_init && t.IsValueType)
            {
                var builder = new MethodHelper(headers, implementation)
                {
                    AssemblySafeName = aname,
                    ReturnType       = "nullable instancetype",
                    ManagedTypeName  = t.FullName,
                    MonoSignature    = ".ctor()",
                    ObjCSignature    = "init",
                    ObjCTypeName     = managed_name,
                    IsConstructor    = true,
                    IsValueType      = t.IsValueType,
                    IgnoreException  = true,
                };

                builder.WriteHeaders();
                builder.BeginImplementation();
                // no call to `WriteMethodLookup` since there is not such method if we reached this case

                implementation.WriteLine("if (!_object) {");
                implementation.Indent++;
                implementation.WriteLine($"MonoObject* __instance = mono_object_new (__mono_context.domain, {managed_name}_class);");
                // no call to `WriteInvoke` since there is not such method if we reached this case
                implementation.WriteLine("_object = mono_embeddinator_create_object (__instance);");
                implementation.Indent--;
                implementation.WriteLine("}");
                if (types.Contains(t.BaseType))
                {
                    implementation.WriteLine("return self = [super initForSuper];");
                }
                else
                {
                    implementation.WriteLine("return self = [super init];");
                }
                builder.EndImplementation();

                headers.WriteLine();
                default_init = true;
            }

            if (!default_init || static_type)
            {
                tbuilder.DefineNoDefaultInit();
            }

            List <ProcessedProperty> props;

            if (properties.TryGetValue(t, out props))
            {
                headers.WriteLine();
                foreach (var pi in props)
                {
                    Generate(pi);
                }
            }

            List <ProcessedFieldInfo> f;

            if (fields.TryGetValue(t, out f))
            {
                headers.WriteLine();
                foreach (var fi in f)
                {
                    Generate(fi);
                }
            }

            List <ProcessedProperty> s;

            if (subscriptProperties.TryGetValue(t, out s))
            {
                headers.WriteLine();
                foreach (var si in s)
                {
                    GenerateSubscript(si);
                }
            }

            List <ProcessedMethod> meths;

            if (methods.TryGetValue(t, out meths))
            {
                headers.WriteLine();
                foreach (var mi in meths)
                {
                    Generate(mi);
                }
            }

            MethodInfo m;

            if (icomparable.TryGetValue(t, out m))
            {
                var pt      = m.GetParameters() [0].ParameterType;
                var builder = new ComparableHelper(headers, implementation)
                {
                    ObjCSignature    = $"compare:({managed_name} * _Nullable)other",
                    AssemblySafeName = aname,
                    MetadataToken    = m.MetadataToken,
                    ObjCTypeName     = managed_name,
                    ManagedTypeName  = t.FullName,
                    MonoSignature    = $"CompareTo({NameGenerator.GetMonoName (pt)})",
                };
                builder.WriteHeaders();
                builder.WriteImplementation();
            }

            if (equals.TryGetValue(t, out m))
            {
                var builder = new EqualsHelper(headers, implementation)
                {
                    AssemblySafeName = aname,
                    MetadataToken    = m.MetadataToken,
                    ObjCTypeName     = managed_name,
                    ManagedTypeName  = t.FullName,
                };

                builder.WriteHeaders();
                builder.WriteImplementation();
            }

            if (hashes.TryGetValue(t, out m))
            {
                var builder = new HashHelper(headers, implementation)
                {
                    AssemblySafeName = aname,
                    MetadataToken    = m.MetadataToken,
                    ObjCTypeName     = managed_name,
                    ManagedTypeName  = t.FullName,
                };

                builder.WriteHeaders();
                builder.WriteImplementation();
            }

            tbuilder.EndHeaders();
            tbuilder.EndImplementation();
        }