void CSModuleWriter::GenerateManagedModuleClass(String& sourceOut)
{
    Indent();

    String source;
    String line = ToString("public static partial class %sModule\n", module_->GetName().CString());

    source += IndentLine(line);

    source += IndentLine("{\n");

    Indent();

    source += IndentLine("public static void Initialize()\n");

    source += IndentLine("{\n");

    Indent();

    Vector<SharedPtr<JSBClass>> classes = module_->classes_.Values();

    for (unsigned i = 0; i < classes.Size(); i++)
    {
        JSBClass* klass = classes.At(i);
        JSBPackage* package = module_->GetPackage();

        if (klass->IsNumberArray() || klass->IsAbstract())
            continue;

        line = ToString("NativeCore.RegisterNativeType(typeof(%s));\n", klass->GetName().CString());

        source += IndentLine(line);

        line = ToString("NativeCore.nativeClassIDToManagedConstructor [ %s.csb_%s_%s_GetClassIDStatic ()] = (IntPtr x) => {\n",
                        klass->GetName().CString(), package->GetName().CString(), klass->GetName().CString());

        source += IndentLine(line);

        Indent();

        source += IndentLine(ToString("return new %s (x);\n", klass->GetName().CString()));

        Dedent();

        source += IndentLine("};\n");

    }

    Dedent();

    source += IndentLine("}\n");

    Dedent();

    source += IndentLine("}\n");

    sourceOut += source;

    Dedent();
}
String CSTypeHelper::GetNativeFunctionSignature(JSBFunction* function, String& returnType)
{

    if (function->Skip())
        return String::EMPTY;

    if (function->IsDestructor())
        return String::EMPTY;

    if (OmitFunction(function))
        return String::EMPTY;

    JSBClass* klass = function->GetClass();
    JSBPackage* package = klass->GetPackage();
    String fname = function->IsConstructor() ? "Constructor" : function->GetName();

    returnType = "void";

    if (function->IsConstructor())
    {
        returnType = "RefCounted*";
    }
    else if (function->GetReturnType())
    {
        if (function->IsConstructor())
        {
            returnType = ToString("%s*", klass->GetNativeName().CString());
        }
        else if (function->GetReturnClass())
        {
            if (!function->GetReturnClass()->IsNumberArray())
            {
                returnType = ToString("const %s*", function->GetReturnClass()->GetNativeName().CString());
            }
        }
        else if (function->GetReturnType()->type_->asStringHashType())
        {
            returnType = "unsigned";
        }
        else
        {
            returnType = ToString("%s", CSTypeHelper::GetNativeTypeString(function->GetReturnType()).CString());

            // ScriptVector is handled by a out parameter
            if (returnType.Contains("ScriptVector"))
                returnType = "void";
        }
    }


    String sig;
    GenNativeFunctionParameterSignature(function, sig);

    String functionSig = ToString("csb_%s_%s_%s_%u(%s)",
                package->GetName().CString(), klass->GetName().CString(),
                fname.CString(), function->GetID(), sig.CString());

    return functionSig;
}
void CSFunctionWriter::WriteManagedConstructor(String& source)
{
    JSBClass* klass = function_->GetClass();
    JSBPackage* package = klass->GetPackage();

    if (klass->GetName() == "RefCounted")
        return;

    // wrapping constructor

    String line;

    line = ToString("public %s (IntPtr native) : base (native)\n", klass->GetName().CString());
    source += IndentLine(line);
    source += IndentLine("{\n");
    source += IndentLine("}\n\n");

    String sig;
    GenManagedFunctionParameters(sig);

    line = ToString("public %s (%s)\n", klass->GetName().CString(), sig.CString());

    source += IndentLine(line);

    source += IndentLine("{\n");

    Indent();

    WriteDefaultStructParameters(source);

    line = ToString("if (typeof(%s) == this.GetType()", klass->GetName().CString());
    line += ToString(" || (this.GetType().BaseType == typeof(%s) && !NativeCore.GetNativeType(this.GetType())))\n", klass->GetName().CString());

    source += IndentLine(line);

    source += IndentLine("{\n");

    Indent();

    String callSig;
    GenPInvokeCallParameters(callSig);

    line = ToString("nativeInstance = NativeCore.RegisterNative (csb_%s_%s_Constructor(%s), this);\n",
                     package->GetName().CString(), klass->GetName().CString(), callSig.CString());

    source += IndentLine(line);

    Dedent();

    source += IndentLine("}\n");

    Dedent();

    source += IndentLine("}\n");
}
void CSClassWriter::GenerateNativeSource(String& sourceOut)
{
    String source = "";

    if (klass_->IsNumberArray())
        return;

    JSBPackage* package = klass_->GetPackage();

    if (!klass_->IsInterface())
    {
        source.AppendWithFormat("ATOMIC_EXPORT_API ClassID csb_%s_%s_GetClassIDStatic()\n{\n", package->GetName().CString(), klass_->GetName().CString());
        source.AppendWithFormat("   return %s::GetClassIDStatic();\n}\n\n", klass_->GetNativeName().CString());
    }

    WriteNativeFunctions(source);

    sourceOut += source;
}
void CSClassWriter::GenerateManagedSource(String& sourceOut)
{
    String source = "";

    if (klass_->IsNumberArray())
        return;

    Indent();

    source += "\n";
    String line;

    if (klass_->GetDocString().Length())
    {
        // monodocer -assembly:NETCore.dll -path:en -pretty
        // mdoc export-html -o htmldocs en
        source += IndentLine("/// <summary>\n");
        if (klass_->GetDocString().Contains('\n'))
            source += IndentLine("/* " + klass_->GetDocString() + "*/\n");
        else
            source += IndentLine("/// " + klass_->GetDocString() + "\n");

        source += IndentLine("/// </summary>\n");
    }

    if (klass_->GetBaseClass())
    {

        String baseString = klass_->GetBaseClass()->GetName();

        const PODVector<JSBClass*>& interfaces = klass_->GetInterfaces();

        if (interfaces.Size())
        {
            StringVector baseStrings;
            baseStrings.Push(baseString);
            for (unsigned i = 0; i < interfaces.Size(); i++)
            {
                baseStrings.Push(interfaces.At(i)->GetName());
            }

            baseString = String::Joined(baseStrings, ",");
        }

        line = ToString("public partial class %s%s : %s\n", klass_->GetName().CString(), klass_->IsGeneric() ? "<T>" : "", baseString.CString());
    }
    else
    {
        String classString = "class";

        if (klass_->IsInterface())
            classString = "interface";

        line = ToString("public partial %s %s%s\n", classString.CString(), klass_->GetName().CString(), klass_->IsGeneric() ? "<T>" : "");
    }


    source += IndentLine(line);
    source += IndentLine("{\n");

    Indent();

    WriteManagedProperties(source);

    JSBPackage* package = klass_->GetPackage();

    // CoreCLR has pinvoke security demand code commented out, so we do not (currently) need this optimization:
    // https://github.com/dotnet/coreclr/issues/1605
    // line = "[SuppressUnmanagedCodeSecurity]\n";
    // source += IndentLine(line);

    if (!klass_->IsInterface())
    {
        line = "[DllImport (Constants.LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]\n";
        source += IndentLine(line);
        line = ToString("public static extern IntPtr csb_%s_%s_GetClassIDStatic();\n", package->GetName().CString(), klass_->GetName().CString());
        source += IndentLine(line);
        source += "\n";
    }

    Dedent();

    // managed functions

    CSFunctionWriter::SetWroteConstructor(false);

    for (unsigned i = 0; i < klass_->functions_.Size(); i++)
    {
        JSBFunction* function = klass_->functions_.At(i);

        if (function->Skip())
            continue;

        if (klass_->IsInterface() && function->IsConstructor())
            continue;

        if (function->IsDestructor())
            continue;

        if (CSTypeHelper::OmitFunction(function))
            continue;

        CSFunctionWriter fwriter(function);
        fwriter.GenerateManagedSource(source);

    }

    // There are some constructors being skipped (like HTTPRequest as it uses a vector of strings in args)
    // Make sure we have at least a IntPtr version
    if (!klass_->IsInterface() && !CSFunctionWriter::GetWroteConstructor() && klass_->GetName() != "RefCounted")
    {
        ATOMIC_LOGINFOF("WARNING: %s class didn't write a constructor, filling in generated native constructor", klass_->GetName().CString());

        line = ToString("public %s (IntPtr native) : base (native)\n", klass_->GetName().CString());
        source += IndentLine(line);
        source += IndentLine("{\n");
        source += IndentLine("}\n\n");
    }

    CSFunctionWriter::SetWroteConstructor(false);

    source += IndentLine("}\n");

    Dedent();

    sourceOut += source;
}
void CSFunctionWriter::WriteManagedPInvokeFunctionSignature(String& source)
{
    source += "\n";

    // CoreCLR has pinvoke security demand code commented out, so we do not (currently) need this optimization:
    // https://github.com/dotnet/coreclr/issues/1605
    // line = "[SuppressUnmanagedCodeSecurity]\n";
    // source += IndentLine(line);

    String line = "[DllImport (Constants.LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]\n";
    source += IndentLine(line);
    JSBClass* klass = function_->GetClass();
    JSBPackage* package = klass->GetPackage();

    String returnType = CSTypeHelper::GetPInvokeTypeString(function_->GetReturnType());

    if (returnType == "string")
        returnType = "IntPtr";

    if (function_->IsConstructor())
        returnType = "IntPtr";

    Vector<JSBFunctionType*>& parameters = function_->GetParameters();

    Vector<String> args;

    if (!function_->IsConstructor() && !function_->IsStatic())
    {
        args.Push("IntPtr self");
    }

    if (parameters.Size())
    {
        for (unsigned int i = 0; i < parameters.Size(); i++)
        {
            JSBFunctionType* ptype = parameters.At(i);

            String name = ptype->name_;

            if (name == "object")
                name = "_object";
            else if (name == "readonly")
                name = "readOnly";
            else if (name == "params")
                name = "parameters";

            // ignore "Context" parameters
            if (ptype->type_->asClassType())
            {
                JSBClassType* classType = ptype->type_->asClassType();
                JSBClass* klass = classType->class_;
                if (klass->GetName() == "Context")
                {
                    continue;
                }

                if (klass->IsNumberArray())
                {
                    args.Push("ref " + klass->GetName() + " " + name);
                }
                else
                {
                    args.Push("IntPtr " + name);
                }
            }
            else
            {
                args.Push(CSTypeHelper::GetPInvokeTypeString(ptype) + " " + name);
            }

        }
    }

    if (function_->GetReturnClass())
    {
        JSBClass* retClass = function_->GetReturnClass();
        if (retClass->IsNumberArray())
        {
            args.Push("ref " + retClass->GetName() + " retValue");
        }

    }

    String pstring;
    pstring.Join(args, ", ");

    String fname = function_->IsConstructor() ? "Constructor" : function_->GetName();
    line = ToString("private static extern %s csb_%s_%s_%s(%s);\n",
                    returnType.CString(), package->GetName().CString(), klass->GetName().CString(),
                    fname.CString(), pstring.CString());

    source += IndentLine(line);

    source += "\n";

}
void CSFunctionWriter::WriteManagedConstructor(String& source)
{
    JSBClass* klass = function_->GetClass();
    JSBPackage* package = klass->GetPackage();

    if (klass->GetName() == "RefCounted")
        return;

    // wrapping constructor

    String line;

    if (!wroteConstructor_)
    {
        line = ToString("public %s (IntPtr native) : base (native)\n", klass->GetName().CString());
        source += IndentLine(line);
        source += IndentLine("{\n");
        source += IndentLine("}\n\n");
    }

    // don't add wrapping constructor for overloads
    wroteConstructor_ = true;

    String sig;
    GenManagedFunctionParameters(sig);

    line = ToString("public %s (%s)\n", klass->GetName().CString(), sig.CString());

    source += IndentLine(line);

    source += IndentLine("{\n");

    Indent();

    WriteDefaultStructParameters(source);

    source += IndentLine("if (nativeInstance == IntPtr.Zero)\n");
    source += IndentLine("{\n");

    Indent();

    source += IndentLine(ToString("var classType = typeof(%s);\n", klass->GetName().CString()));
    source += IndentLine("var thisType = this.GetType();\n");
    source += IndentLine("var thisTypeIsNative = NativeCore.IsNativeType(thisType);\n");    
    source += IndentLine("var nativeAncsestorType = NativeCore.GetNativeAncestorType(thisType);\n");
    source += IndentLine("if ( (thisTypeIsNative && (thisType == classType)) || (!thisTypeIsNative && (nativeAncsestorType == classType)))\n");
    source += IndentLine("{\n");

    Indent();

    String callSig;
    GenPInvokeCallParameters(callSig);

    line = ToString("nativeInstance = NativeCore.RegisterNative (csb_%s_%s_Constructor_%u(%s), this);\n",
                     package->GetName().CString(), klass->GetName().CString(), function_->GetID(), callSig.CString());

    source += IndentLine(line);

    Dedent();

    source += IndentLine("}\n");

    Dedent();

    source += IndentLine("}\n");

    Dedent();

    source += IndentLine("}\n");
}
void CSFunctionWriter::WriteManagedPInvokeFunctionSignature(String& source)
{
    JSBClass* klass = function_->GetClass();
    JSBPackage* package = klass->GetPackage();

    if (klass->IsInterface())
        return;

    source += "\n";

    // CoreCLR has pinvoke security demand code commented out, so we do not (currently) need this optimization:
    // https://github.com/dotnet/coreclr/issues/1605
    // line = "[SuppressUnmanagedCodeSecurity]\n";
    // source += IndentLine(line);

    String line = "[DllImport (Constants.LIBNAME, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]\n";
    source += IndentLine(line);

    String returnType = CSTypeHelper::GetPInvokeTypeString(function_->GetReturnType());

    // handled by out parameter
    if (function_->GetReturnType() && function_->GetReturnType()->type_->asVectorType())
        returnType = "void";

    if (returnType == "bool")
    {
        // default boolean marshal is 4 byte windows type BOOL and not 1 byte bool
        // https://blogs.msdn.microsoft.com/jaredpar/2008/10/14/pinvoke-and-bool-or-should-i-say-bool/        
        source += IndentLine("[return: MarshalAs(UnmanagedType.I1)]\n");
    }

    if (returnType == "string")
        returnType = "IntPtr";

    if (function_->IsConstructor())
        returnType = "IntPtr";

    const Vector<JSBFunctionType*>& parameters = function_->GetParameters();

    Vector<String> args;

    if (!function_->IsConstructor() && !function_->IsStatic())
    {
        args.Push("IntPtr self");
    }

    if (parameters.Size())
    {
        for (unsigned int i = 0; i < parameters.Size(); i++)
        {
            JSBFunctionType* ptype = parameters.At(i);

            String name = ptype->name_;

            if (name == "object")
                name = "_object";
            else if (name == "readonly")
                name = "readOnly";
            else if (name == "params")
                name = "parameters";

            // ignore "Context" parameters
            if (ptype->type_->asClassType())
            {
                JSBClassType* classType = ptype->type_->asClassType();
                JSBClass* klass = classType->class_;
                if (klass->GetName() == "Context")
                {
                    continue;
                }

                if (klass->IsNumberArray())
                {
                    args.Push("ref " + klass->GetName() + " " + name);
                }
                else
                {
                    args.Push("IntPtr " + name);
                }
            }
            else
            {
                args.Push(CSTypeHelper::GetPInvokeTypeString(ptype) + " " + name);
            }

        }
    }

    if (function_->GetReturnClass())
    {
        JSBClass* retClass = function_->GetReturnClass();

        if (retClass->IsNumberArray())
        {
            args.Push("ref " + retClass->GetName() + " retValue");
        }

    }
    else if (function_->GetReturnType() && function_->GetReturnType()->type_->asVectorType())
    {
        args.Push("IntPtr returnValue");
    }

    String pstring;
    pstring.Join(args, ", ");

    String fname = function_->IsConstructor() ? "Constructor" : function_->GetName();
    line = ToString("private static extern %s csb_%s_%s_%s_%u(%s);\n",
                    returnType.CString(), package->GetName().CString(), klass->GetName().CString(),
                    fname.CString(), function_->GetID(), pstring.CString());

    source += IndentLine(line);

    source += "\n";

}