private void GeneratePayloadCode(
            Type type, CodeWriter.CodeWriter w,
            List<Tuple<MethodInfo, Tuple<string, string>>> method2PayloadTypeNames)
        {
            var tagName = Utility.GetActorInterfaceTagName(type);

            w._($"[PayloadTable(typeof({type.GetSymbolDisplay(typeless: true)}), PayloadTableKind.Request)]");
            using (w.B($"public static class {Utility.GetPayloadTableClassName(type)}{type.GetGenericParameters()}{type.GetGenericConstraintClause()}"))
            {
                // generate GetPayloadTypes method

                using (w.B("public static Type[,] GetPayloadTypes()"))
                {
                    using (w.i("return new Type[,] {", "};"))
                    {
                        foreach (var m in method2PayloadTypeNames)
                        {
                            var genericParameters = m.Item1.GetGenericParameters(typeless: true);
                            var payloadTypes = m.Item2;
                            var returnType = payloadTypes.Item2 != "" ? $"typeof({payloadTypes.Item2}{genericParameters})" : "null";
                            w._($"{{ typeof({payloadTypes.Item1}{genericParameters}), {returnType} }},");
                        }
                    }
                }

                // generate payload classes for all methods

                foreach (var m in method2PayloadTypeNames)
                {
                    var method = m.Item1;
                    var payloadTypes = m.Item2;
                    var returnType = method.ReturnType.GenericTypeArguments.FirstOrDefault();
                    var observerParameters = method.GetParameters()
                        .Select(p => Tuple.Create(p, Utility.GetReachableMemebers(p.ParameterType, Utility.IsObserverInterface).ToArray()))
                        .Where(i => i.Item2.Length > 0)
                        .ToArray();

                    // Invoke payload

                    if (Options.UseProtobuf)
                        w._("[ProtoContract, TypeAlias]");

                    var tagOverridable = tagName != null ? ", IPayloadTagOverridable" : "";
                    var observerUpdatable = observerParameters.Any() ? ", IPayloadObserverUpdatable" : "";
                    using (w.B($"public class {payloadTypes.Item1}{method.GetGenericParameters()}",
                               $": IInterfacedPayload, IAsyncInvokable{tagOverridable}{observerUpdatable}{method.GetGenericConstraintClause()}"))
                    {
                        // Parameters

                        var parameters = method.GetParameters();
                        for (var i = 0; i < parameters.Length; i++)
                        {
                            var parameter = parameters[i];

                            var attr = "";
                            var defaultValueExpression = "";
                            if (Options.UseProtobuf)
                            {
                                var defaultValueAttr =
                                    parameter.HasNonTrivialDefaultValue()
                                        ? $", DefaultValue({parameter.DefaultValue.GetValueLiteral()})"
                                        : "";
                                attr = $"[ProtoMember({i + 1}){defaultValueAttr}] ";

                                if (parameter.HasNonTrivialDefaultValue())
                                {
                                    defaultValueExpression = " = " + parameter.DefaultValue.GetValueLiteral();
                                }
                            }

                            var typeName = parameter.ParameterType.GetSymbolDisplay(true);
                            w._($"{attr}public {typeName} {parameter.Name}{defaultValueExpression};");
                        }
                        if (parameters.Any())
                            w._();

                        // GetInterfaceType

                        using (w.B($"public Type GetInterfaceType()"))
                        {
                            w._($"return typeof({type.GetSymbolDisplay()});");
                        }

                        // InvokeAsync

                        if (Options.UseSlimClient)
                        {
                            using (w.B("public Task<IValueGetable> InvokeAsync(object __target)"))
                            {
                                w._("return null;");
                            }
                        }
                        else
                        {
                            using (w.B("public async Task<IValueGetable> InvokeAsync(object __target)"))
                            {
                                var parameterNames = string.Join(", ", method.GetParameters().Select(p => p.Name));
                                if (returnType != null)
                                {
                                    w._($"var __v = await (({type.GetSymbolDisplay()})__target).{method.Name}{method.GetGenericParameters()}({parameterNames});",
                                        $"return (IValueGetable)(new {payloadTypes.Item2}{method.GetGenericParameters()} {{ v = __v }});");
                                }
                                else
                                {
                                    w._($"await (({type.GetSymbolDisplay()})__target).{method.Name}{method.GetGenericParameters()}({parameterNames});",
                                        $"return null;");
                                }
                            }
                        }

                        // IPayloadTagOverridable.SetTag

                        if (tagName != null)
                        {
                            using (w.B($"void IPayloadTagOverridable.SetTag(object value)"))
                            {
                                var tagParameter = parameters.FirstOrDefault(pi => pi.Name == tagName);
                                if (tagParameter != null)
                                {
                                    var typeName = tagParameter.ParameterType.GetSymbolDisplay(true);
                                    w._($"{tagName} = ({typeName})value;");
                                }
                            }
                        }

                        // IPayloadObserverUpdatable.Update

                        if (observerParameters.Any())
                        {
                            using (w.B("void IPayloadObserverUpdatable.Update(Action<IInterfacedObserver> updater)"))
                            {
                                foreach (var p in observerParameters)
                                {
                                    using (w.b($"if ({p.Item1.Name} != null)"))
                                    {
                                        foreach (var o in p.Item2)
                                        {
                                            if (o == "")
                                                w._($"updater({p.Item1.Name});");
                                            else
                                                w._($"if ({p.Item1.Name}.{o} != null) updater({p.Item1.Name}.{o});");
                                        }
                                    }
                                }
                            }
                        }
                    }

                    // Return payload

                    if (returnType != null)
                    {
                        var actorRefs = Utility.GetReachableMemebers(returnType, Utility.IsActorInterface).ToArray();

                        if (Options.UseProtobuf)
                            w._("[ProtoContract, TypeAlias]");

                        var actorRefUpdatable = actorRefs.Any() ? ", IPayloadActorRefUpdatable" : "";
                        using (w.B($"public class {payloadTypes.Item2}{method.GetGenericParameters()}",
                                   $": IInterfacedPayload, IValueGetable{actorRefUpdatable}{method.GetGenericConstraintClause()}"))
                        {
                            var attr = (Options.UseProtobuf) ? "[ProtoMember(1)] " : "";
                            w._($"{attr}public {returnType.GetSymbolDisplay(true)} v;");
                            w._();

                            // GetInterfaceType

                            using (w.B("public Type GetInterfaceType()"))
                            {
                                w._($"return typeof({type.GetSymbolDisplay()});");
                            }

                            // IValueGetable.Value

                            using (w.B("public object Value"))
                            {
                                w._($"get {{ return v; }}");
                            }

                            // IPayloadActorRefUpdatable.Update

                            if (actorRefs.Any())
                            {
                                using (w.B("void IPayloadActorRefUpdatable.Update(Action<object> updater)"))
                                {
                                    using (w.b($"if (v != null)"))
                                    {
                                        foreach (var r in actorRefs)
                                        {
                                            if (r == "")
                                                w._($"updater(v); ");
                                            else
                                                w._($"if (v.{r} != null) updater(v.{r});");
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
        private void GenerateSyncCode(
            Type type, CodeWriter.CodeWriter w, Type[] baseTypes,
            Tuple<Type, List<Tuple<MethodInfo, Tuple<string, string>>>>[] typeInfos)
        {
            // NoReply Interface

            var baseSynces = baseTypes.Select(t => Utility.GetActorSyncInterfaceName(t));
            var baseSyncesInherit = baseSynces.Any() ? string.Join(", ", baseSynces) : "IInterfacedActorSync";
            w._($"[AlternativeInterface(typeof({type.GetSymbolDisplay(typeless: true)}))]");
            using (w.B($"public interface {Utility.GetActorSyncInterfaceName(type)}{type.GetGenericParameters()} : {baseSyncesInherit}{type.GetGenericConstraintClause()}"))
            {
                foreach (var m in typeInfos.First().Item2)
                {
                    var method = m.Item1;
                    var parameters = method.GetParameters();
                    var paramStr = string.Join(", ", parameters.Select(p => p.GetParameterDeclaration(true)));
                    var returnType = method.ReturnType.GenericTypeArguments.FirstOrDefault();
                    var returnTypeLiteral = (returnType != null) ? returnType.GetSymbolDisplay(true) : "void";
                    w._($"{returnTypeLiteral} {method.Name}{method.GetGenericParameters()}({paramStr}){method.GetGenericConstraintClause()};");
                }
            }
        }
        private void GeneratePayloadCode(
            Type type, CodeWriter.CodeWriter w,
            List<Tuple<MethodInfo, string>> method2PayloadTypeNames)
        {
            w._($"[PayloadTable(typeof({type.GetSymbolDisplay(typeless: true)}), PayloadTableKind.Notification)]");
            using (w.B($"public static class {Utility.GetPayloadTableClassName(type)}{type.GetGenericParameters()}{type.GetGenericConstraintClause()}"))
            {
                // generate GetPayloadTypes method

                using (w.B("public static Type[] GetPayloadTypes()"))
                {
                    using (w.i("return new Type[] {", "};"))
                    {
                        foreach (var m in method2PayloadTypeNames)
                        {
                            var genericParameters = m.Item1.GetGenericParameters(typeless: true);
                            w._($"typeof({m.Item2}{genericParameters}),");
                        }
                    }
                }

                // generate payload classes for all methods

                foreach (var m in method2PayloadTypeNames)
                {
                    var method = m.Item1;
                    var payloadTypeName = m.Item2;

                    // Invoke payload

                    if (Options.UseProtobuf)
                        w._("[ProtoContract, TypeAlias]");

                    using (w.B($"public class {payloadTypeName}{method.GetGenericParameters()} : IInterfacedPayload, IInvokable{method.GetGenericConstraintClause()}"))
                    {
                        // Parameters

                        var parameters = method.GetParameters();
                        for (var i = 0; i < parameters.Length; i++)
                        {
                            var parameter = parameters[i];

                            var attr = "";
                            var defaultValueExpression = "";
                            if (Options.UseProtobuf)
                            {
                                var defaultValueAttr =
                                    parameter.HasNonTrivialDefaultValue()
                                        ? $", DefaultValue({parameter.DefaultValue.GetValueLiteral()})"
                                        : "";
                                attr = $"[ProtoMember({i + 1}){defaultValueAttr}] ";

                                if (parameter.HasNonTrivialDefaultValue())
                                {
                                    defaultValueExpression = " = " + parameter.DefaultValue.GetValueLiteral();
                                }
                            }

                            var typeName = parameter.ParameterType.GetSymbolDisplay(true);
                            w._($"{attr}public {typeName} {parameter.Name}{defaultValueExpression};");
                        }
                        if (parameters.Any())
                            w._();

                        // GetInterfaceType

                        using (w.B("public Type GetInterfaceType()"))
                        {
                            w._($"return typeof({type.GetSymbolDisplay()});");
                        }

                        // Invoke

                        using (w.B("public void Invoke(object __target)"))
                        {
                            var parameterNames = string.Join(", ", method.GetParameters().Select(p => p.Name));
                            w._($"(({type.GetSymbolDisplay()})__target).{method.Name}{method.GetGenericParameters()}({parameterNames});");
                        }
                    }
                }
            }
        }
        private void GenerateRefCode(
            Type type, CodeWriter.CodeWriter w, Type[] baseTypes,
            Tuple<Type, List<Tuple<MethodInfo, Tuple<string, string>>>>[] typeInfos)
        {
            // NoReply Interface

            var baseNoReplys = baseTypes.Select(t => Utility.GetNoReplyInterfaceName(t));
            var baseNoReplysInherit = baseNoReplys.Any() ? " : " + string.Join(", ", baseNoReplys) : "";
            using (w.B($"public interface {Utility.GetNoReplyInterfaceName(type)}{type.GetGenericParameters()}{baseNoReplysInherit}{type.GetGenericConstraintClause()}"))
            {
                foreach (var m in typeInfos.First().Item2)
                {
                    var method = m.Item1;
                    var parameters = method.GetParameters();
                    var paramStr = string.Join(", ", parameters.Select(p => p.GetParameterDeclaration(true)));
                    w._($"void {method.Name}{method.GetGenericParameters()}({paramStr}){method.GetGenericConstraintClause()};");
                }
            }

            // ActorRef

            var refClassName = Utility.GetActorRefClassName(type);
            var refClassGenericName = refClassName + type.GetGenericParameters();
            var noReplyInterfaceName = Utility.GetNoReplyInterfaceName(type);
            var noReplyInterfaceGenericName = noReplyInterfaceName + type.GetGenericParameters();

            using (w.B($"public class {refClassName}{type.GetGenericParameters()} : InterfacedActorRef, {type.GetSymbolDisplay()}, {noReplyInterfaceName}{type.GetGenericParameters()}{type.GetGenericConstraintClause()}"))
            {
                // InterfaceType property

                w._($"public override Type InterfaceType => typeof({type.GetSymbolDisplay()});");
                w._();

                // Constructors

                using (w.B($"public {refClassName}() : base(null)"))
                {
                }

                using (w.B($"public {refClassName}(IRequestTarget target) : base(target)"))
                {
                }

                using (w.B($"public {refClassName}(IRequestTarget target, IRequestWaiter requestWaiter, TimeSpan? timeout = null) : base(target, requestWaiter, timeout)"))
                {
                }

                // With Helpers

                using (w.B($"public {noReplyInterfaceGenericName} WithNoReply()"))
                {
                    w._("return this;");
                }

                using (w.B($"public {refClassGenericName} WithRequestWaiter(IRequestWaiter requestWaiter)"))
                {
                    w._($"return new {refClassGenericName}(Target, requestWaiter, Timeout);");
                }

                using (w.B($"public {refClassGenericName} WithTimeout(TimeSpan? timeout)"))
                {
                    w._($"return new {refClassGenericName}(Target, RequestWaiter, timeout);");
                }

                // IInterface message methods

                foreach (var t in typeInfos)
                {
                    var payloadTableClassName = Utility.GetPayloadTableClassName(t.Item1) + type.GetGenericParameters();

                    foreach (var m in t.Item2)
                    {
                        var method = m.Item1;
                        var payloadTypes = m.Item2;
                        var parameters = method.GetParameters();

                        var parameterTypeNames = string.Join(", ", parameters.Select(p => p.GetParameterDeclaration(true)));
                        var parameterInits = string.Join(", ", parameters.Select(Utility.GetParameterAssignment));
                        var returnType = method.ReturnType.GenericTypeArguments.FirstOrDefault();

                        // Request Methods

                        var returnTaskType = (returnType != null) ? $"Task<{returnType.GetSymbolDisplay(true)}>" : "Task";
                        var prototype = $"public {returnTaskType} {method.Name}{method.GetGenericParameters()}({parameterTypeNames}){method.GetGenericConstraintClause()}";
                        using (w.B(prototype))
                        {
                            using (w.i("var requestMessage = new RequestMessage {", "};"))
                            {
                                w._($"InvokePayload = new {payloadTableClassName}.{payloadTypes.Item1}{method.GetGenericParameters()} {{ {parameterInits} }}");
                            }

                            if (returnType != null)
                                w._($"return SendRequestAndReceive<{returnType.GetSymbolDisplay(true)}>(requestMessage);");
                            else
                                w._($"return SendRequestAndWait(requestMessage);");
                        }
                    }
                }

                // IInterface_NoReply message methods

                foreach (var t in typeInfos)
                {
                    var interfaceName = Utility.GetNoReplyInterfaceName(t.Item1);
                    var interfaceGenericName = interfaceName + t.Item1.GetGenericParameters();

                    var payloadTableClassName = Utility.GetPayloadTableClassName(t.Item1) + type.GetGenericParameters();

                    foreach (var m in t.Item2)
                    {
                        var method = m.Item1;
                        var payloadTypes = m.Item2;
                        var parameters = method.GetParameters();

                        var parameterTypeNames = string.Join(", ", parameters.Select(p => p.GetParameterDeclaration(false)));
                        var parameterInits = string.Join(", ", parameters.Select(Utility.GetParameterAssignment));

                        // Request Methods

                        using (w.B($"void {interfaceGenericName}.{method.Name}{method.GetGenericParameters()}({parameterTypeNames})"))
                        {
                            using (w.i("var requestMessage = new RequestMessage {", "};"))
                            {
                                w._($"InvokePayload = new {payloadTableClassName}.{payloadTypes.Item1}{method.GetGenericParameters()} {{ {parameterInits} }}");
                            }
                            w._("SendRequest(requestMessage);");
                        }
                    }
                }
            }

            // Protobuf-net specialized

            if (Options.UseProtobuf)
            {
                var surrogateClassName = Utility.GetSurrogateClassName(type);

                w._("[ProtoContract]");
                using (w.B($"public class {surrogateClassName}"))
                {
                    w._($"[ProtoMember(1)] public IRequestTarget Target;");
                    w._();

                    w._("[ProtoConverter]");
                    using (w.B($"public static {surrogateClassName} Convert({type.Name} value)"))
                    {
                        w._($"if (value == null) return null;");
                        w._($"return new {surrogateClassName} {{ Target = (({refClassName})value).Target }};");
                    }

                    w._("[ProtoConverter]");
                    using (w.B($"public static {type.Name} Convert({surrogateClassName} value)"))
                    {
                        w._($"if (value == null) return null;");
                        w._($"return new {refClassName}(value.Target);");
                    }
                }
            }
        }
        private void GenerateObserverCode(
            Type type, CodeWriter.CodeWriter w, Type[] baseTypes,
            Tuple<Type, List<Tuple<MethodInfo, string>>>[] typeInfos)
        {
            var className = Utility.GetObserverClassName(type);

            using (w.B($"public class {className}{type.GetGenericParameters()} : InterfacedObserver, {type.GetSymbolDisplay()}{type.GetGenericConstraintClause()}"))
            {
                // Constructor

                using (w.B($"public {className}()",
                           $": base(null, 0)"))
                {
                }

                // Constructor (INotificationChannel)

                using (w.B($"public {className}(INotificationChannel channel, int observerId = 0)",
                           $": base(channel, observerId)"))
                {
                }

                // Observer method messages

                foreach (var t in typeInfos)
                {
                    var payloadTableClassName = Utility.GetPayloadTableClassName(t.Item1) + type.GetGenericParameters();

                    foreach (var m in t.Item2)
                    {
                        var method = m.Item1;
                        var payloadType = m.Item2;
                        var parameters = method.GetParameters();

                        var parameterNames = string.Join(", ", parameters.Select(p => p.Name));
                        var parameterTypeNames = string.Join(", ", parameters.Select(p => (p.GetCustomAttribute<ParamArrayAttribute>() != null ? "params " : "") + p.ParameterType.GetSymbolDisplay(true) + " " + p.Name));
                        var parameterInits = string.Join(", ", parameters.Select(Utility.GetParameterAssignment));

                        // Request Methods

                        using (w.B($"public void {method.Name}{method.GetGenericParameters()}({parameterTypeNames}){method.GetGenericConstraintClause()}"))
                        {
                            w._($"var payload = new {payloadTableClassName}.{payloadType}{method.GetGenericParameters()} {{ {parameterInits} }};",
                                $"Notify(payload);");
                        }
                    }
                }
            }

            // Protobuf-net specialized

            if (Options.UseProtobuf)
            {
                var surrogateClassName = Utility.GetSurrogateClassName(type);

                w._("[ProtoContract]");
                using (w.B($"public class {surrogateClassName}"))
                {
                    w._("[ProtoMember(1)] public INotificationChannel Channel;");
                    w._("[ProtoMember(2)] public int ObserverId;");
                    w._();

                    w._("[ProtoConverter]");
                    using (w.B($"public static {surrogateClassName} Convert({type.Name} value)"))
                    {
                        w._($"if (value == null) return null;");
                        w._($"var o = ({className})value;");
                        w._($"return new {surrogateClassName} {{ Channel = o.Channel, ObserverId = o.ObserverId }};");
                    }

                    w._("[ProtoConverter]");
                    using (w.B($"public static {type.Name} Convert({surrogateClassName} value)"))
                    {
                        w._($"if (value == null) return null;");
                        w._($"return new {className}(value.Channel, value.ObserverId);");
                    }
                }
            }
        }