/* -----------------------------------------------------------------------------
 * This file is part of SWIG, which is licensed as a whole under version 3
 * (or any later version) of the GNU General Public License. Some additional
 * terms also apply to certain portions of SWIG. The full details of the SWIG
 * license and copyrights can be found in the LICENSE and COPYRIGHT files
 * included with the SWIG source code as distributed by the SWIG developers
 * and at https://www.swig.org/legal.html.
 *
 * csharp.cxx
 *
 * C# language module for SWIG.
 * ----------------------------------------------------------------------------- */

#include "swigmod.h"
#include "cparse.h"
#include <limits.h>  // for INT_MAX
#include <ctype.h>
#include <csharpdoc.h>

/* Hash type used for upcalls from C/C++ */
typedef DOH UpcallData;

class CSHARP : public Language {
  static const char *usage;
  const String *empty_string;
  const String *public_string;
  const String *protected_string;

  Hash *swig_types_hash;
  File *f_begin;
  File *f_runtime;
  File *f_runtime_h;
  File *f_header;
  File *f_wrappers;
  File *f_init;
  File *f_directors;
  File *f_directors_h;
  File *f_single_out;
  List *filenames_list;

  bool proxy_flag;                          // Flag for generating proxy classes
  bool native_function_flag;                // Flag for when wrapping a native function
  bool enum_constant_flag;                  // Flag for when wrapping an enum or constant
  bool static_flag;                         // Flag for when wrapping a static functions or member variables
  bool variable_wrapper_flag;               // Flag for when wrapping a nonstatic member variable
  bool wrapping_member_flag;                // Flag for when wrapping a member variable/enum/const
  bool global_variable_flag;                // Flag for when wrapping a global variable
  bool old_variable_names;                  // Flag for old style variable names in the intermediary class
  bool generate_property_declaration_flag;  // Flag for generating properties
  bool doxygen;                             // Flag for Doxygen generation

  String *imclass_name;        // intermediary class name
  String *module_class_name;   // module class name
  String *imclass_class_code;  // intermediary class code
  String *proxy_class_def;
  String *proxy_class_code;
  String *interface_class_code;  // if %feature("interface") was declared for a class, here goes the interface declaration
  String *module_class_code;
  String *proxy_class_name;   // proxy class name
  String *full_imclass_name;  // fully qualified intermediary class name when using nspace feature, otherwise same as imclass_name
  String *variable_name;      // Name of a variable being wrapped
  String *proxy_class_constants_code;
  String *module_class_constants_code;
  String *common_begin_code;
  String *enum_code;
  String *dllimport;                      // DllImport attribute name
  String *namespce;                       // Optional namespace name
  String *imclass_imports;                // intermediary class imports from %pragma
  String *module_imports;                 // module imports from %pragma
  String *imclass_baseclass;              // inheritance for intermediary class class from %pragma
  String *module_baseclass;               // inheritance for module class from %pragma
  String *imclass_interfaces;             // interfaces for intermediary class class from %pragma
  String *module_interfaces;              // interfaces for module class from %pragma
  String *imclass_class_modifiers;        // class modifiers for intermediary class overridden by %pragma
  String *module_class_modifiers;         // class modifiers for module class overridden by %pragma
  String *upcasts_code;                   // C++ casts for inheritance hierarchies C++ code
  String *imclass_cppcasts_code;          // C++ casts up inheritance hierarchies intermediary class code
  String *director_callback_typedefs;     // Director function pointer typedefs for callbacks
  String *director_callbacks;             // Director callback function pointer member variables
  String *director_delegate_callback;     // Director callback method that delegates are set to call
  String *director_delegate_definitions;  // Director delegates definitions in proxy class
  String *director_delegate_instances;    // Director delegates member variables in proxy class
  String *director_method_types;          // Director method types
  String *director_connect_parms;         // Director delegates parameter list for director connect call
  String *destructor_call;                // C++ destructor call if any
  String *output_file;                    // File name for single file mode. If set all generated code will be written to this file

  // Director method stuff:
  List *dmethods_seq;
  Hash *dmethods_table;
  int n_dmethods;
  int n_directors;
  int first_class_dmethod;
  int curr_class_dmethod;
  int nesting_depth;

  enum EnumFeature { SimpleEnum, TypeunsafeEnum, TypesafeEnum, ProperEnum };

public:
  /* -----------------------------------------------------------------------------
   * CSHARP()
   * ----------------------------------------------------------------------------- */

  CSHARP() :
    empty_string(NewString("")),
    public_string(NewString("public")),
    protected_string(NewString("protected")),
    swig_types_hash(NULL),
    f_begin(NULL),
    f_runtime(NULL),
    f_runtime_h(NULL),
    f_header(NULL),
    f_wrappers(NULL),
    f_init(NULL),
    f_directors(NULL),
    f_directors_h(NULL),
    f_single_out(NULL),
    filenames_list(NULL),
    proxy_flag(true),
    native_function_flag(false),
    enum_constant_flag(false),
    static_flag(false),
    variable_wrapper_flag(false),
    wrapping_member_flag(false),
    global_variable_flag(false),
    old_variable_names(false),
    generate_property_declaration_flag(false),
    doxygen(false),
    imclass_name(NULL),
    module_class_name(NULL),
    imclass_class_code(NULL),
    proxy_class_def(NULL),
    proxy_class_code(NULL),
    interface_class_code(NULL),
    module_class_code(NULL),
    proxy_class_name(NULL),
    full_imclass_name(NULL),
    variable_name(NULL),
    proxy_class_constants_code(NULL),
    module_class_constants_code(NULL),
    common_begin_code(NULL),
    enum_code(NULL),
    dllimport(NULL),
    namespce(NULL),
    imclass_imports(NULL),
    module_imports(NULL),
    imclass_baseclass(NULL),
    module_baseclass(NULL),
    imclass_interfaces(NULL),
    module_interfaces(NULL),
    imclass_class_modifiers(NULL),
    module_class_modifiers(NULL),
    upcasts_code(NULL),
    imclass_cppcasts_code(NULL),
    director_callback_typedefs(NULL),
    director_callbacks(NULL),
    director_delegate_callback(NULL),
    director_delegate_definitions(NULL),
    director_delegate_instances(NULL),
    director_method_types(NULL),
    director_connect_parms(NULL),
    destructor_call(NULL),
    output_file(NULL),
    dmethods_seq(NULL),
    dmethods_table(NULL),
    n_dmethods(0),
    n_directors(0),
    first_class_dmethod(0),
    curr_class_dmethod(0),
    nesting_depth(0) {
    /* for now, multiple inheritance in directors is disabled, this
       should be easy to implement though */
    director_multiple_inheritance = 0;
    directorLanguage();
  }

  /* -----------------------------------------------------------------------------
   * ~CSHARP()
   * ----------------------------------------------------------------------------- */

  ~CSHARP() {
    delete doxygenTranslator;
  }

  /* -----------------------------------------------------------------------------
   * getProxyName()
   *
   * Test to see if a type corresponds to something wrapped with a proxy class.
   * Return NULL if not otherwise the proxy class name, fully qualified with
   * a namespace if the nspace feature is used.
   * ----------------------------------------------------------------------------- */

  String *getProxyName(SwigType *t) {
    String *proxyname = NULL;
    if (proxy_flag) {
      Node *n = classLookup(t);
      if (n) {
        proxyname = Getattr(n, "proxyname");
        if (!proxyname) {
          String *nspace = Getattr(n, "sym:nspace");
          String *symname = Copy(Getattr(n, "sym:name"));
          if (symname && !GetFlag(n, "feature:flatnested")) {
            for (Node *outer_class = Getattr(n, "nested:outer"); outer_class; outer_class = Getattr(outer_class, "nested:outer")) {
              if (String *name = Getattr(outer_class, "sym:name")) {
                Push(symname, ".");
                Push(symname, name);
              } else
                return NULL;
            }
          }
          if (nspace) {
            if (namespce)
              proxyname = NewStringf("%s.%s.%s", namespce, nspace, symname);
            else
              proxyname = NewStringf("%s.%s", nspace, symname);
          } else {
            proxyname = Copy(symname);
          }
          Setattr(n, "proxyname", proxyname);
          Delete(proxyname);
          Delete(symname);
        }
      }
    }
    return proxyname;
  }

  /* ------------------------------------------------------------
   * main()
   * ------------------------------------------------------------ */

  virtual void main(int argc, char *argv[]) {

    SWIG_library_directory("csharp");

    int doxygen_translator_flags = 0;

    // Look for certain command line options
    for (int i = 1; i < argc; i++) {
      if (argv[i]) {
        if (strcmp(argv[i], "-dllimport") == 0) {
          if (argv[i + 1]) {
            dllimport = NewString("");
            Printf(dllimport, argv[i + 1]);
            Swig_mark_arg(i);
            Swig_mark_arg(i + 1);
            i++;
          } else {
            Swig_arg_error();
          }
        } else if (strcmp(argv[i], "-namespace") == 0) {
          if (argv[i + 1]) {
            namespce = NewString("");
            Printf(namespce, argv[i + 1]);
            if (Len(namespce) == 0) {
              Delete(namespce);
              namespce = 0;
            }
            Swig_mark_arg(i);
            Swig_mark_arg(i + 1);
            i++;
          } else {
            Swig_arg_error();
          }
        } else if ((strcmp(argv[i], "-noproxy") == 0)) {
          Swig_mark_arg(i);
          proxy_flag = false;
        } else if (strcmp(argv[i], "-oldvarnames") == 0) {
          Swig_mark_arg(i);
          old_variable_names = true;
        } else if (strcmp(argv[i], "-outfile") == 0) {
          if (argv[i + 1]) {
            output_file = NewString("");
            Printf(output_file, argv[i + 1]);
            Swig_mark_arg(i);
            Swig_mark_arg(i + 1);
            i++;
          } else {
            Swig_arg_error();
          }
        } else if (strcmp(argv[i], "-doxygen") == 0) {
          doxygen = true;
          scan_doxygen_comments = 1;
          Swig_mark_arg(i);
        } else if (strcmp(argv[i], "-debug-doxygen-translator") == 0) {
          doxygen_translator_flags |= DoxygenTranslator::debug_translator;
          Swig_mark_arg(i);
        } else if (strcmp(argv[i], "-debug-doxygen-parser") == 0) {
          doxygen_translator_flags |= DoxygenTranslator::debug_parser;
          Swig_mark_arg(i);
        } else if (strcmp(argv[i], "-help") == 0) {
          Printf(stdout, "%s", usage);
        }
      }
    }

    if (doxygen)
      doxygenTranslator = new CSharpDocConverter(doxygen_translator_flags);

    // Add a symbol to the parser for conditional compilation
    Preprocessor_define("SWIGCSHARP 1", 0);

    SWIG_config_file("csharp.swg");

    allow_overloading();
    Swig_interface_feature_enable();
  }

  /* ---------------------------------------------------------------------
   * top()
   * --------------------------------------------------------------------- */

  virtual int top(Node *n) {

    // Get any options set in the module directive
    Node *module = Getattr(n, "module");
    Node *optionsnode = Getattr(module, "options");

    if (optionsnode) {
      if (Getattr(optionsnode, "imclassname"))
        imclass_name = Copy(Getattr(optionsnode, "imclassname"));
      /* check if directors are enabled for this module.  note: this
       * is a "master" switch, without which no director code will be
       * emitted.  %feature("director") statements are also required
       * to enable directors for individual classes or methods.
       *
       * use %module(directors="1") modulename at the start of the
       * interface file to enable director generation.
       */
      if (Getattr(optionsnode, "directors")) {
        allow_directors();
      }
      if (Getattr(optionsnode, "dirprot")) {
        allow_dirprot();
      }
      allow_allprotected(GetFlag(optionsnode, "allprotected"));
      common_begin_code = Getattr(optionsnode, "csbegin");
      if (common_begin_code)
        Printf(common_begin_code, "\n");
    }

    /* Initialize all of the output files */
    String *outfile = Getattr(n, "outfile");
    String *outfile_h = Getattr(n, "outfile_h");

    if (!outfile) {
      Printf(stderr, "Unable to determine outfile\n");
      Exit(EXIT_FAILURE);
    }

    f_begin = NewFile(outfile, "w", SWIG_output_files());
    if (!f_begin) {
      FileErrorDisplay(outfile);
      Exit(EXIT_FAILURE);
    }

    if (Swig_directors_enabled()) {
      if (!outfile_h) {
        Printf(stderr, "Unable to determine outfile_h\n");
        Exit(EXIT_FAILURE);
      }
      f_runtime_h = NewFile(outfile_h, "w", SWIG_output_files());
      if (!f_runtime_h) {
        FileErrorDisplay(outfile_h);
        Exit(EXIT_FAILURE);
      }
    }

    f_runtime = NewString("");
    f_init = NewString("");
    f_header = NewString("");
    f_wrappers = NewString("");
    f_directors_h = NewString("");
    f_directors = NewString("");

    /* Register file targets with the SWIG file handler */
    Swig_register_filebyname("header", f_header);
    Swig_register_filebyname("wrapper", f_wrappers);
    Swig_register_filebyname("begin", f_begin);
    Swig_register_filebyname("runtime", f_runtime);
    Swig_register_filebyname("init", f_init);
    Swig_register_filebyname("director", f_directors);
    Swig_register_filebyname("director_h", f_directors_h);

    swig_types_hash = NewHash();
    filenames_list = NewList();

    // Make the intermediary class and module class names. The intermediary class name can be set in the module directive.
    if (!imclass_name) {
      imclass_name = NewStringf("%sPINVOKE", Getattr(n, "name"));
      module_class_name = Copy(Getattr(n, "name"));
    } else {
      // Rename the module name if it is the same as intermediary class name - a backwards compatibility solution
      if (Cmp(imclass_name, Getattr(n, "name")) == 0)
        module_class_name = NewStringf("%sModule", Getattr(n, "name"));
      else
        module_class_name = Copy(Getattr(n, "name"));
    }

    // module class and intermediary classes are always created
    if (!addSymbol(imclass_name, module))
      return SWIG_ERROR;
    if (!addSymbol(module_class_name, module))
      return SWIG_ERROR;

    imclass_class_code = NewString("");
    proxy_class_def = NewString("");
    proxy_class_code = NewString("");
    module_class_constants_code = NewString("");
    imclass_baseclass = NewString("");
    imclass_interfaces = NewString("");
    imclass_class_modifiers = NewString("");
    module_class_code = NewString("");
    module_baseclass = NewString("");
    module_interfaces = NewString("");
    module_imports = NewString("");
    module_class_modifiers = NewString("");
    imclass_imports = NewString("");
    imclass_cppcasts_code = NewString("");
    director_connect_parms = NewString("");
    upcasts_code = NewString("");
    dmethods_seq = NewList();
    dmethods_table = NewHash();
    n_dmethods = 0;
    n_directors = 0;
    if (!dllimport)
      dllimport = Copy(module_class_name);

    Swig_banner(f_begin);

    Swig_obligatory_macros(f_runtime, "CSHARP");

    if (Swig_directors_enabled()) {
      Printf(f_runtime, "#define SWIG_DIRECTORS\n");

      /* Emit initial director header and director code: */
      Swig_banner(f_directors_h);
      Printf(f_directors_h, "\n");
      Printf(f_directors_h, "#ifndef SWIG_%s_WRAP_H_\n", module_class_name);
      Printf(f_directors_h, "#define SWIG_%s_WRAP_H_\n\n", module_class_name);

      Printf(f_directors, "\n\n");
      Printf(f_directors, "/* ---------------------------------------------------\n");
      Printf(f_directors, " * C++ director class methods\n");
      Printf(f_directors, " * --------------------------------------------------- */\n\n");
      if (outfile_h) {
        String *filename = Swig_file_filename(outfile_h);
        Printf(f_directors, "#include \"%s\"\n\n", filename);
        Delete(filename);
      }
    }

    Printf(f_runtime, "\n");
    if (namespce) {
      String *wrapper_name = NewStringf("");
      Printf(wrapper_name, "CSharp_%s_%%f", namespce);
      Swig_name_register("wrapper", wrapper_name);
      Delete(wrapper_name);
    } else {
      Swig_name_register("wrapper", "CSharp_%f");
    }

    if (old_variable_names) {
      Swig_name_register("set", "set_%n%v");
      Swig_name_register("get", "get_%n%v");
    }

    Printf(f_wrappers, "\n#ifdef __cplusplus\n");
    Printf(f_wrappers, "extern \"C\" {\n");
    Printf(f_wrappers, "#endif\n\n");

    /* Emit code */
    Language::top(n);

    if (Swig_directors_enabled()) {
      // Insert director runtime into the f_runtime file (make it occur before %header section)
      Swig_insert_file("director_common.swg", f_runtime);
      Swig_insert_file("director.swg", f_runtime);
    }
    // Generate the intermediary class
    {
      File *f_im = getOutputFile(SWIG_output_directory(), imclass_name);

      addOpenNamespace(0, f_im);

      if (imclass_imports)
        Printf(f_im, "%s\n", imclass_imports);

      if (Len(imclass_class_modifiers) > 0)
        Printf(f_im, "%s ", imclass_class_modifiers);
      Printf(f_im, "%s ", imclass_name);

      if (imclass_baseclass && *Char(imclass_baseclass))
        Printf(f_im, ": %s ", imclass_baseclass);
      if (Len(imclass_interfaces) > 0)
        Printv(f_im, "implements ", imclass_interfaces, " ", NIL);
      Printf(f_im, "{\n");

      // Add the intermediary class methods
      Replaceall(imclass_class_code, "$module", module_class_name);
      Replaceall(imclass_class_code, "$imclassname", imclass_name);
      Replaceall(imclass_class_code, "$dllimport", dllimport);
      Printv(f_im, imclass_class_code, NIL);
      Printv(f_im, imclass_cppcasts_code, NIL);

      // Finish off the class
      Printf(f_im, "}\n");
      addCloseNamespace(0, f_im);

      if (f_im != f_single_out)
        Delete(f_im);
      f_im = NULL;
    }

    // Generate the C# module class
    {
      File *f_module = getOutputFile(SWIG_output_directory(), module_class_name);

      addOpenNamespace(0, f_module);

      if (module_imports)
        Printf(f_module, "%s\n", module_imports);

      if (Len(module_class_modifiers) > 0)
        Printf(f_module, "%s ", module_class_modifiers);
      Printf(f_module, "%s ", module_class_name);

      if (module_baseclass && *Char(module_baseclass))
        Printf(f_module, ": %s ", module_baseclass);
      if (Len(module_interfaces) > 0)
        Printv(f_module, "implements ", module_interfaces, " ", NIL);
      Printf(f_module, "{\n");

      Replaceall(module_class_code, "$module", module_class_name);
      Replaceall(module_class_constants_code, "$module", module_class_name);

      Replaceall(module_class_code, "$imclassname", imclass_name);
      Replaceall(module_class_constants_code, "$imclassname", imclass_name);

      Replaceall(module_class_code, "$dllimport", dllimport);
      Replaceall(module_class_constants_code, "$dllimport", dllimport);

      // Add the wrapper methods
      Printv(f_module, module_class_code, NIL);

      // Write out all the global constants
      Printv(f_module, module_class_constants_code, NIL);

      // Finish off the class
      Printf(f_module, "}\n");
      addCloseNamespace(0, f_module);

      if (f_module != f_single_out)
        Delete(f_module);
      f_module = NULL;
    }

    if (upcasts_code)
      Printv(f_wrappers, upcasts_code, NIL);

    Printf(f_wrappers, "#ifdef __cplusplus\n");
    Printf(f_wrappers, "}\n");
    Printf(f_wrappers, "#endif\n");

    // Output a C# type wrapper class for each SWIG type
    for (Iterator swig_type = First(swig_types_hash); swig_type.key; swig_type = Next(swig_type)) {
      emitTypeWrapperClass(swig_type.key, swig_type.item);
    }

    // Check for overwriting file problems on filesystems that are case insensitive
    Iterator it1;
    Iterator it2;
    for (it1 = First(filenames_list); it1.item; it1 = Next(it1)) {
      String *item1_lower = Swig_string_lower(it1.item);
      for (it2 = Next(it1); it2.item; it2 = Next(it2)) {
        String *item2_lower = Swig_string_lower(it2.item);
        if (it1.item && it2.item) {
          if (Strcmp(item1_lower, item2_lower) == 0) {
            Swig_warning(WARN_LANG_PORTABILITY_FILENAME,
                         input_file,
                         line_number,
                         "Portability warning: File %s will be overwritten by %s on case insensitive filesystems such as "
                         "Windows' FAT32 and NTFS unless the class/module name is renamed\n",
                         it1.item,
                         it2.item);
          }
        }
        Delete(item2_lower);
      }
      Delete(item1_lower);
    }

    Delete(swig_types_hash);
    swig_types_hash = NULL;
    Delete(filenames_list);
    filenames_list = NULL;
    Delete(imclass_name);
    imclass_name = NULL;
    Delete(imclass_class_code);
    imclass_class_code = NULL;
    Delete(proxy_class_def);
    proxy_class_def = NULL;
    Delete(proxy_class_code);
    proxy_class_code = NULL;
    Delete(module_class_constants_code);
    module_class_constants_code = NULL;
    Delete(imclass_baseclass);
    imclass_baseclass = NULL;
    Delete(imclass_interfaces);
    imclass_interfaces = NULL;
    Delete(imclass_class_modifiers);
    imclass_class_modifiers = NULL;
    Delete(module_class_name);
    module_class_name = NULL;
    Delete(module_class_code);
    module_class_code = NULL;
    Delete(module_baseclass);
    module_baseclass = NULL;
    Delete(module_interfaces);
    module_interfaces = NULL;
    Delete(module_imports);
    module_imports = NULL;
    Delete(module_class_modifiers);
    module_class_modifiers = NULL;
    Delete(imclass_imports);
    imclass_imports = NULL;
    Delete(imclass_cppcasts_code);
    imclass_cppcasts_code = NULL;
    Delete(upcasts_code);
    upcasts_code = NULL;
    Delete(dmethods_seq);
    dmethods_seq = NULL;
    Delete(dmethods_table);
    dmethods_table = NULL;
    Delete(namespce);
    namespce = NULL;
    n_dmethods = 0;

    /* Close all of the files */
    Dump(f_runtime, f_begin);
    Dump(f_header, f_begin);

    if (Swig_directors_enabled()) {
      Dump(f_directors, f_begin);
      Dump(f_directors_h, f_runtime_h);

      Printf(f_runtime_h, "\n");
      Printf(f_runtime_h, "#endif\n");

      Delete(f_runtime_h);
      f_runtime_h = NULL;
      Delete(f_directors);
      f_directors = NULL;
      Delete(f_directors_h);
      f_directors_h = NULL;
    }

    if (f_single_out) {
      Dump(f_single_out, f_begin);
      Delete(f_single_out);
      f_single_out = NULL;
    }

    Dump(f_wrappers, f_begin);
    Wrapper_pretty_print(f_init, f_begin);
    Delete(f_header);
    Delete(f_wrappers);
    Delete(f_init);
    Delete(f_runtime);
    Delete(f_begin);
    return SWIG_OK;
  }

  /* -----------------------------------------------------------------------------
   * emitBanner()
   * ----------------------------------------------------------------------------- */

  void emitBanner(File *f) {
    Printf(f, "//------------------------------------------------------------------------------\n");
    Printf(f, "// <auto-generated />\n");
    Printf(f, "//\n");
    Swig_banner_target_lang(f, "//");
    Printf(f, "//------------------------------------------------------------------------------\n\n");
    Printv(f, common_begin_code, NIL);
  }

  /* -----------------------------------------------------------------------------
   * getOutputFile()
   *
   * Prepares a File object by creating the file in the file system and
   * writing the banner for auto-generated files to it (emitBanner).
   * If '-outfile' is provided (single file mode) the supplied parameters will
   * be ignored and the returned file will always be:
   *  <outdir>/<outfile>
   * Otherwise the file will be:
   *  <dir>/<name>.cs
   * ----------------------------------------------------------------------------- */

  File *getOutputFile(const String *dir, const String *name) {
    if (output_file) {
      if (!f_single_out) {
        String *filen = NewStringf("%s%s", SWIG_output_directory(), output_file);
        f_single_out = NewFile(filen, "w", SWIG_output_files());
        if (!f_single_out) {
          FileErrorDisplay(filen);
          Exit(EXIT_FAILURE);
        }
        Append(filenames_list, Copy(filen));
        Delete(filen);
        filen = NULL;

        emitBanner(f_single_out);
      }
      return f_single_out;
    } else {
      String *filen = NewStringf("%s%s.cs", dir, name);
      File *f = NewFile(filen, "w", SWIG_output_files());
      if (!f) {
        FileErrorDisplay(filen);
        Exit(EXIT_FAILURE);
      }
      Append(filenames_list, Copy(filen));
      Delete(filen);
      filen = NULL;

      emitBanner(f);
      return f;
    }
  }

  /*-----------------------------------------------------------------------
   * Add new director upcall signature
   *----------------------------------------------------------------------*/

  UpcallData *addUpcallMethod(String *imclass_method, String *class_method, String *decl, String *overloaded_name) {
    String *key = NewStringf("%s|%s", imclass_method, decl);

    ++curr_class_dmethod;

    String *class_methodidx = NewStringf("%d", n_dmethods - first_class_dmethod);
    n_dmethods++;

    Hash *new_udata = NewHash();
    Append(dmethods_seq, new_udata);
    Setattr(dmethods_table, key, new_udata);

    Setattr(new_udata, "method", Copy(class_method));
    Setattr(new_udata, "class_methodidx", class_methodidx);
    Setattr(new_udata, "decl", Copy(decl));
    Setattr(new_udata, "overname", Copy(overloaded_name));

    Delete(key);
    return new_udata;
  }

  /*-----------------------------------------------------------------------
   * Get director upcall signature
   *----------------------------------------------------------------------*/

  /*
     UpcallData * getUpcallMethodData(String *director_class, String *decl) {
     String             *key = NewStringf("%s|%s", director_class, decl);
     UpcallData         *udata = Getattr(dmethods_table, key);

     Delete(key);
     return udata;
     }
   */

  /* ----------------------------------------------------------------------
   * nativeWrapper()
   * ---------------------------------------------------------------------- */

  virtual int nativeWrapper(Node *n) {
    String *wrapname = Getattr(n, "wrap:name");

    if (!addSymbol(wrapname, n, imclass_name))
      return SWIG_ERROR;

    if (Getattr(n, "type")) {
      Swig_save("nativeWrapper", n, "name", NIL);
      Setattr(n, "name", wrapname);
      native_function_flag = true;
      functionWrapper(n);
      Swig_restore(n);
      native_function_flag = false;
    } else {
      Swig_error(input_file, line_number, "No return type for %%native method %s.\n", Getattr(n, "wrap:name"));
    }

    return SWIG_OK;
  }

  /* ----------------------------------------------------------------------
   * functionWrapper()
   * ---------------------------------------------------------------------- */

  virtual int functionWrapper(Node *n) {
    String *symname = Getattr(n, "sym:name");
    SwigType *returntype = Getattr(n, "type");
    ParmList *l = Getattr(n, "parms");
    String *tm;
    Parm *p;
    int i;
    String *c_return_type = NewString("");
    String *im_return_type = NewString("");
    String *cleanup = NewString("");
    String *outarg = NewString("");
    String *body = NewString("");
    String *im_outattributes = 0;
    int num_arguments = 0;
    bool is_void_return;
    String *overloaded_name = getOverloadedName(n);

    if (!Getattr(n, "sym:overloaded")) {
      if (!addSymbol(symname, n, imclass_name))
        return SWIG_ERROR;
    }

    /*
       The rest of this function deals with generating the intermediary class wrapper function (that wraps
       a c/c++ function) and generating the PInvoke c code. Each C# wrapper function has a
       matching PInvoke c function call.
     */

    // A new wrapper function object
    Wrapper *f = NewWrapper();

    // Make a wrapper name for this function
    String *wname = Swig_name_wrapper(overloaded_name);

    /* Attach the non-standard typemaps to the parameter list. */
    Swig_typemap_attach_parms("ctype", l, f);
    Swig_typemap_attach_parms("imtype", l, f);

    /* Get return types */
    if ((tm = Swig_typemap_lookup("ctype", n, "", 0))) {
      String *ctypeout = Getattr(n, "tmap:ctype:out");  // the type in the ctype typemap's out attribute overrides the type in the typemap
      if (ctypeout)
        tm = ctypeout;
      Printf(c_return_type, "%s", tm);
    } else {
      Swig_warning(WARN_CSHARP_TYPEMAP_CTYPE_UNDEF, input_file, line_number, "No ctype typemap defined for %s\n", SwigType_str(returntype, 0));
    }

    if ((tm = Swig_typemap_lookup("imtype", n, "", 0))) {
      String *imtypeout = Getattr(n, "tmap:imtype:out");  // the type in the imtype typemap's out attribute overrides the type in the typemap
      if (imtypeout)
        tm = imtypeout;
      Printf(im_return_type, "%s", tm);
      im_outattributes = Getattr(n, "tmap:imtype:outattributes");
    } else {
      Swig_warning(WARN_CSHARP_TYPEMAP_CSTYPE_UNDEF, input_file, line_number, "No imtype typemap defined for %s\n", SwigType_str(returntype, 0));
    }

    is_void_return = Cmp(c_return_type, "void") == 0;
    if (!is_void_return)
      Wrapper_add_localv(f, "jresult", c_return_type, "jresult = $null", NIL);

    Printv(f->def, " SWIGEXPORT ", c_return_type, " SWIGSTDCALL ", wname, "(", NIL);

    // Emit all of the local variables for holding arguments.
    emit_parameter_variables(l, f);

    /* Attach the standard typemaps */
    emit_attach_parmmaps(l, f);

    // Parameter overloading
    Setattr(n, "wrap:parms", l);
    Setattr(n, "wrap:name", wname);

    // Wrappers not wanted for some methods where the parameters cannot be overloaded in C#
    if (Getattr(n, "sym:overloaded")) {
      // Emit warnings for the few cases that can't be overloaded in C# and give up on generating wrapper
      Swig_overload_check(n);
      if (Getattr(n, "overload:ignore")) {
        DelWrapper(f);
        return SWIG_OK;
      }
    }

    Printv(imclass_class_code, "\n  [global::System.Runtime.InteropServices.DllImport(\"", dllimport, "\", EntryPoint=\"", wname, "\")]\n", NIL);

    if (im_outattributes)
      Printf(imclass_class_code, "  %s\n", im_outattributes);

    Printf(imclass_class_code, "  public static extern %s %s(", im_return_type, overloaded_name);

    /* Get number of required and total arguments */
    num_arguments = emit_num_arguments(l);
    int gencomma = 0;

    // Now walk the function parameter list and generate code to get arguments
    for (i = 0, p = l; i < num_arguments; i++) {

      while (checkAttribute(p, "tmap:in:numinputs", "0")) {
        p = Getattr(p, "tmap:in:next");
      }

      SwigType *pt = Getattr(p, "type");
      String *ln = Getattr(p, "lname");
      String *im_param_type = NewString("");
      String *c_param_type = NewString("");
      String *arg = NewString("");

      Printf(arg, "j%s", ln);

      /* Get the ctype types of the parameter */
      if ((tm = Getattr(p, "tmap:ctype"))) {
        Printv(c_param_type, tm, NIL);
      } else {
        Swig_warning(WARN_CSHARP_TYPEMAP_CTYPE_UNDEF, input_file, line_number, "No ctype typemap defined for %s\n", SwigType_str(pt, 0));
      }

      /* Get the intermediary class parameter types of the parameter */
      if ((tm = Getattr(p, "tmap:imtype"))) {
        const String *inattributes = Getattr(p, "tmap:imtype:inattributes");
        Printf(im_param_type, "%s%s", inattributes ? inattributes : empty_string, tm);
      } else {
        Swig_warning(WARN_CSHARP_TYPEMAP_CSTYPE_UNDEF, input_file, line_number, "No imtype typemap defined for %s\n", SwigType_str(pt, 0));
      }

      /* Add parameter to intermediary class method */
      if (gencomma)
        Printf(imclass_class_code, ", ");
      Printf(imclass_class_code, "%s %s", im_param_type, arg);

      // Add parameter to C function
      Printv(f->def, gencomma ? ", " : "", c_param_type, " ", arg, NIL);

      gencomma = 1;

      // Get typemap for this argument
      if ((tm = Getattr(p, "tmap:in"))) {
        canThrow(n, "in", p);
        Replaceall(tm, "$arg", arg); /* deprecated? */
        Replaceall(tm, "$input", arg);
        Setattr(p, "emit:input", arg);
        Printf(f->code, "%s\n", tm);
        p = Getattr(p, "tmap:in:next");
      } else {
        Swig_warning(WARN_TYPEMAP_IN_UNDEF, input_file, line_number, "Unable to use type %s as a function argument.\n", SwigType_str(pt, 0));
        p = nextSibling(p);
      }
      Delete(im_param_type);
      Delete(c_param_type);
      Delete(arg);
    }

    /* Insert constraint checking code */
    for (p = l; p;) {
      if ((tm = Getattr(p, "tmap:check"))) {
        canThrow(n, "check", p);
        Replaceall(tm, "$arg", Getattr(p, "emit:input")); /* deprecated? */
        Replaceall(tm, "$input", Getattr(p, "emit:input"));
        Printv(f->code, tm, "\n", NIL);
        p = Getattr(p, "tmap:check:next");
      } else {
        p = nextSibling(p);
      }
    }

    /* Insert cleanup code */
    for (p = l; p;) {
      if ((tm = Getattr(p, "tmap:freearg"))) {
        canThrow(n, "freearg", p);
        Replaceall(tm, "$arg", Getattr(p, "emit:input")); /* deprecated? */
        Replaceall(tm, "$input", Getattr(p, "emit:input"));
        Printv(cleanup, tm, "\n", NIL);
        p = Getattr(p, "tmap:freearg:next");
      } else {
        p = nextSibling(p);
      }
    }

    /* Insert argument output code */
    for (p = l; p;) {
      if ((tm = Getattr(p, "tmap:argout"))) {
        canThrow(n, "argout", p);
        Replaceall(tm, "$arg", Getattr(p, "emit:input")); /* deprecated? */
        Replaceall(tm, "$result", "jresult");
        Replaceall(tm, "$input", Getattr(p, "emit:input"));
        Printv(outarg, tm, "\n", NIL);
        p = Getattr(p, "tmap:argout:next");
      } else {
        p = nextSibling(p);
      }
    }

    // Look for usage of throws typemap and the canthrow flag
    ParmList *throw_parm_list = NULL;
    if ((throw_parm_list = Getattr(n, "catchlist"))) {
      Swig_typemap_attach_parms("throws", throw_parm_list, f);
      for (p = throw_parm_list; p; p = nextSibling(p)) {
        if (Getattr(p, "tmap:throws")) {
          canThrow(n, "throws", p);
        }
      }
    }

    String *null_attribute = 0;
    // Now write code to make the function call
    if (!native_function_flag) {

      if (Swig_director_emit_dynamic_cast(n, f)) {
        /* Add protection */
        Setattr(n, "csharp:canthrow", "1");
        Append(f->code, "if (!darg) {\n");
        Append(f->code, "  SWIG_CSharpSetPendingExceptionArgument(SWIG_CSharpArgumentNullException, \"'self' is not a director\", 0);\n");
        if (is_void_return)
          Append(f->code, "  return;\n");
        else
          Append(f->code, "  return jresult;\n");
        Append(f->code, "}\n");
      }
      String *actioncode = emit_action(n);

      /* Return value if necessary  */
      if ((tm = Swig_typemap_lookup_out("out", n, Swig_cresult_name(), f, actioncode))) {
        canThrow(n, "out", n);
        Replaceall(tm, "$result", "jresult");

        if (GetFlag(n, "feature:new"))
          Replaceall(tm, "$owner", "1");
        else
          Replaceall(tm, "$owner", "0");

        Printf(f->code, "%s", tm);
        null_attribute = Getattr(n, "tmap:out:null");
        if (Len(tm))
          Printf(f->code, "\n");
      } else {
        Swig_warning(
          WARN_TYPEMAP_OUT_UNDEF, input_file, line_number, "Unable to use return type %s in function %s.\n", SwigType_str(returntype, 0), Getattr(n, "name"));
      }
      emit_return_variable(n, returntype, f);
    }

    /* Output argument output code */
    Printv(f->code, outarg, NIL);

    /* Output cleanup code */
    Printv(f->code, cleanup, NIL);

    /* Look to see if there is any newfree cleanup code */
    if (GetFlag(n, "feature:new")) {
      if ((tm = Swig_typemap_lookup("newfree", n, Swig_cresult_name(), 0))) {
        canThrow(n, "newfree", n);
        Printf(f->code, "%s\n", tm);
      }
    }

    /* See if there is any return cleanup code */
    if (!native_function_flag) {
      if ((tm = Swig_typemap_lookup("ret", n, Swig_cresult_name(), 0))) {
        canThrow(n, "ret", n);
        Printf(f->code, "%s\n", tm);
      }
    }

    /* Finish C function and intermediary class function definitions */
    Printf(imclass_class_code, ")");
    Printf(imclass_class_code, ";\n");

    Printf(f->def, ") {");

    if (!is_void_return)
      Printv(f->code, "    return jresult;\n", NIL);
    Printf(f->code, "}\n");

    /* Substitute the cleanup code */
    Replaceall(f->code, "$cleanup", cleanup);

    Replaceall(f->code, "$isvoid", is_void_return ? "1" : "0");

    /* Substitute the function name */
    Replaceall(f->code, "$symname", symname);

    /* Contract macro modification */
    if (Replaceall(f->code, "SWIG_contract_assert(", "SWIG_contract_assert($null, ") > 0) {
      Setattr(n, "csharp:canthrow", "1");
    }

    if (!null_attribute) {
      Replaceall(f->code, "$null", "0");
      Replaceall(f->locals, "$null", "0");
    } else {
      Replaceall(f->code, "$null", null_attribute);
      Replaceall(f->locals, "$null", null_attribute);
    }

    /* Dump the function out */
    if (!native_function_flag) {
      Wrapper_print(f, f_wrappers);

      // Handle %csexception which sets the canthrow attribute
      if (Getattr(n, "feature:except:canthrow"))
        Setattr(n, "csharp:canthrow", "1");

      // A very simple check (it is not foolproof) to help typemap/feature writers for
      // throwing C# exceptions from unmanaged code. It checks for the common methods which
      // set a pending C# exception... the 'canthrow' typemap/feature attribute must be set
      // so that code which checks for pending exceptions is added in the C# proxy method.
      if (!Getattr(n, "csharp:canthrow")) {
        if (Strstr(f->code, "SWIG_exception")) {
          Swig_warning(WARN_CSHARP_CANTHROW,
                       input_file,
                       line_number,
                       "Unmanaged code contains a call to SWIG_exception and C# code does not handle pending exceptions via the canthrow attribute.\n");
        } else if (Strstr(f->code, "SWIG_CSharpSetPendingException")) {
          Swig_warning(WARN_CSHARP_CANTHROW,
                       input_file,
                       line_number,
                       "Unmanaged code contains a call to a SWIG_CSharpSetPendingException method and C# code does not handle pending exceptions via the "
                       "canthrow attribute.\n");
        }
      }
    }

    if (!(proxy_flag && is_wrapping_class()) && !enum_constant_flag) {
      moduleClassFunctionHandler(n);
    }

    /*
     * Generate the proxy class properties for public member variables.
     * Not for enums and constants.
     */
    if (proxy_flag && wrapping_member_flag && !enum_constant_flag) {
      // Capitalize the first letter in the variable in the getter/setter function name
      bool getter_flag = Cmp(symname, Swig_name_set(getNSpace(), Swig_name_member(0, getClassPrefix(), variable_name))) != 0;

      String *getter_setter_name = NewString("");
      if (!getter_flag)
        Printf(getter_setter_name, "set");
      else
        Printf(getter_setter_name, "get");
      Putc(toupper((int)*Char(variable_name)), getter_setter_name);
      Printf(getter_setter_name, "%s", Char(variable_name) + 1);

      Setattr(n, "proxyfuncname", getter_setter_name);
      Setattr(n, "imfuncname", symname);

      proxyClassFunctionHandler(n);
      Delete(getter_setter_name);
    }

    Delete(c_return_type);
    Delete(im_return_type);
    Delete(cleanup);
    Delete(outarg);
    Delete(body);
    Delete(overloaded_name);
    DelWrapper(f);
    return SWIG_OK;
  }

  /* -----------------------------------------------------------------------
   * variableWrapper()
   * ----------------------------------------------------------------------- */

  virtual int variableWrapper(Node *n) {
    Language::variableWrapper(n);
    return SWIG_OK;
  }

  /* -----------------------------------------------------------------------
   * globalvariableHandler()
   * ------------------------------------------------------------------------ */

  virtual int globalvariableHandler(Node *n) {

    generate_property_declaration_flag = true;
    variable_name = Getattr(n, "sym:name");
    global_variable_flag = true;
    int ret = Language::globalvariableHandler(n);
    global_variable_flag = false;
    generate_property_declaration_flag = false;

    if (proxy_flag) {
      Printf(module_class_code, "\n  }\n\n");
    }

    return ret;
  }

  String *getCurrentScopeName(String *nspace) {
    String *scope = 0;
    if (nspace || getCurrentClass()) {
      scope = NewString("");
      if (nspace)
        Printf(scope, "%s", nspace);
      if (Node *cls = getCurrentClass()) {
        if (Node *outer = Getattr(cls, "nested:outer")) {
          String *outerClassesPrefix = Copy(Getattr(outer, "sym:name"));
          for (outer = Getattr(outer, "nested:outer"); outer != 0; outer = Getattr(outer, "nested:outer")) {
            Push(outerClassesPrefix, ".");
            Push(outerClassesPrefix, Getattr(outer, "sym:name"));
          }
          Printv(scope, nspace ? "." : "", outerClassesPrefix, ".", proxy_class_name, NIL);
          Delete(outerClassesPrefix);
        } else
          Printv(scope, nspace ? "." : "", proxy_class_name, NIL);
      }
    }
    return scope;
  }

  /* ----------------------------------------------------------------------
   * enumDeclaration()
   *
   * C/C++ enums can be mapped in one of 4 ways, depending on the cs:enum feature specified:
   * 1) Simple enums - simple constant within the proxy class or module class
   * 2) Typeunsafe enums - simple constant in a C# class (class named after the c++ enum name)
   * 3) Typesafe enum - typesafe enum pattern (class named after the c++ enum name)
   * 4) Proper enums - proper C# enum
   * Anonymous enums always default to 1)
   * ---------------------------------------------------------------------- */

  virtual int enumDeclaration(Node *n) {

    if (!ImportMode) {
      if (getCurrentClass() && (cplus_mode != PUBLIC))
        return SWIG_NOWRAP;

      String *nspace = Getattr(n, "sym:nspace");  // NSpace/getNSpace() only works during Language::enumDeclaration call
      enum_code = NewString("");
      String *symname = Getattr(n, "sym:name");
      String *constants_code = (proxy_flag && is_wrapping_class()) ? proxy_class_constants_code : module_class_constants_code;
      EnumFeature enum_feature = decodeEnumFeature(n);
      String *typemap_lookup_type = Getattr(n, "name");

      // Translate documentation comments
      if (have_docstring(n)) {
        String *ds = docstring(n, "");
        Printv(enum_code, ds, NIL);
        Delete(ds);
      }

      if ((enum_feature != SimpleEnum) && symname && typemap_lookup_type) {
        // Wrap (non-anonymous) C/C++ enum within a typesafe, typeunsafe or proper C# enum

        String *scope = getCurrentScopeName(nspace);
        if (!addSymbol(symname, n, scope))
          return SWIG_ERROR;

        // Enum base (underlying enum type)
        Node *attributes = NewHash();
        const String *pure_baseclass = typemapLookup(n, "csbase", typemap_lookup_type, WARN_NONE, attributes);
        bool purebase_replace = GetFlag(attributes, "tmap:csbase:replace") ? true : false;
        Delete(attributes);

        String *baseclass = NULL;
        if (!purebase_replace) {
          String *underlying_enum_type = Getattr(n, "enumbase");
          if (underlying_enum_type) {
            const String *typemap_baseclass = typemapLookup(n, "cstype", underlying_enum_type, WARN_CSHARP_TYPEMAP_CSWTYPE_UNDEF);
            baseclass = Copy(typemap_baseclass);
            substituteClassname(underlying_enum_type, baseclass);
          }
        }

        const String *wanted_base = baseclass ? baseclass : pure_baseclass;

        if (purebase_replace) {
          wanted_base = pure_baseclass;
        } else if (Len(pure_baseclass) > 0 && Len(baseclass) > 0) {
          Swig_warning(WARN_CSHARP_MULTIPLE_INHERITANCE,
                       Getfile(n),
                       Getline(n),
                       "Warning for %s, enum base %s ignored. Multiple enum bases is not supported in C# enums. "
                       "Perhaps you need the 'replace' attribute in the csbase typemap?\n",
                       typemap_lookup_type,
                       pure_baseclass);
        }

        // Class attributes
        const String *csattributes = typemapLookup(n, "csattributes", typemap_lookup_type, WARN_NONE);
        if (csattributes && *Char(csattributes))
          Printf(enum_code, "%s\n", csattributes);

        // Emit the enum
        Printv(enum_code,
               typemapLookup(n, "csclassmodifiers", typemap_lookup_type, WARN_CSHARP_TYPEMAP_CLASSMOD_UNDEF),  // Class modifiers (enum modifiers really)
               " ",
               symname,
               *Char(wanted_base) ? " : " : "",
               wanted_base,
               " {\n",
               NIL);
        Delete(scope);
      } else {
        // Wrap C++ enum with integers - just indicate start of enum with a comment, no comment for anonymous enums of any sort
        if (symname && !Getattr(n, "unnamedinstance"))
          Printf(constants_code, "  // %s \n", symname);
      }

      if (proxy_flag && !is_wrapping_class()) {
        // Global enums / enums in a namespace
        assert(!full_imclass_name);

        if (!nspace) {
          full_imclass_name = NewStringf("%s", imclass_name);
        } else {
          if (namespce) {
            full_imclass_name = NewStringf("%s.%s", namespce, imclass_name);
          } else {
            full_imclass_name = NewStringf("%s", imclass_name);
          }
        }
      }

      // Emit each enum item
      Language::enumDeclaration(n);

      if (proxy_flag && !is_wrapping_class()) {
        Delete(full_imclass_name);
        full_imclass_name = 0;
      }

      if ((enum_feature != SimpleEnum) && symname && typemap_lookup_type) {
        // Wrap (non-anonymous) C/C++ enum within a typesafe, typeunsafe or proper C# enum
        // Finish the enum declaration
        // Typemaps are used to generate the enum definition in a similar manner to proxy classes.
        Printv(enum_code,
               (enum_feature == ProperEnum) ? "\n" : typemapLookup(n, "csbody", typemap_lookup_type, WARN_CSHARP_TYPEMAP_CSBODY_UNDEF),  // main body of class
               typemapLookup(n, "cscode", typemap_lookup_type, WARN_NONE),                                                               // extra C# code
               "}",
               NIL);

        Replaceall(enum_code, "$csclassname", symname);

        // Substitute $enumvalues - intended usage is for typesafe enums
        if (Getattr(n, "enumvalues"))
          Replaceall(enum_code, "$enumvalues", Getattr(n, "enumvalues"));
        else
          Replaceall(enum_code, "$enumvalues", "");

        if (proxy_flag && is_wrapping_class()) {
          // Enums defined within the C++ class are defined within the proxy class

          // Add extra indentation
          Replaceall(enum_code, "\n", "\n  ");
          Replaceall(enum_code, "  \n", "\n");

          Printv(proxy_class_constants_code, "  ", enum_code, "\n\n", NIL);
        } else {
          // Global enums are defined in their own file
          String *output_directory = outputDirectory(nspace);
          File *f_enum = getOutputFile(output_directory, symname);

          addOpenNamespace(nspace, f_enum);

          Printv(f_enum,
                 typemapLookup(n, "csimports", typemap_lookup_type, WARN_NONE),  // Import statements
                 "\n",
                 enum_code,
                 "\n",
                 NIL);

          addCloseNamespace(nspace, f_enum);
          if (f_enum != f_single_out)
            Delete(f_enum);
          f_enum = NULL;
          Delete(output_directory);
        }
      } else {
        // Wrap C++ enum with simple constant
        Printf(enum_code, "\n");
        if (proxy_flag && is_wrapping_class())
          Printv(proxy_class_constants_code, enum_code, NIL);
        else
          Printv(module_class_constants_code, enum_code, NIL);
      }

      Delete(enum_code);
      enum_code = NULL;
    }
    return SWIG_OK;
  }

  /* ----------------------------------------------------------------------
   * enumvalueDeclaration()
   * ---------------------------------------------------------------------- */

  virtual int enumvalueDeclaration(Node *n) {
    if (getCurrentClass() && (cplus_mode != PUBLIC))
      return SWIG_NOWRAP;

    Swig_require("enumvalueDeclaration", n, "*name", "?value", NIL);
    String *symname = Getattr(n, "sym:name");
    String *value = Getattr(n, "value");
    String *name = Getattr(n, "name");
    Node *parent = parentNode(n);
    int unnamedinstance = GetFlag(parent, "unnamedinstance");
    String *parent_name = Getattr(parent, "name");
    String *nspace = getNSpace();
    String *newsymname = 0;
    String *tmpValue;

    // Strange hack from parent method
    if (value)
      tmpValue = NewString(value);
    else
      tmpValue = NewString(name);
    // Note that this is used in enumValue() amongst other places
    Setattr(n, "value", tmpValue);

    // Deal with enum values that are not int
    int swigtype = SwigType_type(Getattr(n, "type"));
    if (swigtype == T_CHAR) {
      String *enumstringval = Getattr(n, "enumstringval");
      if (enumstringval) {
        // Escape character literal for C#.
        String *val = NewStringf("'%(csharpescape)s'", enumstringval);
        Setattr(n, "enumvalue", val);
        Delete(val);
      }
    } else {
      String *numval = Getattr(n, "enumnumval");
      if (numval)
        Setattr(n, "enumvalue", numval);
    }

    {
      EnumFeature enum_feature = decodeEnumFeature(parent);

      if ((enum_feature == SimpleEnum) && GetFlag(parent, "scopedenum")) {
        newsymname = Swig_name_member(0, Getattr(parent, "sym:name"), symname);
        symname = newsymname;
      }

      // Add to language symbol table
      String *scope = 0;
      if (unnamedinstance || !parent_name || enum_feature == SimpleEnum) {
        String *enumClassPrefix = getEnumClassPrefix();
        if (enumClassPrefix) {
          scope = NewString("");
          if (nspace)
            Printf(scope, "%s.", nspace);
          Printf(scope, "%s", enumClassPrefix);
        } else {
          scope = Copy(module_class_name);
        }
      } else {
        scope = getCurrentScopeName(nspace);
        if (!scope)
          scope = Copy(Getattr(parent, "sym:name"));
        else
          Printf(scope, ".%s", Getattr(parent, "sym:name"));
      }
      if (!addSymbol(symname, n, scope))
        return SWIG_ERROR;

      const String *csattributes = Getattr(n, "feature:cs:attributes");

      if ((enum_feature == ProperEnum) && parent_name && !unnamedinstance) {
        // Wrap (non-anonymous) C/C++ enum with a proper C# enum
        // Emit the enum item.
        if (!GetFlag(n, "firstenumitem"))
          Printf(enum_code, ",\n");

        if (csattributes)
          Printf(enum_code, "  %s\n", csattributes);

        // Translate documentation comments
        if (have_docstring(n)) {
          String *ds = docstring(n, tab2);
          Printv(enum_code, ds, NIL);
          Delete(ds);
        }
        Printf(enum_code, "  %s", symname);

        // Check for the %csconstvalue feature
        String *value = Getattr(n, "feature:cs:constvalue");

        // Note that the enum value must be a true constant and cannot be set from a PINVOKE call, thus no support for %csconst(0)
        value = value ? value : Getattr(n, "enumvalue");
        if (value) {
          Printf(enum_code, " = %s", value);
        }
      } else {
        // Wrap C/C++ enums with constant integers or use the typesafe enum pattern
        SwigType *typemap_lookup_type = parent_name ? parent_name : NewString("enum ");
        Setattr(n, "type", typemap_lookup_type);
        const String *tm = typemapLookup(n, "cstype", typemap_lookup_type, WARN_CSHARP_TYPEMAP_CSWTYPE_UNDEF);

        String *return_type = Copy(tm);
        substituteClassname(typemap_lookup_type, return_type);
        const String *methodmods = Getattr(n, "feature:cs:methodmodifiers");
        methodmods = methodmods ? methodmods : (is_public(n) ? public_string : protected_string);

        if (csattributes)
          Printf(enum_code, "  %s\n", csattributes);

        if ((enum_feature == TypesafeEnum) && parent_name && !unnamedinstance) {
          // Wrap (non-anonymous) enum using the typesafe enum pattern
          if (Getattr(n, "enumvalue")) {
            String *value = enumValue(n);
            Printf(enum_code, "  %s static readonly %s %s = new %s(\"%s\", %s);\n", methodmods, return_type, symname, return_type, symname, value);
            Delete(value);
          } else {
            Printf(enum_code, "  %s static readonly %s %s = new %s(\"%s\");\n", methodmods, return_type, symname, return_type, symname);
          }
        } else {
          // Simple integer constants
          // Note these are always generated for anonymous enums, no matter what enum_feature is specified
          // Code generated is the same for SimpleEnum and TypeunsafeEnum -> the class it is generated into is determined later

          // The %csconst feature determines how the constant value is obtained
          int const_feature_flag = GetFlag(n, "feature:cs:const");

          const char *const_readonly = const_feature_flag ? "const" : "static readonly";
          String *value = enumValue(n);
          Printf(enum_code, "  %s %s %s %s = %s;\n", methodmods, const_readonly, return_type, symname, value);
          Delete(value);
        }
        Delete(return_type);
      }

      // Add the enum value to the comma separated list being constructed in the enum declaration.
      String *enumvalues = Getattr(parent, "enumvalues");
      if (!enumvalues)
        Setattr(parent, "enumvalues", Copy(symname));
      else
        Printv(enumvalues, ", ", symname, NIL);
      Delete(scope);
    }

    Delete(newsymname);
    Delete(tmpValue);
    Swig_restore(n);
    return SWIG_OK;
  }

  /* -----------------------------------------------------------------------
   * constantWrapper()
   * Used for wrapping constants - #define or %constant.
   * Also for inline initialised const static primitive type member variables (short, int, double, enums etc).
   * C# static const variables are generated for these.
   * If the %csconst(1) feature is used then the C constant value is used to initialise the C# const variable.
   * If not, a PINVOKE method is generated to get the C constant value for initialisation of the C# const variable.
   * However, if the %csconstvalue feature is used, it overrides all other ways to generate the initialisation.
   * Also note that this method might be called for wrapping enum items (when the enum is using %csconst(0)).
   * ------------------------------------------------------------------------ */

  virtual int constantWrapper(Node *n) {
    String *symname = Getattr(n, "sym:name");
    SwigType *t = Getattr(n, "type");
    ParmList *l = Getattr(n, "parms");
    String *tm;
    String *return_type = NewString("");
    String *constants_code = NewString("");
    // The value as C# code.
    String *csvalue = Copy(Getattr(n, "value"));
    Swig_save("constantWrapper",
              n,
              "tmap:ctype:out",
              "tmap:imtype:out",
              "tmap:cstype:out",
              "tmap:out:null",
              "tmap:imtype:outattributes",
              "tmap:cstype:outattributes",
              NIL);

    bool is_enum_item = (Cmp(nodeType(n), "enumitem") == 0);

    const String *itemname = (proxy_flag && wrapping_member_flag) ? variable_name : symname;
    if (!is_enum_item) {
      String *scope = 0;
      if (proxy_class_name) {
        String *nspace = getNSpace();
        scope = NewString("");
        if (nspace)
          Printf(scope, "%s.", nspace);
        Printf(scope, "%s", proxy_class_name);
      } else {
        scope = Copy(module_class_name);
      }
      if (!addSymbol(itemname, n, scope))
        return SWIG_ERROR;
      Delete(scope);
    }

    // The %csconst feature determines how the constant value is obtained
    int const_feature_flag = GetFlag(n, "feature:cs:const");

    /* Adjust the enum type for the Swig_typemap_lookup.
     * We want the same jstype typemap for all the enum items so we use the enum type (parent node). */
    if (is_enum_item) {
      t = Getattr(parentNode(n), "enumtype");
      Setattr(n, "type", t);
    }

    /* Attach the non-standard typemaps to the parameter list. */
    Swig_typemap_attach_parms("cstype", l, NULL);

    /* Get C# return types */
    bool classname_substituted_flag = false;

    if ((tm = Swig_typemap_lookup("cstype", n, "", 0))) {
      String *cstypeout = Getattr(n, "tmap:cstype:out");  // the type in the cstype typemap's out attribute overrides the type in the typemap
      if (cstypeout)
        tm = cstypeout;
      classname_substituted_flag = substituteClassname(t, tm);
      Printf(return_type, "%s", tm);
    } else {
      Swig_warning(WARN_CSHARP_TYPEMAP_CSWTYPE_UNDEF, input_file, line_number, "No cstype typemap defined for %s\n", SwigType_str(t, 0));
    }

    if (Getattr(n, "stringval")) {
      char quote = 0;
      switch (SwigType_type(t)) {
      case T_STRING:
      case T_WSTRING:
        quote = '\"';
        break;
      case T_CHAR:
      case T_WCHAR:
        quote = '\'';
        break;
      }
      if (quote) {
        // Escape character literal for C#.
        Delete(csvalue);
        csvalue = NewStringf("%c%(csharpescape)s%c", quote, Getattr(n, "stringval"), quote);
      }
    }

    const String *outattributes = Getattr(n, "tmap:cstype:outattributes");
    if (outattributes)
      Printf(constants_code, "  %s\n", outattributes);

    const String *methodmods = Getattr(n, "feature:cs:methodmodifiers");
    methodmods = methodmods ? methodmods : (is_public(n) ? public_string : protected_string);

    // Translate documentation comments
    if (have_docstring(n)) {
      String *ds = docstring(n, tab2);
      Printv(constants_code, ds, NIL);
      Delete(ds);
    }

    Printf(constants_code, "  %s %s %s %s = ", methodmods, (const_feature_flag ? "const" : "static readonly"), return_type, itemname);

    // Check for the %csconstvalue feature
    String *value = Getattr(n, "feature:cs:constvalue");

    if (value) {
      Printf(constants_code, "%s;\n", value);
    } else if (!const_feature_flag) {
      // Default enum and constant handling will work with any type of C constant and initialises the C# variable from C through a PINVOKE call.

      if (classname_substituted_flag) {
        if (SwigType_isenum(t)) {
          // This handles wrapping of inline initialised const enum static member variables (not when wrapping enum items - ignored later on)
          Printf(constants_code, "(%s)%s.%s();\n", return_type, full_imclass_name ? full_imclass_name : imclass_name, Swig_name_get(getNSpace(), symname));
        } else {
          // This handles function pointers using the %constant directive
          Printf(constants_code,
                 "new %s(%s.%s(), false);\n",
                 return_type,
                 full_imclass_name ? full_imclass_name : imclass_name,
                 Swig_name_get(getNSpace(), symname));
        }
      } else {
        Printf(constants_code, "%s.%s();\n", full_imclass_name ? full_imclass_name : imclass_name, Swig_name_get(getNSpace(), symname));
      }

      // Each constant and enum value is wrapped with a separate PInvoke function call
      SetFlag(n, "feature:immutable");
      enum_constant_flag = true;
      variableWrapper(n);
      enum_constant_flag = false;
    } else {
      // Alternative constant handling will use the C syntax to make a true C# constant and hope that it compiles as C# code
      if (Getattr(n, "wrappedasconstant")) {
        if (SwigType_type(t) == T_CHAR) {
          String *stringval = Getattr(n, "stringval");
          if (stringval)
            Printf(constants_code, "'%(csharpescape)s';\n", stringval);
          else
            Printf(constants_code, "(char)%s;\n", Getattr(n, "staticmembervariableHandler:value"));
        } else {
          Printf(constants_code, "%s;\n", Getattr(n, "staticmembervariableHandler:value"));
        }
      } else {
        Printf(constants_code, "%s;\n", csvalue);
      }
    }

    // Emit the generated code to appropriate place
    // Enums only emit the intermediate and PINVOKE methods, so no proxy or module class wrapper methods needed
    if (!is_enum_item) {
      if (proxy_flag && wrapping_member_flag)
        Printv(proxy_class_constants_code, constants_code, NIL);
      else
        Printv(module_class_constants_code, constants_code, NIL);
    }
    // Cleanup
    Swig_restore(n);
    Delete(return_type);
    Delete(constants_code);
    Delete(csvalue);
    return SWIG_OK;
  }

  /* -----------------------------------------------------------------------------
   * insertDirective()
   * ----------------------------------------------------------------------------- */

  virtual int insertDirective(Node *n) {
    int ret = SWIG_OK;
    String *code = Getattr(n, "code");
    String *section = Getattr(n, "section");
    Replaceall(code, "$module", module_class_name);
    Replaceall(code, "$imclassname", imclass_name);
    Replaceall(code, "$dllimport", dllimport);

    if (!ImportMode && (Cmp(section, "proxycode") == 0)) {
      if (proxy_class_code) {
        Swig_typemap_replace_embedded_typemap(code, n);
        int offset = Len(code) > 0 && *Char(code) == '\n' ? 1 : 0;
        Printv(proxy_class_code, Char(code) + offset, "\n", NIL);
      }
    } else {
      ret = Language::insertDirective(n);
    }
    return ret;
  }

  /* -----------------------------------------------------------------------------
   * pragmaDirective()
   *
   * Valid Pragmas:
   * imclassbase            - base (extends) for the intermediary class
   * imclassclassmodifiers  - class modifiers for the intermediary class
   * imclasscode            - text (C# code) is copied verbatim to the intermediary class
   * imclassimports         - import statements for the intermediary class
   * imclassinterfaces      - interface (implements) for the intermediary class
   *
   * modulebase              - base (extends) for the module class
   * moduleclassmodifiers    - class modifiers for the module class
   * modulecode              - text (C# code) is copied verbatim to the module class
   * moduleimports           - import statements for the module class
   * moduleinterfaces        - interface (implements) for the module class
   *
   * ----------------------------------------------------------------------------- */

  virtual int pragmaDirective(Node *n) {
    if (!ImportMode) {
      String *lang = Getattr(n, "lang");
      String *code = Getattr(n, "name");
      String *value = Getattr(n, "value");

      if (Strcmp(lang, "csharp") == 0) {

        String *strvalue = NewString(value);
        Replaceall(strvalue, "\\\"", "\"");

        if (Strcmp(code, "imclassbase") == 0) {
          Delete(imclass_baseclass);
          imclass_baseclass = Copy(strvalue);
        } else if (Strcmp(code, "imclassclassmodifiers") == 0) {
          Delete(imclass_class_modifiers);
          imclass_class_modifiers = Copy(strvalue);
        } else if (Strcmp(code, "imclasscode") == 0) {
          Printf(imclass_class_code, "%s\n", strvalue);
        } else if (Strcmp(code, "imclassimports") == 0) {
          Delete(imclass_imports);
          imclass_imports = Copy(strvalue);
        } else if (Strcmp(code, "imclassinterfaces") == 0) {
          Delete(imclass_interfaces);
          imclass_interfaces = Copy(strvalue);
        } else if (Strcmp(code, "modulebase") == 0) {
          Delete(module_baseclass);
          module_baseclass = Copy(strvalue);
        } else if (Strcmp(code, "moduleclassmodifiers") == 0) {
          Delete(module_class_modifiers);
          module_class_modifiers = Copy(strvalue);
        } else if (Strcmp(code, "modulecode") == 0) {
          Printf(module_class_code, "%s\n", strvalue);
        } else if (Strcmp(code, "moduleimports") == 0) {
          Delete(module_imports);
          module_imports = Copy(strvalue);
        } else if (Strcmp(code, "moduleinterfaces") == 0) {
          Delete(module_interfaces);
          module_interfaces = Copy(strvalue);
        } else {
          Swig_error(input_file, line_number, "Unrecognized pragma.\n");
        }
        Delete(strvalue);
      }
    }
    return Language::pragmaDirective(n);
  }

  /* -----------------------------------------------------------------------------
   * getQualifiedInterfaceName()
   * ----------------------------------------------------------------------------- */

  String *getQualifiedInterfaceName(Node *n) {
    String *ret = Getattr(n, "interface:qname");
    if (!ret) {
      String *nspace = Getattr(n, "sym:nspace");
      String *interface_name = Getattr(n, "interface:name");
      if (nspace) {
        if (namespce)
          ret = NewStringf("%s.%s.%s", namespce, nspace, interface_name);
        else
          ret = NewStringf("%s.%s", nspace, interface_name);
      } else {
        ret = Copy(interface_name);
      }
      Setattr(n, "interface:qname", ret);
    }
    return ret;
  }

  /* -----------------------------------------------------------------------------
   * getInterfaceName()
   * ----------------------------------------------------------------------------- */

  String *getInterfaceName(SwigType *t, bool qualified) {
    String *interface_name = NULL;
    if (proxy_flag) {
      Node *n = classLookup(t);
      if (n && Getattr(n, "interface:name"))
        interface_name = qualified ? getQualifiedInterfaceName(n) : Getattr(n, "interface:name");
    }
    return interface_name;
  }

  /* -----------------------------------------------------------------------------
   * addInterfaceNameAndUpcasts()
   * ----------------------------------------------------------------------------- */

  void addInterfaceNameAndUpcasts(SwigType *smart, String *interface_list, String *interface_upcasts, List *base_list, SwigType *c_classname) {
    for (Iterator it = First(base_list); it.item; it = Next(it)) {
      Node *base = it.item;
      SwigType *c_baseclassname = Getattr(base, "name");
      String *interface_name = Getattr(base, "interface:name");
      String *qualified_interface_name = getQualifiedInterfaceName(base);
      SwigType *bsmart = Getattr(base, "smart");
      if (Len(interface_list))
        Append(interface_list, ", ");
      Append(interface_list, qualified_interface_name);

      Node *attributes = NewHash();
      String *interface_code = Copy(typemapLookup(base, "csinterfacecode", Getattr(base, "classtypeobj"), WARN_CSHARP_TYPEMAP_INTERFACECODE_UNDEF, attributes));
      String *cptr_method_name = 0;
      if (interface_code) {
        Replaceall(interface_code, "$interfacename", interface_name);
        Replaceall(interface_code, "$csinterfacename", qualified_interface_name);
        Printv(interface_upcasts, interface_code, NIL);
        cptr_method_name = Copy(Getattr(attributes, "tmap:csinterfacecode:cptrmethod"));
      }
      if (!cptr_method_name)
        cptr_method_name = NewStringf("%s_GetInterfaceCPtr", interface_name);
      Replaceall(cptr_method_name, ".", "_");
      Replaceall(cptr_method_name, "$interfacename", interface_name);

      String *upcast_method_name = Swig_name_member(getNSpace(), getClassPrefix(), cptr_method_name);
      upcastsCode(smart, bsmart, upcast_method_name, c_classname, c_baseclassname);

      Delete(upcast_method_name);
      Delete(cptr_method_name);
      Delete(interface_code);
    }
  }

  /* -----------------------------------------------------------------------------
   * upcastsCode()
   *
   * Add code for C++ casting to base class
   * ----------------------------------------------------------------------------- */

  void upcastsCode(SwigType *smart, SwigType *bsmart, String *upcast_method_name, SwigType *c_classname, SwigType *c_baseclassname) {
    String *wname = Swig_name_wrapper(upcast_method_name);

    Printv(imclass_cppcasts_code, "\n  [global::System.Runtime.InteropServices.DllImport(\"", dllimport, "\", EntryPoint=\"", wname, "\")]\n", NIL);
    Printf(imclass_cppcasts_code, "  public static extern global::System.IntPtr %s(global::System.IntPtr jarg1);\n", upcast_method_name);

    Replaceall(imclass_cppcasts_code, "$csclassname", proxy_class_name);

    if (smart) {
      if (bsmart) {
        String *smartnamestr = SwigType_namestr(smart);
        String *bsmartnamestr = SwigType_namestr(bsmart);

        Printv(upcasts_code,
               "SWIGEXPORT ",
               bsmartnamestr,
               " * SWIGSTDCALL ",
               wname,
               "(",
               smartnamestr,
               " *jarg1) {\n",
               "    return jarg1 ? new ",
               bsmartnamestr,
               "(*jarg1) : 0;\n"
               "}\n",
               "\n",
               NIL);

        Delete(bsmartnamestr);
        Delete(smartnamestr);
      }
    } else {
      String *classname = SwigType_namestr(c_classname);
      String *baseclassname = SwigType_namestr(c_baseclassname);

      Printv(upcasts_code,
             "SWIGEXPORT ",
             baseclassname,
             " * SWIGSTDCALL ",
             wname,
             "(",
             classname,
             " *jarg1) {\n",
             "    return (",
             baseclassname,
             " *)jarg1;\n"
             "}\n",
             "\n",
             NIL);

      Delete(baseclassname);
      Delete(classname);
    }

    Delete(wname);
  }

  /* -----------------------------------------------------------------------------
   * emitProxyClassDefAndCPPCasts()
   * ----------------------------------------------------------------------------- */

  void emitProxyClassDefAndCPPCasts(Node *n) {
    SwigType *c_classname = Getattr(n, "name");
    SwigType *c_baseclassname = NULL;
    String *baseclass = NULL;
    String *interface_list = NewStringEmpty();
    String *interface_upcasts = NewStringEmpty();
    SwigType *typemap_lookup_type = Getattr(n, "classtypeobj");
    bool feature_director = Swig_directorclass(n) ? true : false;
    bool has_outerclass = Getattr(n, "nested:outer") != 0 && !GetFlag(n, "feature:flatnested");
    SwigType *smart = Getattr(n, "smart");
    SwigType *bsmart = 0;

    // Inheritance from pure C# classes
    Node *attributes = NewHash();
    const String *pure_baseclass = typemapLookup(n, "csbase", typemap_lookup_type, WARN_NONE, attributes);
    bool purebase_replace = GetFlag(attributes, "tmap:csbase:replace") ? true : false;
    bool purebase_notderived = GetFlag(attributes, "tmap:csbase:notderived") ? true : false;
    Delete(attributes);

    // C++ inheritance
    if (!purebase_replace) {
      List *baselist = Getattr(n, "bases");
      if (baselist) {
        Iterator base = First(baselist);
        while (base.item) {
          if (!(GetFlag(base.item, "feature:ignore") || GetFlag(base.item, "feature:interface"))) {
            SwigType *baseclassname = Getattr(base.item, "name");
            if (!c_baseclassname) {
              String *name = getProxyName(baseclassname);
              if (name) {
                c_baseclassname = baseclassname;
                baseclass = name;
                bsmart = Getattr(base.item, "smart");
              }
            } else {
              /* Warn about multiple inheritance for additional base class(es) */
              String *proxyclassname = Getattr(n, "classtypeobj");
              Swig_warning(WARN_CSHARP_MULTIPLE_INHERITANCE,
                           Getfile(n),
                           Getline(n),
                           "Warning for %s, base %s ignored. Multiple inheritance is not supported in C#.\n",
                           SwigType_namestr(proxyclassname),
                           SwigType_namestr(baseclassname));
            }
          }
          base = Next(base);
        }
      }
    }
    List *interface_bases = Getattr(n, "interface:bases");
    if (interface_bases)
      addInterfaceNameAndUpcasts(smart, interface_list, interface_upcasts, interface_bases, c_classname);

    bool derived = baseclass != 0;
    if (derived && purebase_notderived)
      pure_baseclass = empty_string;
    const String *wanted_base = baseclass ? baseclass : pure_baseclass;

    if (purebase_replace) {
      wanted_base = pure_baseclass;
      derived = false;
      baseclass = NULL;
      if (purebase_notderived)
        Swig_error(
          Getfile(n), Getline(n), "The csbase typemap for proxy %s must contain just one of the 'replace' or 'notderived' attributes.\n", typemap_lookup_type);
    } else if (Len(pure_baseclass) > 0 && Len(baseclass) > 0) {
      Swig_warning(WARN_CSHARP_MULTIPLE_INHERITANCE,
                   Getfile(n),
                   Getline(n),
                   "Warning for %s, base %s ignored. Multiple inheritance is not supported in C#. "
                   "Perhaps you need one of the 'replace' or 'notderived' attributes in the csbase typemap?\n",
                   typemap_lookup_type,
                   pure_baseclass);
    }

    // Pure C# interfaces
    const String *pure_interfaces = typemapLookup(n, derived ? "csinterfaces_derived" : "csinterfaces", typemap_lookup_type, WARN_NONE);
    if (*Char(interface_list) && *Char(pure_interfaces))
      Append(interface_list, ", ");
    Append(interface_list, pure_interfaces);
    // Start writing the proxy class
    if (!has_outerclass)
      Printv(proxy_class_def,
             typemapLookup(n, "csimports", typemap_lookup_type, WARN_NONE),  // Import statements
             "\n",
             NIL);

    // Translate documentation comments
    if (have_docstring(n)) {
      String *ds = docstring(n, "");
      Printv(proxy_class_def, ds, NIL);
      Delete(ds);
    }

    // Class attributes
    const String *csattributes = typemapLookup(n, "csattributes", typemap_lookup_type, WARN_NONE);
    if (csattributes && *Char(csattributes))
      Printf(proxy_class_def, "%s\n", csattributes);

    Printv(proxy_class_def,
           typemapLookup(n, "csclassmodifiers", typemap_lookup_type, WARN_CSHARP_TYPEMAP_CLASSMOD_UNDEF),  // Class modifiers
           " $csclassname",                                                                                // Class name and base class
           (*Char(wanted_base) || *Char(interface_list)) ? " : " : "",
           wanted_base,
           (*Char(wanted_base) && *Char(interface_list)) ?  // Interfaces
             ", "
                                                         : "",
           interface_list,
           " {",
           derived ? typemapLookup(n, "csbody_derived", typemap_lookup_type, WARN_CSHARP_TYPEMAP_CSBODY_UNDEF) :  // main body of class
             typemapLookup(n, "csbody", typemap_lookup_type, WARN_CSHARP_TYPEMAP_CSBODY_UNDEF),                   // main body of class
           NIL);

    // C++ destructor is wrapped by the Finalize and Dispose methods

    const char *tmap_method = derived ? "csdestruct_derived" : "csdestruct";
    const String *tm = typemapExists(n, tmap_method, typemap_lookup_type);
    if (tm) {
      Swig_error(Getfile(tm),
                 Getline(tm),
                 "A deprecated %s typemap was found for %s, please remove it and replace all csdestruct, csdestruct_derived and csfinalize typemaps by the "
                 "csdispose, csdispose_derived, csdisposing and csdisposing_derived typemaps.\n",
                 tmap_method,
                 proxy_class_name);
    }
    tmap_method = "csfinalize";
    tm = typemapExists(n, tmap_method, typemap_lookup_type);
    if (tm) {
      Swig_error(Getfile(tm),
                 Getline(tm),
                 "A deprecated %s typemap was found for %s, please remove it and replace all csdestruct, csdestruct_derived and csfinalize typemaps by the "
                 "csdispose, csdispose_derived, csdisposing and csdisposing_derived typemaps.\n",
                 tmap_method,
                 proxy_class_name);
    }

    tmap_method = derived ? "csdisposing_derived" : "csdisposing";
    String *destruct = NewString("");
    attributes = NewHash();
    const String *destruct_methodname = NULL;
    const String *destruct_methodmodifiers = NULL;
    const String *destruct_parameters = NULL;
    if (derived) {
      tm = typemapLookup(n, "csdisposing_derived", typemap_lookup_type, WARN_NONE, attributes);
      destruct_methodname = Getattr(attributes, "tmap:csdisposing_derived:methodname");
      destruct_methodmodifiers = Getattr(attributes, "tmap:csdisposing_derived:methodmodifiers");
      destruct_parameters = Getattr(attributes, "tmap:csdisposing_derived:parameters");
    } else {
      tm = typemapLookup(n, "csdisposing", typemap_lookup_type, WARN_NONE, attributes);
      destruct_methodname = Getattr(attributes, "tmap:csdisposing:methodname");
      destruct_methodmodifiers = Getattr(attributes, "tmap:csdisposing:methodmodifiers");
      destruct_parameters = Getattr(attributes, "tmap:csdisposing:parameters");
    }
    if (tm && *Char(tm)) {
      if (!destruct_methodname) {
        Swig_error(Getfile(n), Getline(n), "No methodname attribute defined in %s typemap for %s\n", tmap_method, proxy_class_name);
      }
      if (!destruct_methodmodifiers) {
        Swig_error(Getfile(n), Getline(n), "No methodmodifiers attribute defined in %s typemap for %s.\n", tmap_method, proxy_class_name);
      }
      if (!destruct_parameters)
        destruct_parameters = empty_string;
    }
    // Emit the Finalize and Dispose methods
    if (tm) {
      // Finalize and Dispose methods
      Printv(proxy_class_def, typemapLookup(n, derived ? "csdispose_derived" : "csdispose", typemap_lookup_type, WARN_NONE), NIL);
      // Dispose(bool disposing) method
      Printv(destruct, tm, NIL);
      if (*Char(destructor_call))
        Replaceall(destruct, "$imcall", destructor_call);
      else
        Replaceall(destruct, "$imcall", "throw new global::System.MethodAccessException(\"C++ destructor does not have public access\")");
      if (*Char(destruct)) {
        Printv(proxy_class_def, "\n  ", NIL);
        const String *methodmods = Getattr(n, "destructmethodmodifiers");
        if (methodmods)
          Printv(proxy_class_def, methodmods, NIL);
        else
          Printv(proxy_class_def, destruct_methodmodifiers, " ", derived ? "override" : "virtual", NIL);
        Printv(proxy_class_def, " void ", destruct_methodname, "(", destruct_parameters, ") ", destruct, "\n", NIL);
      }
    }
    if (*Char(interface_upcasts))
      Printv(proxy_class_def, interface_upcasts, NIL);

    if (feature_director) {
      // Generate director connect method
      // put this in classDirectorEnd ???
      Printf(proxy_class_code, "  private void SwigDirectorConnect() {\n");

      int i;
      for (i = first_class_dmethod; i < curr_class_dmethod; ++i) {
        UpcallData *udata = Getitem(dmethods_seq, i);
        String *method = Getattr(udata, "method");
        String *methid = Getattr(udata, "class_methodidx");
        String *overname = Getattr(udata, "overname");
        Printf(proxy_class_code, "    if (SwigDerivedClassHasMethod(\"%s\", swigMethodTypes%s))\n", method, methid);
        Printf(proxy_class_code, "      swigDelegate%s = new SwigDelegate%s_%s(SwigDirectorMethod%s);\n", methid, proxy_class_name, methid, overname);
      }
      String *director_connect_method_name = Swig_name_member(getNSpace(), getClassPrefix(), "director_connect");
      Printf(proxy_class_code, "    %s.%s(swigCPtr", imclass_name, director_connect_method_name);
      for (i = first_class_dmethod; i < curr_class_dmethod; ++i) {
        UpcallData *udata = Getitem(dmethods_seq, i);
        String *methid = Getattr(udata, "class_methodidx");
        Printf(proxy_class_code, ", swigDelegate%s", methid);
      }
      Printf(proxy_class_code, ");\n");
      Printf(proxy_class_code, "  }\n");

      if (first_class_dmethod < curr_class_dmethod) {
        // Only emit if there is at least one director method
        Printf(proxy_class_code, "\n");
        Printf(proxy_class_code, "  private bool SwigDerivedClassHasMethod(string methodName, global::System.Type[] methodTypes) {\n");
        Printf(proxy_class_code, "    global::System.Reflection.MethodInfo[] methodInfos = this.GetType().GetMethods(\n");
        Printf(proxy_class_code,
               "        global::System.Reflection.BindingFlags.Public | global::System.Reflection.BindingFlags.NonPublic | "
               "global::System.Reflection.BindingFlags.Instance);\n");
        Printf(proxy_class_code, "    foreach (global::System.Reflection.MethodInfo methodInfo in methodInfos) {\n");
        Printf(proxy_class_code, "      if (methodInfo.DeclaringType == null)\n");
        Printf(proxy_class_code, "        continue;\n\n");
        Printf(proxy_class_code, "      if (methodInfo.Name != methodName)\n");
        Printf(proxy_class_code, "        continue;\n\n");
        Printf(proxy_class_code, "      var parameters = methodInfo.GetParameters();\n");
        Printf(proxy_class_code, "      if (parameters.Length != methodTypes.Length)\n");
        Printf(proxy_class_code, "        continue;\n\n");
        Printf(proxy_class_code, "      bool parametersMatch = true;\n");
        Printf(proxy_class_code, "      for (var i = 0; i < parameters.Length; i++) {\n");
        Printf(proxy_class_code, "        if (parameters[i].ParameterType != methodTypes[i]) {\n");
        Printf(proxy_class_code, "          parametersMatch = false;\n");
        Printf(proxy_class_code, "          break;\n");
        Printf(proxy_class_code, "        }\n");
        Printf(proxy_class_code, "      }\n\n");
        Printf(proxy_class_code, "      if (!parametersMatch)\n");
        Printf(proxy_class_code, "        continue;\n\n");
        Printf(proxy_class_code, "      if (methodInfo.IsVirtual && (methodInfo.DeclaringType.IsSubclassOf(typeof(%s))) &&\n", proxy_class_name);
        Printf(proxy_class_code, "        methodInfo.DeclaringType != methodInfo.GetBaseDefinition().DeclaringType) {\n");
        Printf(proxy_class_code, "        return true;\n");
        Printf(proxy_class_code, "      }\n");
        Printf(proxy_class_code, "    }\n\n");
        Printf(proxy_class_code, "    return false;\n");

        /* Could add this code to cover corner case where the GetMethod() returns a method which allows type
         * promotion, eg it will return foo(double), if looking for foo(int).
         if (hasDerivedMethod) {
         hasDerivedMethod = false;
         if (methodInfo != null)
         {
         hasDerivedMethod = true;
         ParameterInfo[] parameterArray1 = methodInfo.GetParameters();
         for (int i=0; i<methodTypes.Length; i++)
         {
         if (parameterArray1[0].ParameterType != methodTypes[0])
         {
         hasDerivedMethod = false;
         break;
         }
         }
         }
         }
         */
        // Printf(proxy_class_code, "    return hasDerivedMethod;\n");
        Printf(proxy_class_code, "  }\n");
      }

      if (Len(director_delegate_callback) > 0)
        Printv(proxy_class_code, director_delegate_callback, NIL);
      if (Len(director_delegate_definitions) > 0)
        Printv(proxy_class_code, "\n", director_delegate_definitions, NIL);
      if (Len(director_delegate_instances) > 0)
        Printv(proxy_class_code, "\n", director_delegate_instances, NIL);
      if (Len(director_method_types) > 0)
        Printv(proxy_class_code, "\n", director_method_types, NIL);

      Delete(director_callback_typedefs);
      director_callback_typedefs = NULL;
      Delete(director_callbacks);
      director_callbacks = NULL;
      Delete(director_delegate_callback);
      director_delegate_callback = NULL;
      Delete(director_delegate_definitions);
      director_delegate_definitions = NULL;
      Delete(director_delegate_instances);
      director_delegate_instances = NULL;
      Delete(director_method_types);
      director_method_types = NULL;
      Delete(director_connect_parms);
      director_connect_parms = NULL;
      Delete(director_connect_method_name);
    }

    Delete(interface_upcasts);
    Delete(interface_list);
    Delete(attributes);
    Delete(destruct);

    // Emit extra user code
    Printv(proxy_class_def,
           typemapLookup(n, "cscode", typemap_lookup_type, WARN_NONE),  // extra C# code
           "\n",
           NIL);

    if (derived) {
      String *upcast_method_name = Swig_name_member(getNSpace(), getClassPrefix(), smart != 0 ? "SWIGSmartPtrUpcast" : "SWIGUpcast");
      upcastsCode(smart, bsmart, upcast_method_name, c_classname, c_baseclassname);
      Delete(upcast_method_name);
    }
  }

  /* ----------------------------------------------------------------------
   * emitInterfaceDeclaration()
   * ---------------------------------------------------------------------- */

  void emitInterfaceDeclaration(Node *n, String *interface_name, File *f_interface) {
    Printv(f_interface, typemapLookup(n, "csimports", Getattr(n, "classtypeobj"), WARN_NONE), "\n", NIL);

    if (have_docstring(n)) {
      String *ds = docstring(n, "");
      Printv(interface_class_code, ds, NIL);
      Delete(ds);
    }

    Printv(f_interface, typemapLookup(n, "csinterfacemodifiers", Getattr(n, "classtypeobj"), WARN_CSHARP_TYPEMAP_INTERFACEMODIFIERS_UNDEF), NIL);
    Printf(f_interface, " %s", interface_name);

    String *additional = Getattr(n, "feature:interface:additional");
    String *bases = additional ? NewStringf(" : %s", additional) : 0;
    if (List *baselist = Getattr(n, "bases")) {
      for (Iterator base = First(baselist); base.item; base = Next(base)) {
        if (GetFlag(base.item, "feature:ignore") || !GetFlag(base.item, "feature:interface"))
          continue;  // TODO: warn about skipped non-interface bases
        String *base_iname = getQualifiedInterfaceName(base.item);
        if (!bases)
          bases = NewStringf(" : %s", base_iname);
        else {
          Append(bases, ", ");
          Append(bases, base_iname);
        }
      }
    }
    if (bases) {
      Printv(f_interface, bases, NIL);
      Delete(bases);
    }
    Printf(f_interface, " {\n");

    Node *attributes = NewHash();
    String *interface_code = Copy(typemapLookup(n, "csinterfacecode", Getattr(n, "classtypeobj"), WARN_CSHARP_TYPEMAP_INTERFACECODE_UNDEF, attributes));
    if (interface_code) {
      String *interface_declaration = Copy(Getattr(attributes, "tmap:csinterfacecode:declaration"));
      if (interface_declaration) {
        Replaceall(interface_declaration, "$interfacename", interface_name);
        Printv(f_interface, interface_declaration, NIL);
        Delete(interface_declaration);
      }
      Delete(interface_code);
    }
  }

  /* ----------------------------------------------------------------------
   * classHandler()
   * ---------------------------------------------------------------------- */

  virtual int classHandler(Node *n) {
    String *nspace = getNSpace();
    File *f_proxy = NULL;
    File *f_interface = NULL;
    // save class local variables
    String *old_proxy_class_name = proxy_class_name;
    String *old_full_imclass_name = full_imclass_name;
    String *old_destructor_call = destructor_call;
    String *old_proxy_class_constants_code = proxy_class_constants_code;
    String *old_proxy_class_def = proxy_class_def;
    String *old_proxy_class_code = proxy_class_code;
    bool has_outerclass = Getattr(n, "nested:outer") && !GetFlag(n, "feature:flatnested");
    String *old_interface_class_code = interface_class_code;
    interface_class_code = 0;

    if (proxy_flag) {
      proxy_class_name = NewString(Getattr(n, "sym:name"));
      String *interface_name = GetFlag(n, "feature:interface") ? Getattr(n, "interface:name") : 0;
      if (Node *outer = Getattr(n, "nested:outer")) {
        String *outerClassesPrefix = Copy(Getattr(outer, "sym:name"));
        for (outer = Getattr(outer, "nested:outer"); outer != 0; outer = Getattr(outer, "nested:outer")) {
          Push(outerClassesPrefix, ".");
          Push(outerClassesPrefix, Getattr(outer, "sym:name"));
        }
        String *fnspace = nspace ? NewStringf("%s.%s", nspace, outerClassesPrefix) : outerClassesPrefix;
        if (!addSymbol(proxy_class_name, n, fnspace))
          return SWIG_ERROR;
        if (interface_name && !addInterfaceSymbol(interface_name, n, fnspace))
          return SWIG_ERROR;
        if (nspace)
          Delete(fnspace);
        Delete(outerClassesPrefix);
      } else {
        if (!addSymbol(proxy_class_name, n, nspace))
          return SWIG_ERROR;
        if (interface_name && !addInterfaceSymbol(interface_name, n, nspace))
          return SWIG_ERROR;
      }

      if (!nspace) {
        full_imclass_name = NewStringf("%s", imclass_name);
        if (Cmp(proxy_class_name, imclass_name) == 0) {
          Printf(stderr, "Class name cannot be equal to intermediary class name: %s\n", proxy_class_name);
          Exit(EXIT_FAILURE);
        }

        if (Cmp(proxy_class_name, module_class_name) == 0) {
          Printf(stderr, "Class name cannot be equal to module class name: %s\n", proxy_class_name);
          Exit(EXIT_FAILURE);
        }
      } else {
        if (namespce) {
          full_imclass_name = NewStringf("%s.%s", namespce, imclass_name);
        } else {
          full_imclass_name = NewStringf("%s", imclass_name);
        }
      }

      if (!has_outerclass) {
        String *output_directory = outputDirectory(nspace);
        f_proxy = getOutputFile(output_directory, proxy_class_name);

        addOpenNamespace(nspace, f_proxy);
        Delete(output_directory);
      } else
        ++nesting_depth;

      proxy_class_def = NewString("");
      proxy_class_code = NewString("");
      destructor_call = NewString("");
      proxy_class_constants_code = NewString("");

      if (GetFlag(n, "feature:interface")) {
        interface_class_code = NewString("");
        String *output_directory = outputDirectory(nspace);
        f_interface = getOutputFile(output_directory, interface_name);
        addOpenNamespace(nspace, f_interface);
        emitInterfaceDeclaration(n, interface_name, interface_class_code);
        Delete(output_directory);
      }
    }

    Language::classHandler(n);

    if (proxy_flag) {

      emitProxyClassDefAndCPPCasts(n);

      String *csclazzname = Swig_name_member(getNSpace(), getClassPrefix(), "");  // mangled full proxy class name

      Replaceall(proxy_class_def, "$csclassname", proxy_class_name);
      Replaceall(proxy_class_code, "$csclassname", proxy_class_name);
      Replaceall(proxy_class_constants_code, "$csclassname", proxy_class_name);
      Replaceall(interface_class_code, "$csclassname", proxy_class_name);

      Replaceall(proxy_class_def, "$csclazzname", csclazzname);
      Replaceall(proxy_class_code, "$csclazzname", csclazzname);
      Replaceall(proxy_class_constants_code, "$csclazzname", csclazzname);
      Replaceall(interface_class_code, "$csclazzname", csclazzname);

      Replaceall(proxy_class_def, "$module", module_class_name);
      Replaceall(proxy_class_code, "$module", module_class_name);
      Replaceall(proxy_class_constants_code, "$module", module_class_name);
      Replaceall(interface_class_code, "$module", module_class_name);

      Replaceall(proxy_class_def, "$imclassname", full_imclass_name);
      Replaceall(proxy_class_code, "$imclassname", full_imclass_name);
      Replaceall(proxy_class_constants_code, "$imclassname", full_imclass_name);
      Replaceall(interface_class_code, "$imclassname", full_imclass_name);

      Replaceall(proxy_class_def, "$dllimport", dllimport);
      Replaceall(proxy_class_code, "$dllimport", dllimport);
      Replaceall(proxy_class_constants_code, "$dllimport", dllimport);
      Replaceall(interface_class_code, "$dllimport", dllimport);

      if (!has_outerclass)
        Printv(f_proxy, proxy_class_def, proxy_class_code, NIL);
      else {
        Swig_offset_string(proxy_class_def, nesting_depth);
        Append(old_proxy_class_code, proxy_class_def);
        Swig_offset_string(proxy_class_code, nesting_depth);
        Append(old_proxy_class_code, proxy_class_code);
      }

      // Write out all the constants
      if (Len(proxy_class_constants_code) != 0) {
        if (!has_outerclass)
          Printv(f_proxy, proxy_class_constants_code, NIL);
        else {
          Swig_offset_string(proxy_class_constants_code, nesting_depth);
          Append(old_proxy_class_code, proxy_class_constants_code);
        }
      }
      if (!has_outerclass) {
        Printf(f_proxy, "}\n");
        addCloseNamespace(nspace, f_proxy);
        if (f_proxy != f_single_out)
          Delete(f_proxy);
        f_proxy = NULL;
      } else {
        for (int i = 0; i < nesting_depth; ++i)
          Append(old_proxy_class_code, "  ");
        Append(old_proxy_class_code, "}\n\n");
        --nesting_depth;
      }

      if (f_interface) {
        Printv(f_interface, interface_class_code, "}\n", NIL);
        addCloseNamespace(nspace, f_interface);
        if (f_interface != f_single_out)
          Delete(f_interface);
        f_interface = 0;
      }

      emitDirectorExtraMethods(n);

      Delete(interface_class_code);
      interface_class_code = old_interface_class_code;
      Delete(csclazzname);
      Delete(proxy_class_name);
      proxy_class_name = old_proxy_class_name;
      Delete(full_imclass_name);
      full_imclass_name = old_full_imclass_name;
      Delete(destructor_call);
      destructor_call = old_destructor_call;
      Delete(proxy_class_constants_code);
      proxy_class_constants_code = old_proxy_class_constants_code;
      Delete(proxy_class_def);
      proxy_class_def = old_proxy_class_def;
      Delete(proxy_class_code);
      proxy_class_code = old_proxy_class_code;
    }
    return SWIG_OK;
  }

  void printArgumentDeclaration(Node *n, Parm *p, String *param_type, String *arg, String *code) {
    String *specifiedoverridekey = NewString("feature:cs:defaultargs:");
    Append(specifiedoverridekey, arg);
    String *specifiedoverridevalue = Getattr(n, specifiedoverridekey);
    if (specifiedoverridevalue) {
      Printf(code, "%s %s=%s", param_type, arg, specifiedoverridevalue);
    } else {
      String *cppvalue = NULL;
      // if they've not specified defaultargs, then fall back to
      // the normal default handling of specifying one overload per possible
      // set of arguments.  If they have, then use the default argument from
      // c++ as a literal csharp expression.
      if (Getattr(n, "feature:cs:defaultargs"))
        cppvalue = Getattr(p, "value");
      if (cppvalue)
        Printf(code, "%s %s=%s", param_type, arg, cppvalue);
      else
        Printf(code, "%s %s", param_type, arg);
    }
    Delete(specifiedoverridekey);
  }

  /* ----------------------------------------------------------------------
   * memberfunctionHandler()
   * ---------------------------------------------------------------------- */

  virtual int memberfunctionHandler(Node *n) {
    Language::memberfunctionHandler(n);

    if (proxy_flag) {
      String *overloaded_name = getOverloadedName(n);
      String *intermediary_function_name = Swig_name_member(getNSpace(), getClassPrefix(), overloaded_name);
      Setattr(n, "proxyfuncname", Getattr(n, "sym:name"));
      Setattr(n, "imfuncname", intermediary_function_name);
      proxyClassFunctionHandler(n);
      Delete(overloaded_name);
    }
    return SWIG_OK;
  }

  /* ----------------------------------------------------------------------
   * staticmemberfunctionHandler()
   * ---------------------------------------------------------------------- */

  virtual int staticmemberfunctionHandler(Node *n) {

    static_flag = true;
    Language::staticmemberfunctionHandler(n);

    if (proxy_flag) {
      String *overloaded_name = getOverloadedName(n);
      String *intermediary_function_name = Swig_name_member(getNSpace(), getClassPrefix(), overloaded_name);
      Setattr(n, "proxyfuncname", Getattr(n, "sym:name"));
      Setattr(n, "imfuncname", intermediary_function_name);
      proxyClassFunctionHandler(n);
      Delete(overloaded_name);
    }
    static_flag = false;

    return SWIG_OK;
  }

  /* -----------------------------------------------------------------------------
   * proxyClassFunctionHandler()
   *
   * Function called for creating a C# wrapper function around a c++ function in the
   * proxy class. Used for both static and non-static C++ class functions.
   * C++ class static functions map to C# static functions.
   * Two extra attributes in the Node must be available. These are "proxyfuncname" -
   * the name of the C# class proxy function, which in turn will call "imfuncname" -
   * the intermediary (PInvoke) function name in the intermediary class.
   * ----------------------------------------------------------------------------- */

  void proxyClassFunctionHandler(Node *n) {
    SwigType *t = Getattr(n, "type");
    ParmList *l = Getattr(n, "parms");
    String *intermediary_function_name = Getattr(n, "imfuncname");
    String *proxy_function_name = Getattr(n, "proxyfuncname");
    String *tm;
    Parm *p;
    Parm *last_parm = 0;
    int i;
    String *comment_code = NewString("");
    String *imcall = NewString("");
    String *return_type = NewString("");
    String *function_code = NewString("");
    bool setter_flag = false;
    String *pre_code = NewString("");
    String *post_code = NewString("");
    String *terminator_code = NewString("");
    bool is_interface =
      GetFlag(parentNode(n), "feature:interface") && !checkAttribute(n, "kind", "variable") && !static_flag && Getattr(n, "interface:owner") == 0;

    if (!proxy_flag)
      return;

    // Wrappers not wanted for some methods where the parameters cannot be overloaded in C#
    if (Getattr(n, "overload:ignore"))
      return;

    if (Getattr(n, "feature:cs:defaultargs") && Getattr(n, "defaultargs"))
      return;

    // Don't generate proxy method for additional explicitcall method used in directors
    if (GetFlag(n, "explicitcall"))
      return;

    // Translate documentation comments
    if (have_docstring(n)) {
      comment_code = docstring(n, tab2);
      if (is_interface)
        Printv(interface_class_code, "\n", comment_code, NIL);
    }

    if (l) {
      if (SwigType_type(Getattr(l, "type")) == T_VOID) {
        l = nextSibling(l);
      }
    }

    /* Attach the non-standard typemaps to the parameter list */
    Swig_typemap_attach_parms("in", l, NULL);
    Swig_typemap_attach_parms("cstype", l, NULL);
    Swig_typemap_attach_parms("csin", l, NULL);

    /* Get return types */
    if ((tm = Swig_typemap_lookup("cstype", n, "", 0))) {
      // Note that in the case of polymorphic (covariant) return types, the method's return type is changed to be the base of the C++ return type
      SwigType *covariant = Getattr(n, "covariant");
      String *cstypeout = Getattr(n, "tmap:cstype:out");  // the type in the cstype typemap's out attribute overrides the type in the typemap
      if (cstypeout)
        tm = cstypeout;
      substituteClassname(covariant ? covariant : t, tm);
      Printf(return_type, "%s", tm);
      if (covariant)
        Swig_warning(WARN_CSHARP_COVARIANT_RET,
                     input_file,
                     line_number,
                     "Covariant return types not supported in C#. Proxy method will return %s.\n",
                     SwigType_str(covariant, 0));
    } else {
      Swig_warning(WARN_CSHARP_TYPEMAP_CSWTYPE_UNDEF, input_file, line_number, "No cstype typemap defined for %s\n", SwigType_str(t, 0));
    }

    if (wrapping_member_flag && !enum_constant_flag) {
      // Properties
      setter_flag = (Cmp(Getattr(n, "sym:name"), Swig_name_set(getNSpace(), Swig_name_member(0, getClassPrefix(), variable_name))) == 0);
      if (setter_flag)
        Swig_typemap_attach_parms("csvarin", l, NULL);
    }

    /* Start generating the proxy function */
    const String *outattributes = Getattr(n, "tmap:cstype:outattributes");
    if (outattributes)
      Printf(function_code, "  %s\n", outattributes);
    const String *csattributes = Getattr(n, "feature:cs:attributes");
    if (csattributes)
      Printf(function_code, "  %s\n", csattributes);
    const String *methodmods = Getattr(n, "feature:cs:methodmodifiers");
    if (methodmods) {
      if (is_smart_pointer()) {
        // Smart pointer classes do not mirror the inheritance hierarchy of the underlying pointer type, so no virtual/override/new required.
        String *mmods = Copy(methodmods);
        Replaceall(mmods, "override", "");
        Replaceall(mmods, "virtual", "");
        Replaceall(mmods, "new", "");
        Chop(mmods);  // remove trailing whitespace
        Printf(function_code, "  %s ", mmods);
        Delete(mmods);
      } else {
        Printf(function_code, "  %s ", methodmods);
      }
    } else {
      methodmods = (is_public(n) ? public_string : protected_string);
      Printf(function_code, "  %s ", methodmods);
      if (!is_smart_pointer()) {
        // Smart pointer classes do not mirror the inheritance hierarchy of the underlying pointer type, so no virtual/override/new required.
        if (Getattr(n, "override"))
          Printf(function_code, "override ");
        else if (checkAttribute(n, "storage", "virtual"))
          Printf(function_code, "virtual ");
        if (Getattr(n, "hides"))
          Printf(function_code, "new ");
      }
    }
    if (static_flag)
      Printf(function_code, "static ");
    Printf(function_code, "%s %s(", return_type, proxy_function_name);
    if (is_interface)
      Printf(interface_class_code, "  %s %s(", return_type, proxy_function_name);

    Printv(imcall, full_imclass_name, ".$imfuncname(", NIL);
    if (!static_flag)
      Printf(imcall, "swigCPtr");

    emit_mark_varargs(l);
    int gencomma = !static_flag;

    /* Output each parameter */
    for (i = 0, p = l; p; i++) {

      /* Ignored varargs */
      if (checkAttribute(p, "varargs:ignore", "1")) {
        p = nextSibling(p);
        continue;
      }

      /* Ignored parameters */
      if (checkAttribute(p, "tmap:in:numinputs", "0")) {
        p = Getattr(p, "tmap:in:next");
        continue;
      }

      /* Ignore the 'this' argument for variable wrappers */
      if (!(variable_wrapper_flag && i == 0)) {
        SwigType *pt = Getattr(p, "type");
        String *param_type = NewString("");
        if (setter_flag)
          last_parm = p;

        /* Get the C# parameter type */
        if ((tm = Getattr(p, "tmap:cstype"))) {
          substituteClassname(pt, tm);
          const String *inattributes = Getattr(p, "tmap:cstype:inattributes");
          Printf(param_type, "%s%s", inattributes ? inattributes : empty_string, tm);
        } else {
          Swig_warning(WARN_CSHARP_TYPEMAP_CSWTYPE_UNDEF, input_file, line_number, "No cstype typemap defined for %s\n", SwigType_str(pt, 0));
        }

        if (gencomma)
          Printf(imcall, ", ");

        String *arg = makeParameterName(n, p, i, setter_flag);

        // Use typemaps to transform type used in C# wrapper function (in proxy class) to type used in PInvoke function (in intermediary class)
        if ((tm = Getattr(p, "tmap:csin"))) {
          substituteClassname(pt, tm);
          Replaceall(tm, "$csinput", arg);
          String *pre = Getattr(p, "tmap:csin:pre");
          if (pre) {
            substituteClassname(pt, pre);
            Replaceall(pre, "$csinput", arg);
            if (Len(pre_code) > 0)
              Printf(pre_code, "\n");
            Printv(pre_code, pre, NIL);
          }
          String *post = Getattr(p, "tmap:csin:post");
          if (post) {
            substituteClassname(pt, post);
            Replaceall(post, "$csinput", arg);
            if (Len(post_code) > 0)
              Printf(post_code, "\n");
            Printv(post_code, post, NIL);
          }
          String *terminator = Getattr(p, "tmap:csin:terminator");
          if (terminator) {
            substituteClassname(pt, terminator);
            Replaceall(terminator, "$csinput", arg);
            if (Len(terminator_code) > 0)
              Insert(terminator_code, 0, "\n");
            Insert(terminator_code, 0, terminator);
          }
          Printv(imcall, tm, NIL);
        } else {
          Swig_warning(WARN_CSHARP_TYPEMAP_CSIN_UNDEF, input_file, line_number, "No csin typemap defined for %s\n", SwigType_str(pt, 0));
        }

        /* Add parameter to proxy function */
        if (gencomma >= 2) {
          Printf(function_code, ", ");
          if (is_interface)
            Printf(interface_class_code, ", ");
        }
        gencomma = 2;
        printArgumentDeclaration(n, p, param_type, arg, function_code);
        if (is_interface)
          printArgumentDeclaration(n, p, param_type, arg, interface_class_code);

        Delete(arg);
        Delete(param_type);
      }
      p = Getattr(p, "tmap:in:next");
    }

    Printf(imcall, ")");
    Printf(function_code, ")");
    if (is_interface)
      Printf(interface_class_code, ");\n");

    // Transform return type used in PInvoke function (in intermediary class) to type used in C# wrapper function (in proxy class)
    if ((tm = Swig_typemap_lookup("csout", n, "", 0))) {
      excodeSubstitute(n, tm, "csout", n);
      bool is_pre_code = Len(pre_code) > 0;
      bool is_post_code = Len(post_code) > 0;
      bool is_terminator_code = Len(terminator_code) > 0;
      if (is_pre_code || is_post_code || is_terminator_code) {
        Replaceall(tm, "\n ", "\n   ");  // add extra indentation to code in typemap
        if (is_post_code) {
          Insert(tm, 0, "\n    try ");
          Printv(tm, " finally {\n", post_code, "\n    }", NIL);
        } else {
          Insert(tm, 0, "\n    ");
        }
        if (is_pre_code) {
          Insert(tm, 0, pre_code);
          Insert(tm, 0, "\n");
        }
        if (is_terminator_code) {
          Printv(tm, "\n", terminator_code, NIL);
        }
        Insert(tm, 0, "{");
        Printf(tm, "\n  }");
      }
      if (GetFlag(n, "feature:new"))
        Replaceall(tm, "$owner", "true");
      else
        Replaceall(tm, "$owner", "false");
      substituteClassname(t, tm);

      // For director methods: generate code to selectively make a normal polymorphic call or
      // an explicit method call - needed to prevent infinite recursion calls in director methods.
      Node *explicit_n = Getattr(n, "explicitcallnode");
      if (explicit_n) {
        String *ex_overloaded_name = getOverloadedName(explicit_n);
        String *ex_intermediary_function_name = Swig_name_member(getNSpace(), getClassPrefix(), ex_overloaded_name);

        String *ex_imcall = Copy(imcall);
        Replaceall(ex_imcall, "$imfuncname", ex_intermediary_function_name);
        Replaceall(imcall, "$imfuncname", intermediary_function_name);
        String *excode = NewString("");
        Node *directorNode = Getattr(n, "directorNode");
        UpcallData *udata = directorNode ? Getattr(directorNode, "upcalldata") : 0;
        if (udata) {
          String *methid = Getattr(udata, "class_methodidx");

          if (!Cmp(return_type, "void"))
            Printf(excode, "if (SwigDerivedClassHasMethod(\"%s\", swigMethodTypes%s)) %s; else %s", proxy_function_name, methid, ex_imcall, imcall);
          else
            Printf(excode, "(SwigDerivedClassHasMethod(\"%s\", swigMethodTypes%s) ? %s : %s)", proxy_function_name, methid, ex_imcall, imcall);

          Clear(imcall);
          Printv(imcall, excode, NIL);
        } else {
          // probably an ignored method or nodirector
        }
        Delete(excode);
        Delete(ex_overloaded_name);
      } else {
        Replaceall(imcall, "$imfuncname", intermediary_function_name);
      }
      Replaceall(tm, "$imfuncname", intermediary_function_name);
      Replaceall(tm, "$imcall", imcall);
    } else {
      Swig_warning(WARN_CSHARP_TYPEMAP_CSOUT_UNDEF, input_file, line_number, "No csout typemap defined for %s\n", SwigType_str(t, 0));
    }

    if (wrapping_member_flag && !enum_constant_flag) {
      // Properties
      if (generate_property_declaration_flag) {  // Ensure the declaration is generated just once should the property contain both a set and get
        // Get the C# variable type - obtained differently depending on whether a setter is required.
        String *variable_type = return_type;
        if (setter_flag) {
          assert(last_parm);  // (last parameter is the only parameter for properties)
          /* Get variable type - ensure the variable name is fully resolved during typemap lookup via the symbol table set in NewParmNode */
          SwigType *cvariable_type = Getattr(last_parm, "type");
          Parm *variable_parm = NewParmNode(cvariable_type, n);
          if ((tm = Swig_typemap_lookup("cstype", variable_parm, "", 0))) {
            String *cstypeout = Getattr(variable_parm, "tmap:cstype:out");  // the type in the cstype typemap's out attribute overrides the type in the typemap
            if (cstypeout)
              tm = cstypeout;
            substituteClassname(cvariable_type, tm);
            variable_type = tm;
          } else {
            Swig_warning(WARN_CSHARP_TYPEMAP_CSOUT_UNDEF, input_file, line_number, "No cstype typemap defined for %s\n", SwigType_str(cvariable_type, 0));
          }
        }
        const String *csattributes = Getattr(n, "feature:cs:attributes");
        if (csattributes)
          Printf(proxy_class_code, "  %s\n", csattributes);
        const String *methodmods = Getattr(n, "feature:cs:methodmodifiers");
        if (!methodmods)
          methodmods = (is_public(n) ? public_string : protected_string);

        // Translate documentation comments
        if (have_docstring(n)) {
          Printv(proxy_class_code, comment_code, NIL);
        }

        // Start property declaration
        Printf(proxy_class_code, "  %s %s%s %s {", methodmods, static_flag ? "static " : "", variable_type, variable_name);
      }
      generate_property_declaration_flag = false;

      if (setter_flag) {
        // Setter method
        assert(last_parm);  // (last parameter is the only parameter for properties)
        SwigType *cvariable_type = Getattr(last_parm, "type");
        Parm *variable_parm = NewParmNode(cvariable_type, n);
        if ((tm = Swig_typemap_lookup("csvarin", variable_parm, "", 0))) {
          substituteClassname(cvariable_type, tm);
          Replaceall(tm, "$csinput", "value");
          Replaceall(tm, "$imfuncname", intermediary_function_name);
          Replaceall(tm, "$imcall", imcall);
          excodeSubstitute(n, tm, "csvarin", variable_parm);
          Printf(proxy_class_code, "%s", tm);
        } else {
          Swig_warning(WARN_CSHARP_TYPEMAP_CSOUT_UNDEF, input_file, line_number, "No csvarin typemap defined for %s\n", SwigType_str(cvariable_type, 0));
        }
      } else {
        // Getter method
        if ((tm = Swig_typemap_lookup("csvarout", n, "", 0))) {
          if (GetFlag(n, "feature:new"))
            Replaceall(tm, "$owner", "true");
          else
            Replaceall(tm, "$owner", "false");
          substituteClassname(t, tm);
          Replaceall(tm, "$imfuncname", intermediary_function_name);
          Replaceall(tm, "$imcall", imcall);
          excodeSubstitute(n, tm, "csvarout", n);
          Printf(proxy_class_code, "%s", tm);
        } else {
          Swig_warning(WARN_CSHARP_TYPEMAP_CSOUT_UNDEF, input_file, line_number, "No csvarout typemap defined for %s\n", SwigType_str(t, 0));
        }
      }
    } else {
      // Normal function call
      Printf(function_code, " %s\n\n", tm ? (const String *)tm : empty_string);
      Printv(proxy_class_code, comment_code, NIL);
      Printv(proxy_class_code, function_code, NIL);
    }

    Delete(pre_code);
    Delete(post_code);
    Delete(terminator_code);
    Delete(function_code);
    Delete(return_type);
    Delete(imcall);
    Delete(comment_code);
  }

  /* ----------------------------------------------------------------------
   * constructorHandler()
   * ---------------------------------------------------------------------- */

  virtual int constructorHandler(Node *n) {

    ParmList *l = Getattr(n, "parms");
    String *tm;
    Parm *p;
    int i;
    String *function_code = NewString("");
    String *comment_code = NewString("");
    String *helper_code =
      NewString("");  // Holds code for the constructor helper method generated only when the csin typemap has code in the pre or post attributes
    String *helper_args = NewString("");
    String *pre_code = NewString("");
    String *post_code = NewString("");
    String *terminator_code = NewString("");
    String *im_return_type = NewString("");
    bool feature_director = (parentNode(n) && Swig_directorclass(n));

    Language::constructorHandler(n);

    // Wrappers not wanted for some methods where the parameters cannot be overloaded in C#
    if (Getattr(n, "overload:ignore"))
      return SWIG_OK;

    if (Getattr(n, "feature:cs:defaultargs") && Getattr(n, "defaultargs"))
      return SWIG_OK;

    if (proxy_flag) {
      String *overloaded_name = getOverloadedName(n);
      String *mangled_overname = Swig_name_construct(getNSpace(), overloaded_name);
      String *imcall = NewString("");

      const String *csattributes = Getattr(n, "feature:cs:attributes");
      if (csattributes) {
        Printf(function_code, "  %s\n", csattributes);
        Printf(helper_code, "  %s\n", csattributes);
      }
      const String *methodmods = Getattr(n, "feature:cs:methodmodifiers");
      methodmods = methodmods ? methodmods : (is_public(n) ? public_string : protected_string);

      tm = Getattr(n, "tmap:imtype");                     // typemaps were attached earlier to the node
      String *imtypeout = Getattr(n, "tmap:imtype:out");  // the type in the imtype typemap's out attribute overrides the type in the typemap
      if (imtypeout)
        tm = imtypeout;
      Printf(im_return_type, "%s", tm);

      Printf(function_code, "  %s %s(", methodmods, proxy_class_name);
      Printf(helper_code, "  static private %s SwigConstruct%s(", im_return_type, proxy_class_name);

      Printv(imcall, full_imclass_name, ".", mangled_overname, "(", NIL);

      /* Attach the non-standard typemaps to the parameter list */
      Swig_typemap_attach_parms("in", l, NULL);
      Swig_typemap_attach_parms("cstype", l, NULL);
      Swig_typemap_attach_parms("csin", l, NULL);

      emit_mark_varargs(l);

      int gencomma = 0;

      /* Output each parameter */
      for (i = 0, p = l; p; i++) {

        /* Ignored varargs */
        if (checkAttribute(p, "varargs:ignore", "1")) {
          p = nextSibling(p);
          continue;
        }

        /* Ignored parameters */
        if (checkAttribute(p, "tmap:in:numinputs", "0")) {
          p = Getattr(p, "tmap:in:next");
          continue;
        }

        SwigType *pt = Getattr(p, "type");
        String *param_type = NewString("");

        /* Get the C# parameter type */
        if ((tm = Getattr(p, "tmap:cstype"))) {
          substituteClassname(pt, tm);
          const String *inattributes = Getattr(p, "tmap:cstype:inattributes");
          Printf(param_type, "%s%s", inattributes ? inattributes : empty_string, tm);
        } else {
          Swig_warning(WARN_CSHARP_TYPEMAP_CSWTYPE_UNDEF, input_file, line_number, "No cstype typemap defined for %s\n", SwigType_str(pt, 0));
        }

        if (gencomma)
          Printf(imcall, ", ");

        String *arg = makeParameterName(n, p, i, false);
        String *cshin = 0;

        // Use typemaps to transform type used in C# wrapper function (in proxy class) to type used in PInvoke function (in intermediary class)
        if ((tm = Getattr(p, "tmap:csin"))) {
          substituteClassname(pt, tm);
          Replaceall(tm, "$csinput", arg);
          String *pre = Getattr(p, "tmap:csin:pre");
          if (pre) {
            substituteClassname(pt, pre);
            Replaceall(pre, "$csinput", arg);
            if (Len(pre_code) > 0)
              Printf(pre_code, "\n");
            Printv(pre_code, pre, NIL);
          }
          String *post = Getattr(p, "tmap:csin:post");
          if (post) {
            substituteClassname(pt, post);
            Replaceall(post, "$csinput", arg);
            if (Len(post_code) > 0)
              Printf(post_code, "\n");
            Printv(post_code, post, NIL);
          }
          String *terminator = Getattr(p, "tmap:csin:terminator");
          if (terminator) {
            substituteClassname(pt, terminator);
            Replaceall(terminator, "$csinput", arg);
            if (Len(terminator_code) > 0)
              Insert(terminator_code, 0, "\n");
            Insert(terminator_code, 0, terminator);
          }
          cshin = Getattr(p, "tmap:csin:cshin");
          if (cshin)
            Replaceall(cshin, "$csinput", arg);
          Printv(imcall, tm, NIL);
        } else {
          Swig_warning(WARN_CSHARP_TYPEMAP_CSIN_UNDEF, input_file, line_number, "No csin typemap defined for %s\n", SwigType_str(pt, 0));
        }

        /* Add parameter to proxy function */
        if (gencomma) {
          Printf(function_code, ", ");
          Printf(helper_code, ", ");
          Printf(helper_args, ", ");
        }
        printArgumentDeclaration(n, p, param_type, arg, function_code);
        Printf(helper_code, "%s %s", param_type, arg);
        Printf(helper_args, "%s", cshin ? cshin : arg);
        ++gencomma;

        Delete(cshin);
        Delete(arg);
        Delete(param_type);
        p = Getattr(p, "tmap:in:next");
      }

      Printf(imcall, ")");

      Printf(function_code, ")");
      Printf(helper_code, ")");

      /* Insert the csconstruct typemap, doing the replacement for $directorconnect, as needed */
      Hash *attributes = NewHash();
      String *typemap_lookup_type = Getattr(getCurrentClass(), "classtypeobj");
      String *construct_tm = Copy(typemapLookup(n, "csconstruct", typemap_lookup_type, WARN_CSHARP_TYPEMAP_CSCONSTRUCT_UNDEF, attributes));
      if (construct_tm) {
        if (!feature_director) {
          Replaceall(construct_tm, "$directorconnect", "");
        } else {
          String *connect_attr = Getattr(attributes, "tmap:csconstruct:directorconnect");

          if (connect_attr) {
            Replaceall(construct_tm, "$directorconnect", connect_attr);
          } else {
            Swig_warning(WARN_CSHARP_NO_DIRECTORCONNECT_ATTR,
                         input_file,
                         line_number,
                         "\"directorconnect\" attribute missing in %s \"csconstruct\" typemap.\n",
                         Getattr(n, "name"));
            Replaceall(construct_tm, "$directorconnect", "");
          }
        }

        Printv(function_code, " ", construct_tm, NIL);
      }

      excodeSubstitute(n, function_code, "csconstruct", attributes);

      bool is_pre_code = Len(pre_code) > 0;
      bool is_post_code = Len(post_code) > 0;
      bool is_terminator_code = Len(terminator_code) > 0;
      if (is_pre_code || is_post_code || is_terminator_code) {
        Printf(helper_code, " {\n");
        if (is_pre_code) {
          Printv(helper_code, pre_code, "\n", NIL);
        }
        if (is_post_code) {
          Printf(helper_code, "    try {\n");
          Printv(helper_code, "      return ", imcall, ";\n", NIL);
          Printv(helper_code, "    } finally {\n", post_code, "\n    }", NIL);
        } else {
          Printv(helper_code, "    return ", imcall, ";", NIL);
        }
        if (is_terminator_code) {
          Printv(helper_code, "\n", terminator_code, NIL);
        }
        Printf(helper_code, "\n  }\n");
        String *helper_name = NewStringf("%s.SwigConstruct%s(%s)", proxy_class_name, proxy_class_name, helper_args);
        String *im_outattributes = Getattr(n, "tmap:imtype:outattributes");
        if (im_outattributes)
          Printf(proxy_class_code, "  %s\n", im_outattributes);
        Printv(proxy_class_code, helper_code, "\n", NIL);
        Replaceall(function_code, "$imcall", helper_name);
        Delete(helper_name);
      } else {
        Replaceall(function_code, "$imcall", imcall);
      }

      // Translate documentation comments
      if (have_docstring(n)) {
        comment_code = docstring(n, tab2);
      }

      Printv(proxy_class_code, comment_code, NIL);
      Printv(proxy_class_code, function_code, "\n", NIL);

      Delete(helper_args);
      Delete(im_return_type);
      Delete(pre_code);
      Delete(post_code);
      Delete(terminator_code);
      Delete(construct_tm);
      Delete(attributes);
      Delete(overloaded_name);
      Delete(imcall);
      Delete(comment_code);
    }

    return SWIG_OK;
  }

  /* ----------------------------------------------------------------------
   * destructorHandler()
   * ---------------------------------------------------------------------- */

  virtual int destructorHandler(Node *n) {
    Language::destructorHandler(n);
    String *symname = Getattr(n, "sym:name");

    if (proxy_flag) {
      Printv(destructor_call, full_imclass_name, ".", Swig_name_destroy(getNSpace(), symname), "(swigCPtr)", NIL);
      const String *methodmods = Getattr(n, "feature:cs:methodmodifiers");
      if (methodmods)
        Setattr(getCurrentClass(), "destructmethodmodifiers", methodmods);
    }

    return SWIG_OK;
  }

  /* ----------------------------------------------------------------------
   * membervariableHandler()
   * ---------------------------------------------------------------------- */

  virtual int membervariableHandler(Node *n) {

    generate_property_declaration_flag = true;
    variable_name = Getattr(n, "sym:name");
    wrapping_member_flag = true;
    variable_wrapper_flag = true;
    Language::membervariableHandler(n);
    wrapping_member_flag = false;
    variable_wrapper_flag = false;
    generate_property_declaration_flag = false;

    // End property declaration
    Printf(proxy_class_code, "\n  }\n\n");

    return SWIG_OK;
  }

  /* ----------------------------------------------------------------------
   * staticmembervariableHandler()
   * ---------------------------------------------------------------------- */

  virtual int staticmembervariableHandler(Node *n) {

    generate_property_declaration_flag = true;
    variable_name = Getattr(n, "sym:name");
    wrapping_member_flag = true;
    static_flag = true;
    Language::staticmembervariableHandler(n);
    wrapping_member_flag = false;
    static_flag = false;
    generate_property_declaration_flag = false;

    if (!GetFlag(n, "wrappedasconstant")) {
      // End property declaration
      Printf(proxy_class_code, "\n  }\n\n");
    }

    return SWIG_OK;
  }

  /* ----------------------------------------------------------------------
   * memberconstantHandler()
   * ---------------------------------------------------------------------- */

  virtual int memberconstantHandler(Node *n) {
    variable_name = Getattr(n, "sym:name");
    wrapping_member_flag = true;
    Language::memberconstantHandler(n);
    wrapping_member_flag = false;
    return SWIG_OK;
  }

  /* -----------------------------------------------------------------------------
   * getOverloadedName()
   * ----------------------------------------------------------------------------- */

  String *getOverloadedName(Node *n) {

    /* A C# HandleRef is used for all classes in the SWIG intermediary class.
     * The intermediary class methods are thus mangled when overloaded to give
     * a unique name. */
    String *overloaded_name = Copy(Getattr(n, "sym:name"));

    if (Getattr(n, "sym:overloaded")) {
      Printv(overloaded_name, Getattr(n, "sym:overname"), NIL);
    }

    return overloaded_name;
  }

  /* -----------------------------------------------------------------------------
   * moduleClassFunctionHandler()
   * ----------------------------------------------------------------------------- */

  void moduleClassFunctionHandler(Node *n) {
    SwigType *t = Getattr(n, "type");
    ParmList *l = Getattr(n, "parms");
    String *tm;
    Parm *p;
    Parm *last_parm = 0;
    int i;
    String *imcall = NewString("");
    String *return_type = NewString("");
    String *function_code = NewString("");
    int num_arguments = 0;
    String *overloaded_name = getOverloadedName(n);
    String *func_name = NULL;
    bool setter_flag = false;
    String *pre_code = NewString("");
    String *post_code = NewString("");
    String *terminator_code = NewString("");

    if (Getattr(n, "feature:cs:defaultargs") && Getattr(n, "defaultargs"))
      return;

    if (l) {
      if (SwigType_type(Getattr(l, "type")) == T_VOID) {
        l = nextSibling(l);
      }
    }

    /* Attach the non-standard typemaps to the parameter list */
    Swig_typemap_attach_parms("cstype", l, NULL);
    Swig_typemap_attach_parms("csin", l, NULL);

    /* Get return types */
    if ((tm = Swig_typemap_lookup("cstype", n, "", 0))) {
      String *cstypeout = Getattr(n, "tmap:cstype:out");  // the type in the cstype typemap's out attribute overrides the type in the typemap
      if (cstypeout)
        tm = cstypeout;
      substituteClassname(t, tm);
      Printf(return_type, "%s", tm);
    } else {
      Swig_warning(WARN_CSHARP_TYPEMAP_CSWTYPE_UNDEF, input_file, line_number, "No cstype typemap defined for %s\n", SwigType_str(t, 0));
    }

    /* Change function name for global variables */
    if (proxy_flag && global_variable_flag) {
      // Capitalize the first letter in the variable to create the getter/setter function name
      func_name = NewString("");
      setter_flag = (Cmp(Getattr(n, "sym:name"), Swig_name_set(getNSpace(), variable_name)) == 0);
      if (setter_flag)
        Printf(func_name, "set");
      else
        Printf(func_name, "get");
      Putc(toupper((int)*Char(variable_name)), func_name);
      Printf(func_name, "%s", Char(variable_name) + 1);
      if (setter_flag)
        Swig_typemap_attach_parms("csvarin", l, NULL);
    } else {
      func_name = Copy(Getattr(n, "sym:name"));
    }

    /* Start generating the function */
    const String *outattributes = Getattr(n, "tmap:cstype:outattributes");
    if (outattributes)
      Printf(function_code, "  %s\n", outattributes);
    const String *csattributes = Getattr(n, "feature:cs:attributes");
    if (csattributes)
      Printf(function_code, "  %s\n", csattributes);
    const String *methodmods = Getattr(n, "feature:cs:methodmodifiers");
    methodmods = methodmods ? methodmods : (is_public(n) ? public_string : protected_string);

    // Translate documentation comments
    if (have_docstring(n)) {
      String *ds = docstring(n, tab2);
      Printv(function_code, ds, NIL);
      Delete(ds);
    }

    Printf(function_code, "  %s static %s %s(", methodmods, return_type, func_name);
    Printv(imcall, imclass_name, ".", overloaded_name, "(", NIL);

    /* Get number of required and total arguments */
    num_arguments = emit_num_arguments(l);

    bool global_or_member_variable = global_variable_flag || (wrapping_member_flag && !enum_constant_flag);
    int gencomma = 0;

    /* Output each parameter */
    for (i = 0, p = l; i < num_arguments; i++) {

      /* Ignored parameters */
      while (checkAttribute(p, "tmap:in:numinputs", "0")) {
        p = Getattr(p, "tmap:in:next");
      }

      SwigType *pt = Getattr(p, "type");
      String *param_type = NewString("");
      last_parm = p;

      /* Get the C# parameter type */
      if ((tm = Getattr(p, "tmap:cstype"))) {
        substituteClassname(pt, tm);
        const String *inattributes = Getattr(p, "tmap:cstype:inattributes");
        Printf(param_type, "%s%s", inattributes ? inattributes : empty_string, tm);
      } else {
        Swig_warning(WARN_CSHARP_TYPEMAP_CSWTYPE_UNDEF, input_file, line_number, "No cstype typemap defined for %s\n", SwigType_str(pt, 0));
      }

      if (gencomma)
        Printf(imcall, ", ");

      String *arg = makeParameterName(n, p, i, global_or_member_variable);

      // Use typemaps to transform type used in C# wrapper function (in proxy class) to type used in PInvoke function (in intermediary class)
      if ((tm = Getattr(p, "tmap:csin"))) {
        substituteClassname(pt, tm);
        Replaceall(tm, "$csinput", arg);
        String *pre = Getattr(p, "tmap:csin:pre");
        if (pre) {
          substituteClassname(pt, pre);
          Replaceall(pre, "$csinput", arg);
          if (Len(pre_code) > 0)
            Printf(pre_code, "\n");
          Printv(pre_code, pre, NIL);
        }
        String *post = Getattr(p, "tmap:csin:post");
        if (post) {
          substituteClassname(pt, post);
          Replaceall(post, "$csinput", arg);
          if (Len(post_code) > 0)
            Printf(post_code, "\n");
          Printv(post_code, post, NIL);
        }
        String *terminator = Getattr(p, "tmap:csin:terminator");
        if (terminator) {
          substituteClassname(pt, terminator);
          Replaceall(terminator, "$csinput", arg);
          if (Len(terminator_code) > 0)
            Insert(terminator_code, 0, "\n");
          Insert(terminator_code, 0, terminator);
        }
        Printv(imcall, tm, NIL);
      } else {
        Swig_warning(WARN_CSHARP_TYPEMAP_CSIN_UNDEF, input_file, line_number, "No csin typemap defined for %s\n", SwigType_str(pt, 0));
      }

      /* Add parameter to module class function */
      if (gencomma >= 2)
        Printf(function_code, ", ");
      gencomma = 2;
      printArgumentDeclaration(n, p, param_type, arg, function_code);

      p = Getattr(p, "tmap:in:next");
      Delete(arg);
      Delete(param_type);
    }

    Printf(imcall, ")");
    Printf(function_code, ")");

    // Transform return type used in PInvoke function (in intermediary class) to type used in C# wrapper function (in module class)
    if ((tm = Swig_typemap_lookup("csout", n, "", 0))) {
      excodeSubstitute(n, tm, "csout", n);
      bool is_pre_code = Len(pre_code) > 0;
      bool is_post_code = Len(post_code) > 0;
      bool is_terminator_code = Len(terminator_code) > 0;
      if (is_pre_code || is_post_code || is_terminator_code) {
        Replaceall(tm, "\n ", "\n   ");  // add extra indentation to code in typemap
        if (is_post_code) {
          Insert(tm, 0, "\n    try ");
          Printv(tm, " finally {\n", post_code, "\n    }", NIL);
        } else {
          Insert(tm, 0, "\n    ");
        }
        if (is_pre_code) {
          Insert(tm, 0, pre_code);
          Insert(tm, 0, "\n");
        }
        if (is_terminator_code) {
          Printv(tm, "\n", terminator_code, NIL);
        }
        Insert(tm, 0, "{");
        Printf(tm, "\n  }");
      }
      if (GetFlag(n, "feature:new"))
        Replaceall(tm, "$owner", "true");
      else
        Replaceall(tm, "$owner", "false");
      substituteClassname(t, tm);
      Replaceall(tm, "$imfuncname", overloaded_name);
      Replaceall(tm, "$imcall", imcall);
    } else {
      Swig_warning(WARN_CSHARP_TYPEMAP_CSOUT_UNDEF, input_file, line_number, "No csout typemap defined for %s\n", SwigType_str(t, 0));
    }

    if (proxy_flag && global_variable_flag) {
      // Properties
      if (generate_property_declaration_flag) {  // Ensure the declaration is generated just once should the property contain both a set and get
        // Get the C# variable type - obtained differently depending on whether a setter is required.
        String *variable_type = return_type;
        if (setter_flag) {
          p = last_parm;  // (last parameter is the only parameter for properties)
          SwigType *pt = Getattr(p, "type");
          if ((tm = Getattr(p, "tmap:cstype"))) {
            substituteClassname(pt, tm);
            String *cstypeout = Getattr(p, "tmap:cstype:out");  // the type in the cstype typemap's out attribute overrides the type in the typemap
            variable_type = cstypeout ? cstypeout : tm;
          } else {
            Swig_warning(WARN_CSHARP_TYPEMAP_CSOUT_UNDEF, input_file, line_number, "No csvarin typemap defined for %s\n", SwigType_str(pt, 0));
          }
        }
        const String *csattributes = Getattr(n, "feature:cs:attributes");
        if (csattributes)
          Printf(module_class_code, "  %s\n", csattributes);
        const String *methodmods = Getattr(n, "feature:cs:methodmodifiers");
        if (!methodmods)
          methodmods = (is_public(n) ? public_string : protected_string);
        Printf(module_class_code, "  %s static %s %s {", methodmods, variable_type, variable_name);
      }
      generate_property_declaration_flag = false;

      if (setter_flag) {
        // Setter method
        p = last_parm;  // (last parameter is the only parameter for properties)
        SwigType *pt = Getattr(p, "type");
        if ((tm = Getattr(p, "tmap:csvarin"))) {
          substituteClassname(pt, tm);
          Replaceall(tm, "$csinput", "value");
          Replaceall(tm, "$imfuncname", overloaded_name);
          Replaceall(tm, "$imcall", imcall);
          excodeSubstitute(n, tm, "csvarin", p);
          Printf(module_class_code, "%s", tm);
        } else {
          Swig_warning(WARN_CSHARP_TYPEMAP_CSOUT_UNDEF, input_file, line_number, "No csvarin typemap defined for %s\n", SwigType_str(pt, 0));
        }
      } else {
        // Getter method
        if ((tm = Swig_typemap_lookup("csvarout", n, "", 0))) {
          if (GetFlag(n, "feature:new"))
            Replaceall(tm, "$owner", "true");
          else
            Replaceall(tm, "$owner", "false");
          substituteClassname(t, tm);
          Replaceall(tm, "$imfuncname", overloaded_name);
          Replaceall(tm, "$imcall", imcall);
          excodeSubstitute(n, tm, "csvarout", n);
          Printf(module_class_code, "%s", tm);
        } else {
          Swig_warning(WARN_CSHARP_TYPEMAP_CSOUT_UNDEF, input_file, line_number, "No csvarout typemap defined for %s\n", SwigType_str(t, 0));
        }
      }
    } else {
      // Normal function call
      Printf(function_code, " %s\n\n", tm ? (const String *)tm : empty_string);
      Printv(module_class_code, function_code, NIL);
    }

    Delete(pre_code);
    Delete(post_code);
    Delete(terminator_code);
    Delete(function_code);
    Delete(return_type);
    Delete(imcall);
    Delete(func_name);
  }

  /*----------------------------------------------------------------------
   * replaceSpecialVariables()
   *--------------------------------------------------------------------*/

  virtual void replaceSpecialVariables(String *method, String *tm, Parm *parm) {
    (void)method;
    SwigType *type = Getattr(parm, "type");
    substituteClassname(type, tm);
  }

  /*----------------------------------------------------------------------
   * decodeEnumFeature()
   * Decode the possible enum features, which are one of:
   *   %csenum(simple)
   *   %csenum(typeunsafe) - default
   *   %csenum(typesafe)
   *   %csenum(proper)
   *--------------------------------------------------------------------*/

  EnumFeature decodeEnumFeature(Node *n) {
    EnumFeature enum_feature = TypeunsafeEnum;
    String *feature = Getattr(n, "feature:cs:enum");
    if (feature) {
      if (Cmp(feature, "simple") == 0)
        enum_feature = SimpleEnum;
      else if (Cmp(feature, "typesafe") == 0)
        enum_feature = TypesafeEnum;
      else if (Cmp(feature, "proper") == 0)
        enum_feature = ProperEnum;
    }
    return enum_feature;
  }

  /* -----------------------------------------------------------------------
   * enumValue()
   * This method will return a string with an enum value to use in C# generated
   * code. If the %csconst feature is not used, the string will contain the intermediary
   * class call to obtain the enum value. The intermediary class and PINVOKE methods to obtain
   * the enum value will be generated. Otherwise the C/C++ enum value will be used if there
   * is one and hopefully it will compile as C# code - e.g. 20 as in: enum E{e=20};
   * The %csconstvalue feature overrides all other ways to generate the constant value.
   * The caller must delete memory allocated for the returned string.
   * ------------------------------------------------------------------------ */

  String *enumValue(Node *n) {
    String *symname = Getattr(n, "sym:name");

    // Check for the %csconstvalue feature
    String *value = Getattr(n, "feature:cs:constvalue");

    if (!value) {
      // The %csconst feature determines how the constant value is obtained
      int const_feature_flag = GetFlag(n, "feature:cs:const");

      if (const_feature_flag) {
        // Use the C syntax to make a true C# constant and hope that it compiles as C# code
        value = Getattr(n, "enumvalue") ? Copy(Getattr(n, "enumvalue")) : Copy(Getattr(n, "enumvalueex"));
      } else {
        String *newsymname = 0;
        if (!getCurrentClass() || !proxy_flag) {
          String *enumClassPrefix = getEnumClassPrefix();
          if (enumClassPrefix) {
            // A global scoped enum
            newsymname = Swig_name_member(0, enumClassPrefix, symname);
            symname = newsymname;
          }
        }

        // Get the enumvalue from a PINVOKE call
        if (!getCurrentClass() || !cparse_cplusplus || !proxy_flag) {
          // Strange hack to change the name
          Setattr(n, "name", Getattr(n, "value")); /* for wrapping of enums in a namespace when emit_action is used */
          constantWrapper(n);
          value = NewStringf("%s.%s()", full_imclass_name ? full_imclass_name : imclass_name, Swig_name_get(getNSpace(), symname));
        } else {
          memberconstantHandler(n);
          value = NewStringf(
            "%s.%s()", full_imclass_name ? full_imclass_name : imclass_name, Swig_name_get(getNSpace(), Swig_name_member(0, getEnumClassPrefix(), symname)));
        }
      }
    }
    return value;
  }

  /* -----------------------------------------------------------------------------
   * getEnumName()
   * ----------------------------------------------------------------------------- */

  String *getEnumName(SwigType *t) {
    Node *enumname = NULL;
    Node *n = enumLookup(t);
    if (n) {
      enumname = Getattr(n, "enumname");
      if (!enumname) {
        String *symname = Getattr(n, "sym:name");
        if (symname) {
          // Add in class scope when referencing enum if not a global enum
          String *scopename_prefix = Swig_scopename_prefix(Getattr(n, "name"));
          String *proxyname = 0;
          if (scopename_prefix) {
            proxyname = getProxyName(scopename_prefix);
          }
          if (proxyname) {
            enumname = NewStringf("%s.%s", proxyname, symname);
          } else {
            // global enum or enum in a namespace
            String *nspace = Getattr(n, "sym:nspace");
            if (nspace) {
              if (namespce)
                enumname = NewStringf("%s.%s.%s", namespce, nspace, symname);
              else
                enumname = NewStringf("%s.%s", nspace, symname);
            } else {
              enumname = Copy(symname);
            }
          }
          Setattr(n, "enumname", enumname);
          Delete(enumname);
          Delete(scopename_prefix);
        }
      }
    }

    return enumname;
  }

  /* -----------------------------------------------------------------------------
   * substituteClassname()
   *
   * Substitute the special variable $csclassname with the proxy class name for classes/structs/unions
   * that SWIG knows about. Also substitutes enums with enum name.
   * Otherwise use the $descriptor name for the C# class name. Note that the $&csclassname substitution
   * is the same as a $&descriptor substitution, ie one pointer added to descriptor name.
   * Inputs:
   *   pt - parameter type
   *   tm - typemap contents that might contain the special variable to be replaced
   * Outputs:
   *   tm - typemap contents complete with the special variable substitution
   * Return:
   *   substitution_performed - flag indicating if a substitution was performed
   * ----------------------------------------------------------------------------- */

  bool substituteClassname(SwigType *pt, String *tm) {
    bool substitution_performed = false;
    SwigType *type = Copy(SwigType_typedef_resolve_all(pt));
    SwigType *strippedtype = SwigType_strip_qualifiers(type);

    if (Strstr(tm, "$csclassname")) {
      SwigType *classnametype = Copy(strippedtype);
      substituteClassnameSpecialVariable(classnametype, tm, "$csclassname");
      substitution_performed = true;
      Delete(classnametype);
    }
    if (Strstr(tm, "$*csclassname")) {
      SwigType *classnametype = Copy(strippedtype);
      Delete(SwigType_pop(classnametype));
      if (Len(classnametype) > 0) {
        substituteClassnameSpecialVariable(classnametype, tm, "$*csclassname");
        substitution_performed = true;
      }
      Delete(classnametype);
    }
    if (Strstr(tm, "$&csclassname")) {
      SwigType *classnametype = Copy(strippedtype);
      SwigType_add_pointer(classnametype);
      substituteClassnameSpecialVariable(classnametype, tm, "$&csclassname");
      substitution_performed = true;
      Delete(classnametype);
    }
    if (Strstr(tm, "$csinterfacename")) {
      SwigType *interfacenametype = Copy(strippedtype);
      substituteInterfacenameSpecialVariable(interfacenametype, tm, "$csinterfacename", true);
      substitution_performed = true;
      Delete(interfacenametype);
    }
    if (Strstr(tm, "$*csinterfacename")) {
      SwigType *interfacenametype = Copy(strippedtype);
      Delete(SwigType_pop(interfacenametype));
      if (Len(interfacenametype) > 0) {
        substituteInterfacenameSpecialVariable(interfacenametype, tm, "$*csinterfacename", true);
        substitution_performed = true;
      }
      Delete(interfacenametype);
    }
    if (Strstr(tm, "$&csinterfacename")) {
      SwigType *interfacenametype = Copy(strippedtype);
      SwigType_add_pointer(interfacenametype);
      substituteInterfacenameSpecialVariable(interfacenametype, tm, "$&csinterfacename", true);
      substitution_performed = true;
      Delete(interfacenametype);
    }
    if (Strstr(tm, "$interfacename")) {
      SwigType *interfacenametype = Copy(strippedtype);
      substituteInterfacenameSpecialVariable(interfacenametype, tm, "$interfacename", false);
      substitution_performed = true;
      Delete(interfacenametype);
    }
    if (Strstr(tm, "$*interfacename")) {
      SwigType *interfacenametype = Copy(strippedtype);
      Delete(SwigType_pop(interfacenametype));
      if (Len(interfacenametype) > 0) {
        substituteInterfacenameSpecialVariable(interfacenametype, tm, "$*interfacename", false);
        substitution_performed = true;
      }
      Delete(interfacenametype);
    }
    if (Strstr(tm, "$&interfacename")) {
      SwigType *interfacenametype = Copy(strippedtype);
      SwigType_add_pointer(interfacenametype);
      substituteInterfacenameSpecialVariable(interfacenametype, tm, "$&interfacename", false);
      substitution_performed = true;
      Delete(interfacenametype);
    }

    Delete(strippedtype);
    Delete(type);

    return substitution_performed;
  }

  /* -----------------------------------------------------------------------------
   * substituteClassnameSpecialVariable()
   * ----------------------------------------------------------------------------- */

  void substituteClassnameSpecialVariable(SwigType *classnametype, String *tm, const char *classnamespecialvariable) {
    String *replacementname;
    if (SwigType_isenum(classnametype)) {
      String *enumname = getEnumName(classnametype);
      if (enumname) {
        replacementname = Copy(enumname);
      } else {
        bool anonymous_enum = (Cmp(classnametype, "enum ") == 0);
        if (anonymous_enum) {
          replacementname = NewString("int");
        } else {
          // An unknown enum - one that has not been parsed (neither a C enum forward reference nor a definition) or an ignored enum
          replacementname = NewStringf("SWIGTYPE%s", SwigType_manglestr(classnametype));
          Replace(replacementname, "enum ", "", DOH_REPLACE_ANY);
          Setattr(swig_types_hash, replacementname, classnametype);
        }
      }
    } else {
      String *classname = getProxyName(classnametype);  // getProxyName() works for pointers to classes too
      if (classname) {
        replacementname = Copy(classname);
      } else {
        // use $descriptor if SWIG does not know anything about this type. Note that any typedefs are resolved.
        replacementname = NewStringf("SWIGTYPE%s", SwigType_manglestr(classnametype));

        // Add to hash table so that the type wrapper classes can be created later
        Setattr(swig_types_hash, replacementname, classnametype);
      }
    }
    Replaceall(tm, classnamespecialvariable, replacementname);

    Delete(replacementname);
  }

  /* -----------------------------------------------------------------------------
   * substituteInterfacenameSpecialVariable()
   * ----------------------------------------------------------------------------- */

  void substituteInterfacenameSpecialVariable(SwigType *interfacenametype, String *tm, const char *interfacenamespecialvariable, bool qualified) {

    String *interfacename = getInterfaceName(interfacenametype, qualified);
    if (interfacename) {
      String *replacementname = Copy(interfacename);
      Replaceall(tm, interfacenamespecialvariable, replacementname);
      Delete(replacementname);
    }
  }

  /* -----------------------------------------------------------------------------
   * emitTypeWrapperClass()
   * ----------------------------------------------------------------------------- */

  void emitTypeWrapperClass(String *classname, SwigType *type) {
    Node *n = NewHash();
    Setfile(n, input_file);
    Setline(n, line_number);

    String *swigtype = NewString("");
    File *f_swigtype = getOutputFile(SWIG_output_directory(), classname);

    addOpenNamespace(0, f_swigtype);

    // Pure C# baseclass and interfaces
    const String *pure_baseclass = typemapLookup(n, "csbase", type, WARN_NONE);
    const String *pure_interfaces = typemapLookup(n, "csinterfaces", type, WARN_NONE);

    // Emit the class
    Printv(swigtype,
           typemapLookup(n, "csimports", type, WARN_NONE),  // Import statements
           "\n",
           NIL);

    // Class attributes
    const String *csattributes = typemapLookup(n, "csattributes", type, WARN_NONE);
    if (csattributes && *Char(csattributes))
      Printf(swigtype, "%s\n", csattributes);

    Printv(swigtype,
           typemapLookup(n, "csclassmodifiers", type, WARN_CSHARP_TYPEMAP_CLASSMOD_UNDEF),  // Class modifiers
           " $csclassname",                                                                 // Class name and base class
           (*Char(pure_baseclass) || *Char(pure_interfaces)) ? " : " : "",
           pure_baseclass,
           ((*Char(pure_baseclass)) && *Char(pure_interfaces)) ?  // Interfaces
             ", "
                                                               : "",
           pure_interfaces,
           " {",
           typemapLookup(n, "csbody", type, WARN_CSHARP_TYPEMAP_CSBODY_UNDEF),  // main body of class
           typemapLookup(n, "cscode", type, WARN_NONE),                         // extra C# code
           "}\n",
           NIL);

    Replaceall(swigtype, "$csclassname", classname);
    Replaceall(swigtype, "$module", module_class_name);
    Replaceall(swigtype, "$imclassname", imclass_name);
    Replaceall(swigtype, "$dllimport", dllimport);

    // For unknown enums
    Replaceall(swigtype, "$enumvalues", "");

    Printv(f_swigtype, swigtype, NIL);

    addCloseNamespace(0, f_swigtype);
    if (f_swigtype != f_single_out)
      Delete(f_swigtype);
    f_swigtype = NULL;
    Delete(swigtype);
    Delete(n);
  }

  /* -----------------------------------------------------------------------------
   * typemapLookup()
   * n - for input only and must contain info for Getfile(n) and Getline(n) to work
   * tmap_method - typemap method name
   * type - typemap type to lookup
   * warning - warning number to issue if no typemaps found
   * typemap_attributes - the typemap attributes are attached to this node and will
   *   also be used for temporary storage if non null
   * return is never NULL, unlike Swig_typemap_lookup()
   * ----------------------------------------------------------------------------- */

  const String *typemapLookup(Node *n, const_String_or_char_ptr tmap_method, SwigType *type, int warning, Node *typemap_attributes = 0) {
    Node *node = !typemap_attributes ? NewHash() : typemap_attributes;
    Setattr(node, "type", type);
    Setfile(node, Getfile(n));
    Setline(node, Getline(n));
    const String *tm = Swig_typemap_lookup(tmap_method, node, "", 0);
    if (!tm) {
      tm = empty_string;
      if (warning != WARN_NONE)
        Swig_warning(warning, Getfile(n), Getline(n), "No %s typemap defined for %s\n", tmap_method, SwigType_str(type, 0));
    }
    if (!typemap_attributes)
      Delete(node);
    return tm;
  }

  /* -----------------------------------------------------------------------------
   * typemapExists()
   * n - for input only and must contain info for Getfile(n) and Getline(n) to work
   * tmap_method - typemap method name
   * type - typemap type to lookup
   * returns found typemap or NULL if not found
   * ----------------------------------------------------------------------------- */

  const String *typemapExists(Node *n, const_String_or_char_ptr tmap_method, SwigType *type) {
    Node *node = NewHash();
    Setattr(node, "type", type);
    Setfile(node, Getfile(n));
    Setline(node, Getline(n));
    const String *tm = Swig_typemap_lookup(tmap_method, node, "", 0);
    Delete(node);
    return tm;
  }

  /* -----------------------------------------------------------------------------
   * canThrow()
   * Determine whether the code in the typemap can throw a C# exception.
   * If so, note it for later when excodeSubstitute() is called.
   * ----------------------------------------------------------------------------- */

  void canThrow(Node *n, const String *typemap, Node *parameter) {
    String *canthrow_attribute = NewStringf("tmap:%s:canthrow", typemap);
    String *canthrow = Getattr(parameter, canthrow_attribute);
    if (canthrow)
      Setattr(n, "csharp:canthrow", "1");
    Delete(canthrow_attribute);
  }

  /* -----------------------------------------------------------------------------
   * excodeSubstitute()
   * If a method can throw a C# exception, additional exception code is added to
   * check for the pending exception so that it can then throw the exception. The
   * $excode special variable is replaced by the exception code in the excode
   * typemap attribute.
   * ----------------------------------------------------------------------------- */

  void excodeSubstitute(Node *n, String *code, const String *typemap, Node *parameter) {
    String *excode_attribute = NewStringf("tmap:%s:excode", typemap);
    String *excode = Getattr(parameter, excode_attribute);
    if (Getattr(n, "csharp:canthrow")) {
      int count = Replaceall(code, "$excode", excode);
      if (count < 1 || !excode) {
        Swig_warning(
          WARN_CSHARP_EXCODE, input_file, line_number, "C# exception may not be thrown - no $excode or excode attribute in '%s' typemap.\n", typemap);
      }
    } else {
      Replaceall(code, "$excode", empty_string);
    }
    Delete(excode_attribute);
  }

  /* -----------------------------------------------------------------------------
   * addOpenNamespace()
   * ----------------------------------------------------------------------------- */

  void addOpenNamespace(const String *nspace, File *file) {
    if (namespce || nspace) {
      Printf(file, "namespace ");
      if (namespce)
        Printv(file, namespce, nspace ? "." : "", NIL);
      if (nspace)
        Printv(file, nspace, NIL);
      Printf(file, " {\n");
    }
  }

  /* -----------------------------------------------------------------------------
   * addCloseNamespace()
   * ----------------------------------------------------------------------------- */

  void addCloseNamespace(const String *nspace, File *file) {
    if (namespce || nspace)
      Printf(file, "\n}\n");
  }

  /* -----------------------------------------------------------------------------
   * outputDirectory()
   *
   * Return the directory to use for generating C# classes/enums and create the
   * subdirectory (does not create if language specific outdir does not exist).
   * ----------------------------------------------------------------------------- */

  String *outputDirectory(String *nspace) {
    String *output_directory = Copy(SWIG_output_directory());
    if (nspace) {
      String *nspace_subdirectory = Copy(nspace);
      Replaceall(nspace_subdirectory, ".", SWIG_FILE_DELIMITER);
      String *newdir_error = Swig_new_subdirectory(output_directory, nspace_subdirectory);
      if (newdir_error) {
        Printf(stderr, "%s\n", newdir_error);
        Delete(newdir_error);
        Exit(EXIT_FAILURE);
      }
      Printv(output_directory, nspace_subdirectory, SWIG_FILE_DELIMITER, 0);
      Delete(nspace_subdirectory);
    }
    return output_directory;
  }

  /*----------------------------------------------------------------------
   * Start of director methods
   *--------------------------------------------------------------------*/

#if 0
  /*----------------------------------------------------------------------
   * emitDirectorUpcalls()
   *--------------------------------------------------------------------*/

  void emitDirectorUpcalls() {
    if (n_dmethods) {
      Wrapper *w = NewWrapper();
      String *dmethod_data = NewString("");
      int n_methods = 0;
      Iterator udata_iter;

      udata_iter = First(dmethods_seq);
      while (udata_iter.item) {
        UpcallData *udata = udata_iter.item;
        Printf(dmethod_data, "  { \"%s\", \"%s\" }", Getattr(udata, "imclass_method"), Getattr(udata, "imclass_fdesc"));
        ++n_methods;

        udata_iter = Next(udata_iter);

        if (udata_iter.item)
          Putc(',', dmethod_data);
        Putc('\n', dmethod_data);
      }


      Wrapper_print(w, f_wrappers);
      Delete(dmethod_data);
      Delete(swig_module_init);
      DelWrapper(w);
    }
  }
#endif

  /*----------------------------------------------------------------------
   * emitDirectorExtraMethods()
   *
   * This is where the director connect method is generated.
   *--------------------------------------------------------------------*/
  void emitDirectorExtraMethods(Node *n) {
    if (!Swig_directorclass(n))
      return;

    // Output the director connect method:
    String *norm_name = SwigType_namestr(Getattr(n, "name"));
    String *dirclassname = directorClassName(n);
    String *swig_director_connect = Swig_name_member(getNSpace(), getClassPrefix(), "director_connect");
    String *wname = Swig_name_wrapper(swig_director_connect);
    String *sym_name = Getattr(n, "sym:name");
    String *qualified_classname = Copy(sym_name);
    String *nspace = getNSpace();
    String *dirClassName = directorClassName(n);
    SwigType *smart = Getattr(n, "smart");
    String *smartptr = smart ? SwigType_namestr(smart) : 0;
    if (!GetFlag(n, "feature:flatnested")) {
      for (Node *outer_class = Getattr(n, "nested:outer"); outer_class; outer_class = Getattr(outer_class, "nested:outer")) {

        Push(qualified_classname, ".");
        Push(qualified_classname, Getattr(outer_class, "sym:name"));
      }
    }
    if (nspace)
      Insert(qualified_classname, 0, NewStringf("%s.", nspace));

    Printv(imclass_class_code, "\n  [global::System.Runtime.InteropServices.DllImport(\"", dllimport, "\", EntryPoint=\"", wname, "\")]\n", NIL);
    Printf(imclass_class_code, "  public static extern void %s(global::System.Runtime.InteropServices.HandleRef jarg1", swig_director_connect);

    Wrapper *code_wrap = NewWrapper();
    Printf(code_wrap->def, "SWIGEXPORT void SWIGSTDCALL %s(void *objarg", wname);

    if (smartptr) {
      Printf(code_wrap->code, "  %s *obj = (%s *)objarg;\n", smartptr, smartptr);
      Printf(code_wrap->code, "  // Keep a local instance of the smart pointer around while we are using the raw pointer\n");
      Printf(code_wrap->code, "  // Avoids using smart pointer specific API.\n");
      Printf(code_wrap->code, "  %s *director = static_cast<%s *>(obj->operator->());\n", dirClassName, dirClassName);
    } else {
      Printf(code_wrap->code, "  %s *obj = (%s *)objarg;\n", norm_name, norm_name);
      Printf(code_wrap->code, "  %s *director = static_cast<%s *>(obj);\n", dirClassName, dirClassName);
    }

    Printf(code_wrap->code, "  director->swig_connect_director(");

    for (int i = first_class_dmethod; i < curr_class_dmethod; ++i) {
      UpcallData *udata = Getitem(dmethods_seq, i);
      String *methid = Getattr(udata, "class_methodidx");

      Printf(code_wrap->def, ", ");
      if (i != first_class_dmethod)
        Printf(code_wrap->code, ", ");
      Printf(code_wrap->def, "%s::SWIG_Callback%s_t callback%s", dirclassname, methid, methid);
      Printf(code_wrap->code, "callback%s", methid);
      Printf(imclass_class_code, ", %s.SwigDelegate%s_%s delegate%s", qualified_classname, sym_name, methid, methid);
    }

    Printf(code_wrap->def, ") {\n");
    Printf(code_wrap->code, ");\n");
    Printf(imclass_class_code, ");\n");
    Printf(code_wrap->code, "}\n");

    Wrapper_print(code_wrap, f_wrappers);
    DelWrapper(code_wrap);

    Delete(wname);
    Delete(swig_director_connect);
    Delete(qualified_classname);
    Delete(dirclassname);
  }

  /* ---------------------------------------------------------------
   * classDirectorMethod()
   *
   * Emit a virtual director method to pass a method call on to the
   * underlying C# object.
   *
   * --------------------------------------------------------------- */

  int classDirectorMethod(Node *n, Node *parent, String *super) {
    String *classname = Getattr(parent, "sym:name");
    String *c_classname = Getattr(parent, "name");
    String *name = Getattr(n, "name");
    String *symname = Getattr(n, "sym:name");
    SwigType *returntype = Getattr(n, "type");
    String *overloaded_name = 0;
    String *storage = Getattr(n, "storage");
    String *value = Getattr(n, "value");
    String *decl = Getattr(n, "decl");
    String *declaration = NewString("");
    String *pre_code = NewString("");
    String *post_code = NewString("");
    String *terminator_code = NewString("");
    String *tm;
    Parm *p;
    int i;
    Wrapper *w = NewWrapper();
    ParmList *l = Getattr(n, "parms");
    bool is_void = !(Cmp(returntype, "void"));
    String *qualified_return = 0;
    bool pure_virtual = (!(Cmp(storage, "virtual")) && !(Cmp(value, "0")));
    int status = SWIG_OK;
    bool output_director = true;
    String *dirclassname = directorClassName(parent);
    String *qualified_name = NewStringf("%s::%s", dirclassname, name);
    SwigType *c_ret_type = NULL;
    String *jupcall_args = NewString("");
    String *callback_typedef_parms = NewString("");
    String *delegate_parms = NewString("");
    String *proxy_method_types = NewString("");
    String *callback_def = NewString("");
    String *callback_code = NewString("");
    String *imcall_args = NewString("");
    bool ignored_method = GetFlag(n, "feature:ignore") ? true : false;

    // Kludge Alert: functionWrapper sets sym:overload properly, but it
    // isn't at this point, so we have to manufacture it ourselves. At least
    // we're consistent with the sym:overload name in functionWrapper. (?? when
    // does the overloaded method name get set?)

    if (!ignored_method)
      overloaded_name = getOverloadedName(n);

    qualified_return = SwigType_rcaststr(returntype, "c_result");

    if (!is_void && (!ignored_method || pure_virtual)) {
      if (!SwigType_isclass(returntype)) {
        if (!(SwigType_ispointer(returntype) || SwigType_isreference(returntype))) {
          String *construct_result = NewStringf("= SwigValueInit< %s >()", SwigType_lstr(returntype, 0));
          Wrapper_add_localv(w, "c_result", SwigType_lstr(returntype, "c_result"), construct_result, NIL);
          Delete(construct_result);
        } else {
          String *base_typename = SwigType_base(returntype);
          String *resolved_typename = SwigType_typedef_resolve_all(base_typename);
          Symtab *symtab = Getattr(n, "sym:symtab");
          Node *typenode = Swig_symbol_clookup(resolved_typename, symtab);

          if (SwigType_ispointer(returntype) || (typenode && Getattr(typenode, "abstracts"))) {
            /* initialize pointers to something sane. Same for abstract
               classes when a reference is returned. */
            Wrapper_add_localv(w, "c_result", SwigType_lstr(returntype, "c_result"), "= 0", NIL);
          } else {
            /* If returning a reference, initialize the pointer to a sane
               default - if a C# exception occurs, then the pointer returns
               something other than a NULL-initialized reference. */
            SwigType *noref_type = SwigType_del_reference(Copy(returntype));
            String *noref_ltype = SwigType_lstr(noref_type, 0);
            String *return_ltype = SwigType_lstr(returntype, 0);

            Wrapper_add_localv(w, "result_default", "static", noref_ltype, "result_default", NIL);
            Wrapper_add_localv(w, "c_result", return_ltype, "c_result", NIL);
            Printf(w->code, "result_default = SwigValueInit< %s >();\n", noref_ltype);
            Printf(w->code, "c_result = &result_default;\n");
            Delete(return_ltype);
            Delete(noref_ltype);
            Delete(noref_type);
          }

          Delete(base_typename);
          Delete(resolved_typename);
        }
      } else {
        SwigType *vt;

        vt = cplus_value_type(returntype);
        if (!vt) {
          Wrapper_add_localv(w, "c_result", SwigType_lstr(returntype, "c_result"), NIL);
        } else {
          Wrapper_add_localv(w, "c_result", SwigType_lstr(vt, "c_result"), NIL);
          Delete(vt);
        }
      }
    }

    if (!ignored_method) {
      /* Create the intermediate class wrapper */
      tm = Swig_typemap_lookup("imtype", n, "", 0);
      if (tm) {
        String *imtypeout = Getattr(n, "tmap:imtype:out");  // the type in the imtype typemap's out attribute overrides the type in the typemap
        if (imtypeout)
          tm = imtypeout;
        const String *im_directoroutattributes = Getattr(n, "tmap:imtype:directoroutattributes");
        if (im_directoroutattributes) {
          Printf(callback_def, "  %s\n", im_directoroutattributes);
          if (!ignored_method)
            Printf(director_delegate_definitions, "  %s\n", im_directoroutattributes);
        }

        Printf(callback_def, "  private %s SwigDirectorMethod%s(", tm, overloaded_name);
        const String *csdirectordelegatemodifiers = Getattr(n, "feature:csdirectordelegatemodifiers");
        String *modifiers = (csdirectordelegatemodifiers ? NewStringf("%s%s", csdirectordelegatemodifiers, Len(csdirectordelegatemodifiers) > 0 ? " " : "")
                                                         : NewStringf("public "));
        Printf(director_delegate_definitions, "  %sdelegate %s", modifiers, tm);
        Delete(modifiers);
      } else {
        Swig_warning(WARN_CSHARP_TYPEMAP_CSTYPE_UNDEF, input_file, line_number, "No imtype typemap defined for %s\n", SwigType_str(returntype, 0));
      }
    }

    if ((c_ret_type = Swig_typemap_lookup("ctype", n, "", 0))) {
      if (!is_void && !ignored_method) {
        String *jretval_decl = NewStringf("%s jresult", c_ret_type);
        Wrapper_add_localv(w, "jresult", jretval_decl, "= 0", NIL);
        Delete(jretval_decl);
      }
    } else {
      Swig_warning(WARN_CSHARP_TYPEMAP_CTYPE_UNDEF,
                   input_file,
                   line_number,
                   "No ctype typemap defined for %s for use in %s::%s (skipping director method)\n",
                   SwigType_str(returntype, 0),
                   SwigType_namestr(c_classname),
                   SwigType_namestr(name));
      output_director = false;
    }

    Swig_director_parms_fixup(l);

    /* Attach the standard typemaps */
    Swig_typemap_attach_parms("out", l, 0);
    Swig_typemap_attach_parms("ctype", l, 0);
    Swig_typemap_attach_parms("imtype", l, 0);
    Swig_typemap_attach_parms("cstype", l, 0);
    Swig_typemap_attach_parms("directorin", l, w);
    Swig_typemap_attach_parms("csdirectorin", l, 0);
    Swig_typemap_attach_parms("directorargout", l, w);

    /* Preamble code */
    if (!ignored_method)
      Printf(w->code, "if (!swig_callback%s) {\n", overloaded_name);

    if (!pure_virtual) {
      String *super_call = Swig_method_call(super, l);
      if (is_void) {
        Printf(w->code, "%s;\n", super_call);
        if (!ignored_method)
          Printf(w->code, "return;\n");
      } else {
        Printf(w->code, "return %s;\n", super_call);
      }
      Delete(super_call);
    } else {
      Printf(w->code, "Swig::DirectorPureVirtualException::raise(\"%s::%s\");\n", SwigType_namestr(c_classname), SwigType_namestr(name));
      if (!is_void)
        Printf(w->code, "return %s;", qualified_return);
      else if (!ignored_method)
        Printf(w->code, "return;\n");
    }

    if (!ignored_method)
      Printf(w->code, "} else {\n");

    /* Go through argument list, convert from native to C# */
    for (i = 0, p = l; p; ++i) {
      /* Is this superfluous? */
      while (checkAttribute(p, "tmap:directorin:numinputs", "0")) {
        p = Getattr(p, "tmap:directorin:next");
      }

      SwigType *pt = Getattr(p, "type");
      String *ln = makeParameterName(n, p, i, false);
      String *c_param_type = NULL;
      String *c_decl = NewString("");
      String *arg = NewString("");

      Printf(arg, "j%s", ln);

      /* And add to the upcall args */
      if (i > 0)
        Printf(jupcall_args, ", ");
      Printf(jupcall_args, "%s", arg);

      /* Get parameter's intermediary C type */
      if ((c_param_type = Getattr(p, "tmap:ctype"))) {
        String *ctypeout = Getattr(p, "tmap:ctype:out");  // the type in the ctype typemap's out attribute overrides the type in the typemap
        if (ctypeout)
          c_param_type = ctypeout;

        /* Add to local variables */
        Printf(c_decl, "%s %s", c_param_type, arg);
        if (!ignored_method)
          Wrapper_add_localv(w, arg, c_decl, (!(SwigType_ispointer(pt) || SwigType_isreference(pt)) ? "" : "= 0"), NIL);

        /* Add input marshalling code */
        if ((tm = Getattr(p, "tmap:directorin"))) {

          Setattr(p, "emit:directorinput", arg);
          Replaceall(tm, "$input", arg);
          Replaceall(tm, "$owner", "0");

          if (Len(tm))
            if (!ignored_method)
              Printf(w->code, "%s\n", tm);

          /* Add C type to callback typedef */
          if (i > 0)
            Printf(callback_typedef_parms, ", ");
          Printf(callback_typedef_parms, "%s", c_param_type);

          /* Add parameter to the intermediate class code if generating the
           * intermediate's upcall code */
          if ((tm = Getattr(p, "tmap:imtype"))) {

            String *imtypeout = Getattr(p, "tmap:imtype:out");  // the type in the imtype typemap's out attribute overrides the type in the typemap
            if (imtypeout)
              tm = imtypeout;
            const String *im_directorinattributes = Getattr(p, "tmap:imtype:directorinattributes");

            String *din = Copy(Getattr(p, "tmap:csdirectorin"));

            if (din) {
              Replaceall(din, "$module", module_class_name);
              Replaceall(din, "$imclassname", imclass_name);
              substituteClassname(pt, din);
              Replaceall(din, "$iminput", ln);

              // pre and post attribute support
              String *pre = Getattr(p, "tmap:csdirectorin:pre");
              if (pre) {
                substituteClassname(pt, pre);
                Replaceall(pre, "$iminput", ln);
                if (Len(pre_code) > 0)
                  Printf(pre_code, "\n");
                Printv(pre_code, pre, NIL);
              }
              String *post = Getattr(p, "tmap:csdirectorin:post");
              if (post) {
                substituteClassname(pt, post);
                Replaceall(post, "$iminput", ln);
                if (Len(post_code) > 0)
                  Printf(post_code, "\n");
                Printv(post_code, post, NIL);
              }
              String *terminator = Getattr(p, "tmap:csdirectorin:terminator");
              if (terminator) {
                substituteClassname(pt, terminator);
                Replaceall(terminator, "$iminput", ln);
                if (Len(terminator_code) > 0)
                  Insert(terminator_code, 0, "\n");
                Insert(terminator_code, 0, terminator);
              }

              if (i > 0) {
                Printf(delegate_parms, ", ");
                Printf(proxy_method_types, ", ");
                Printf(imcall_args, ", ");
              }
              Printf(delegate_parms, "%s%s %s", im_directorinattributes ? im_directorinattributes : empty_string, tm, ln);

              if (Cmp(din, ln)) {
                Printv(imcall_args, din, NIL);
              } else
                Printv(imcall_args, ln, NIL);

              /* Get the C# parameter type */
              if ((tm = Getattr(p, "tmap:cstype"))) {
                substituteClassname(pt, tm);
                int flags = DOH_REPLACE_FIRST | DOH_REPLACE_ID_BEGIN | DOH_REPLACE_NOCOMMENT;
                if (Replace(tm, "ref ", "", flags) || Replace(tm, "ref\t", "", flags)) {
                  Printf(proxy_method_types, "typeof(%s).MakeByRefType()", tm);
                } else if (Replace(tm, "out ", "", flags) || Replace(tm, "out\t", "", flags)) {
                  Printf(proxy_method_types, "typeof(%s).MakeByRefType()", tm);
                } else {
                  Printf(proxy_method_types, "typeof(%s)", tm);
                }
              } else {
                Swig_warning(WARN_CSHARP_TYPEMAP_CSWTYPE_UNDEF, input_file, line_number, "No cstype typemap defined for %s\n", SwigType_str(pt, 0));
              }
            } else {
              Swig_warning(WARN_CSHARP_TYPEMAP_CSDIRECTORIN_UNDEF,
                           input_file,
                           line_number,
                           "No csdirectorin typemap defined for %s for use in %s::%s (skipping director method)\n",
                           SwigType_str(pt, 0),
                           SwigType_namestr(c_classname),
                           SwigType_namestr(name));
              output_director = false;
            }
          } else {
            Swig_warning(WARN_CSHARP_TYPEMAP_CSTYPE_UNDEF,
                         input_file,
                         line_number,
                         "No imtype typemap defined for %s for use in %s::%s (skipping director method)\n",
                         SwigType_str(pt, 0),
                         SwigType_namestr(c_classname),
                         SwigType_namestr(name));
            output_director = false;
          }

          p = Getattr(p, "tmap:directorin:next");

        } else {
          Swig_warning(WARN_CSHARP_TYPEMAP_CSDIRECTORIN_UNDEF,
                       input_file,
                       line_number,
                       "No or improper directorin typemap defined for argument %s for use in %s::%s (skipping director method)\n",
                       SwigType_str(pt, 0),
                       SwigType_namestr(c_classname),
                       SwigType_namestr(name));
          p = nextSibling(p);
          output_director = false;
        }
      } else {
        Swig_warning(WARN_CSHARP_TYPEMAP_CTYPE_UNDEF,
                     input_file,
                     line_number,
                     "No ctype typemap defined for %s for use in %s::%s (skipping director method)\n",
                     SwigType_str(pt, 0),
                     SwigType_namestr(c_classname),
                     SwigType_namestr(name));
        output_director = false;
        p = nextSibling(p);
      }

      Delete(ln);
      Delete(arg);
      Delete(c_decl);
    }

    /* header declaration, start wrapper definition */
    String *target;
    SwigType *rtype = Getattr(n, "conversion_operator") ? 0 : Getattr(n, "classDirectorMethods:type");
    target = Swig_method_decl(rtype, decl, qualified_name, l, 0);
    Printf(w->def, "%s", target);
    Delete(qualified_name);
    Delete(target);
    target = Swig_method_decl(rtype, decl, name, l, 1);
    Printf(declaration, "    virtual %s", target);
    Delete(target);

    // Add any exception specifications to the methods in the director class
    if (Getattr(n, "noexcept")) {
      Append(w->def, " noexcept");
      Append(declaration, " noexcept");
    }
    ParmList *throw_parm_list = NULL;
    if ((throw_parm_list = Getattr(n, "throws")) || Getattr(n, "throw")) {
      int gencomma = 0;

      Append(w->def, " throw(");
      Append(declaration, " throw(");

      if (throw_parm_list)
        Swig_typemap_attach_parms("throws", throw_parm_list, 0);
      for (p = throw_parm_list; p; p = nextSibling(p)) {
        if (Getattr(p, "tmap:throws")) {
          if (gencomma++) {
            Append(w->def, ", ");
            Append(declaration, ", ");
          }
          Printf(w->def, "%s", SwigType_str(Getattr(p, "type"), 0));
          Printf(declaration, "%s", SwigType_str(Getattr(p, "type"), 0));
        }
      }

      Append(w->def, ")");
      Append(declaration, ")");
    }

    Append(w->def, " {");
    Append(declaration, ";\n");

    /* Finish off the inherited upcall's definition */

    Printf(callback_def, "%s)", delegate_parms);
    Printf(callback_def, " {\n");

    /* Emit the intermediate class's upcall to the actual class */

    String *upcall = NewStringf("%s(%s)", symname, imcall_args);

    if ((tm = Swig_typemap_lookup("csdirectorout", n, "", 0))) {
      substituteClassname(returntype, tm);
      Replaceall(tm, "$cscall", upcall);
      if (!is_void)
        Insert(tm, 0, "return ");
      Replaceall(tm, "\n ", "\n   ");  // add extra indentation to code in typemap

      // pre and post attribute support
      bool is_pre_code = Len(pre_code) > 0;
      bool is_post_code = Len(post_code) > 0;
      bool is_terminator_code = Len(terminator_code) > 0;
      if (is_pre_code && is_post_code)
        Printf(callback_code, "%s\n    try {\n      %s;\n    } finally {\n%s\n    }\n", pre_code, tm, post_code);
      else if (is_pre_code)
        Printf(callback_code, "%s\n    %s;\n", pre_code, tm);
      else if (is_post_code)
        Printf(callback_code, "    try {\n      %s;\n    } finally {\n%s\n    }\n", tm, post_code);
      else
        Printf(callback_code, "    %s;\n", tm);
      if (is_terminator_code)
        Printv(callback_code, "\n", terminator_code, NIL);
    }

    Printf(callback_code, "  }\n");
    Delete(upcall);

    if (!ignored_method) {
      if (!is_void)
        Printf(w->code, "jresult = (%s) ", c_ret_type);

      Printf(w->code, "swig_callback%s(%s);\n", overloaded_name, jupcall_args);

      if (!is_void) {
        String *jresult_str = NewString("jresult");
        String *result_str = NewString("c_result");

        /* Copy jresult into c_result... */
        if ((tm = Swig_typemap_lookup("directorout", n, result_str, w))) {
          Replaceall(tm, "$input", jresult_str);
          Replaceall(tm, "$result", result_str);
          Printf(w->code, "%s\n", tm);
        } else {
          Swig_warning(WARN_TYPEMAP_DIRECTOROUT_UNDEF,
                       input_file,
                       line_number,
                       "Unable to use return type %s used in %s::%s (skipping director method)\n",
                       SwigType_str(returntype, 0),
                       SwigType_namestr(c_classname),
                       SwigType_namestr(name));
          output_director = false;
        }

        Delete(jresult_str);
        Delete(result_str);
      }

      /* Marshal outputs */
      for (p = l; p;) {
        if ((tm = Getattr(p, "tmap:directorargout"))) {
          canThrow(n, "directorargout", p);
          Replaceall(tm, "$result", "jresult");
          Replaceall(tm, "$input", Getattr(p, "emit:directorinput"));
          Printv(w->code, tm, "\n", NIL);
          p = Getattr(p, "tmap:directorargout:next");
        } else {
          p = nextSibling(p);
        }
      }

      /* Terminate wrapper code */
      Printf(w->code, "}\n");
      if (!is_void)
        Printf(w->code, "return %s;", qualified_return);
    }

    Printf(w->code, "}");

    // We expose virtual protected methods via an extra public inline method which makes a straight call to the wrapped class' method
    String *inline_extra_method = NewString("");
    if (dirprot_mode() && !is_public(n) && !pure_virtual) {
      Printv(inline_extra_method, declaration, NIL);
      String *extra_method_name = NewStringf("%sSwigPublic", name);
      Replaceall(inline_extra_method, name, extra_method_name);
      Replaceall(inline_extra_method, ";\n", " {\n      ");
      if (!is_void)
        Printf(inline_extra_method, "return ");
      String *methodcall = Swig_method_call(super, l);
      Printv(inline_extra_method, methodcall, ";\n    }\n", NIL);
      Delete(methodcall);
      Delete(extra_method_name);
    }

    /* emit the director method */
    if (status == SWIG_OK && output_director) {
      if (!is_void) {
        Replaceall(w->code, "$null", qualified_return);
      } else {
        Replaceall(w->code, "$null", "");
      }
      Replaceall(w->code, "$isvoid", is_void ? "1" : "0");
      if (!ignored_method)
        Printv(director_delegate_callback, "\n", callback_def, callback_code, NIL);
      if (!Getattr(n, "defaultargs")) {
        Replaceall(w->code, "$symname", symname);
        Wrapper_print(w, f_directors);
        Printv(f_directors_h, declaration, NIL);
        Printv(f_directors_h, inline_extra_method, NIL);
      }
    }

    if (!ignored_method) {
      /* Emit the actual upcall through */
      String *member_name = Swig_name_member(getNSpace(), getClassPrefix(), overloaded_name);
      String *imclass_dmethod = NewStringf("SwigDirector_%s", member_name);
      UpcallData *udata = addUpcallMethod(imclass_dmethod, symname, decl, overloaded_name);
      String *methid = Getattr(udata, "class_methodidx");
      Setattr(n, "upcalldata", udata);
      /*
      Printf(stdout, "setting upcalldata, nodeType: %s %s::%s %p\n", nodeType(n), classname, Getattr(n, "name"), n);
      */

      Printf(director_callback_typedefs, "    typedef %s (SWIGSTDCALL* SWIG_Callback%s_t)(", c_ret_type, methid);
      Printf(director_callback_typedefs, "%s);\n", callback_typedef_parms);
      Printf(director_callbacks, "    SWIG_Callback%s_t swig_callback%s;\n", methid, overloaded_name);

      Printf(director_delegate_definitions, " SwigDelegate%s_%s(%s);\n", classname, methid, delegate_parms);
      Printf(director_delegate_instances, "  private SwigDelegate%s_%s swigDelegate%s;\n", classname, methid, methid);
      Printf(
        director_method_types, "  private static global::System.Type[] swigMethodTypes%s = new global::System.Type[] { %s };\n", methid, proxy_method_types);
      Printf(director_connect_parms, "SwigDirector%s%s delegate%s", classname, methid, methid);

      Delete(imclass_dmethod);
      Delete(member_name);
    }

    Delete(pre_code);
    Delete(post_code);
    Delete(terminator_code);
    Delete(qualified_return);
    Delete(declaration);
    Delete(callback_typedef_parms);
    Delete(delegate_parms);
    Delete(proxy_method_types);
    Delete(callback_def);
    Delete(callback_code);
    Delete(dirclassname);
    DelWrapper(w);

    return status;
  }

  /* ------------------------------------------------------------
   * classDirectorConstructor()
   * ------------------------------------------------------------ */

  int classDirectorConstructor(Node *n) {
    Node *parent = parentNode(n);
    String *decl = Getattr(n, "decl");
    String *supername = Swig_class_name(parent);
    String *dirclassname = directorClassName(parent);
    String *sub = NewString("");
    Parm *p;
    ParmList *superparms = Getattr(n, "parms");
    ParmList *parms;
    int argidx = 0;

    /* Assign arguments to superclass's parameters, if not already done */
    for (p = superparms; p; p = nextSibling(p)) {
      String *pname = Getattr(p, "name");

      if (!pname) {
        pname = NewStringf("arg%d", argidx++);
        Setattr(p, "name", pname);
      }
    }

    // TODO: Is this copy needed?
    parms = CopyParmList(superparms);

    if (!Getattr(n, "defaultargs")) {
      /* constructor */
      {
        String *basetype = Getattr(parent, "classtype");
        String *target = Swig_method_decl(0, decl, dirclassname, parms, 0);
        String *call = Swig_csuperclass_call(0, basetype, superparms);

        Printf(f_directors, "%s::%s : %s, %s {\n", dirclassname, target, call, Getattr(parent, "director:ctor"));
        Printf(f_directors, "  swig_init_callbacks();\n");
        Printf(f_directors, "}\n\n");

        Delete(target);
        Delete(call);
      }

      /* constructor header */
      {
        String *target = Swig_method_decl(0, decl, dirclassname, parms, 1);
        Printf(f_directors_h, "    %s;\n", target);
        Delete(target);
      }
    }

    Delete(sub);
    Delete(supername);
    Delete(parms);
    Delete(dirclassname);
    return Language::classDirectorConstructor(n);
  }

  /* ------------------------------------------------------------
   * classDirectorDefaultConstructor()
   * ------------------------------------------------------------ */

  int classDirectorDefaultConstructor(Node *n) {
    String *dirclassname = directorClassName(n);
    String *classtype = SwigType_namestr(Getattr(n, "name"));
    Wrapper *w = NewWrapper();

    Printf(w->def, "%s::%s() : %s {", dirclassname, dirclassname, Getattr(n, "director:ctor"));
    Printf(w->code, "}\n");
    Wrapper_print(w, f_directors);

    Printf(f_directors_h, "    %s();\n", dirclassname);
    DelWrapper(w);
    Delete(classtype);
    Delete(dirclassname);
    return Language::classDirectorDefaultConstructor(n);
  }

  /* ------------------------------------------------------------
   * classDirectorInit()
   * ------------------------------------------------------------ */

  int classDirectorInit(Node *n) {
    Delete(none_comparison);
    none_comparison = NewString("");  // not used

    Delete(director_ctor_code);
    director_ctor_code = NewString("$director_new");

    directorDeclaration(n);

    Printf(f_directors_h, "%s {\n", Getattr(n, "director:decl"));
    Printf(f_directors_h, "\npublic:\n");

    /* Keep track of the director methods for this class */
    first_class_dmethod = curr_class_dmethod = n_dmethods;

    director_callback_typedefs = NewString("");
    director_callbacks = NewString("");
    director_delegate_callback = NewString("");
    director_delegate_definitions = NewString("");
    director_delegate_instances = NewString("");
    director_method_types = NewString("");
    director_connect_parms = NewString("");

    return Language::classDirectorInit(n);
  }

  int classDeclaration(Node *n) {
    String *old_director_callback_typedefs = director_callback_typedefs;
    String *old_director_callbacks = director_callbacks;
    String *old_director_delegate_callback = director_delegate_callback;
    String *old_director_delegate_definitions = director_delegate_definitions;
    String *old_director_delegate_instances = director_delegate_instances;
    String *old_director_method_types = director_method_types;
    String *old_director_connect_parms = director_connect_parms;

    int ret = Language::classDeclaration(n);

    // these variables are deleted in emitProxyClassDefAndCPPCasts, hence no Delete here
    director_callback_typedefs = old_director_callback_typedefs;
    director_callbacks = old_director_callbacks;
    director_delegate_callback = old_director_delegate_callback;
    director_delegate_definitions = old_director_delegate_definitions;
    director_delegate_instances = old_director_delegate_instances;
    director_method_types = old_director_method_types;
    director_connect_parms = old_director_connect_parms;

    return ret;
  }

  /* ----------------------------------------------------------------------
   * classDirectorDestructor()
   * ---------------------------------------------------------------------- */

  int classDirectorDestructor(Node *n) {
    Node *current_class = getCurrentClass();
    String *dirclassname = directorClassName(current_class);
    Wrapper *w = NewWrapper();

    if (Getattr(n, "noexcept")) {
      Printf(f_directors_h, "    virtual ~%s() noexcept;\n", dirclassname);
      Printf(w->def, "%s::~%s() noexcept {\n", dirclassname, dirclassname);
    } else if (Getattr(n, "throw")) {
      Printf(f_directors_h, "    virtual ~%s() throw();\n", dirclassname);
      Printf(w->def, "%s::~%s() throw() {\n", dirclassname, dirclassname);
    } else {
      Printf(f_directors_h, "    virtual ~%s();\n", dirclassname);
      Printf(w->def, "%s::~%s() {\n", dirclassname, dirclassname);
    }

    Printv(w->code, "}\n", NIL);

    Wrapper_print(w, f_directors);

    DelWrapper(w);
    Delete(dirclassname);
    return SWIG_OK;
  }

  /* ------------------------------------------------------------
   * classDirectorEnd()
   * ------------------------------------------------------------ */

  int classDirectorEnd(Node *n) {
    int i;
    String *dirclassname = directorClassName(n);

    Wrapper *w = NewWrapper();

    if (Len(director_callback_typedefs) > 0) {
      Printf(f_directors_h, "\n%s", director_callback_typedefs);
    }

    Printf(f_directors_h, "    void swig_connect_director(");

    Printf(w->def, "void %s::swig_connect_director(", dirclassname);

    for (i = first_class_dmethod; i < curr_class_dmethod; ++i) {
      UpcallData *udata = Getitem(dmethods_seq, i);
      String *methid = Getattr(udata, "class_methodidx");
      String *overname = Getattr(udata, "overname");

      Printf(f_directors_h, "SWIG_Callback%s_t callback%s", methid, overname);
      Printf(w->def, "SWIG_Callback%s_t callback%s", methid, overname);
      Printf(w->code, "swig_callback%s = callback%s;\n", overname, overname);
      if (i != curr_class_dmethod - 1) {
        Printf(f_directors_h, ", ");
        Printf(w->def, ", ");
      }
    }

    Printf(f_directors_h, ");\n");
    Printf(w->def, ") {");

    if (Len(director_callbacks) > 0) {
      Printf(f_directors_h, "\nprivate:\n%s", director_callbacks);
    }
    Printf(f_directors_h, "    void swig_init_callbacks();\n");
    Printf(f_directors_h, "};\n\n");
    Printf(w->code, "}\n\n");

    Printf(w->code, "void %s::swig_init_callbacks() {\n", dirclassname);
    for (i = first_class_dmethod; i < curr_class_dmethod; ++i) {
      UpcallData *udata = Getitem(dmethods_seq, i);
      String *overname = Getattr(udata, "overname");
      Printf(w->code, "swig_callback%s = 0;\n", overname);
    }
    Printf(w->code, "}");

    Wrapper_print(w, f_directors);

    DelWrapper(w);
    Delete(dirclassname);

    return Language::classDirectorEnd(n);
  }

  /* --------------------------------------------------------------------
   * classDirectorDisown()
   * ------------------------------------------------------------------*/
  virtual int classDirectorDisown(Node *n) {
    (void)n;
    return SWIG_OK;
  }

  /*----------------------------------------------------------------------
   * extraDirectorProtectedCPPMethodsRequired()
   *--------------------------------------------------------------------*/

  bool extraDirectorProtectedCPPMethodsRequired() const {
    return false;
  }

  /*----------------------------------------------------------------------
   * directorDeclaration()
   *
   * Generate the director class's declaration
   * e.g. "class SwigDirector_myclass : public myclass, public Swig::Director {"
   *--------------------------------------------------------------------*/

  void directorDeclaration(Node *n) {

    String *base = Getattr(n, "classtype");
    String *class_ctor = NewString("Swig::Director()");

    String *dirclassname = directorClassName(n);
    String *declaration = Swig_class_declaration(n, dirclassname);

    Printf(declaration, " : public %s, public Swig::Director", base);

    // Stash stuff for later.
    Setattr(n, "director:decl", declaration);
    Setattr(n, "director:ctor", class_ctor);

    Delete(dirclassname);
  }

  /* ------------------------------------------------------------
   * have_docstring()
   *
   * Check for Doxygen comments
   *--------------------------------------------------------------------*/

  bool have_docstring(Node *n) {
    /* autodoc and docstring features not supported in C#
    String *str = Getattr(n, "feature:docstring");

    return ((str && Len(str) > 0)
        || (Getattr(n, "feature:autodoc") && !GetFlag(n, "feature:noautodoc"))
        || (doxygen && doxygenTranslator->hasDocumentation(n))
        );
    */
    return doxygen && doxygenTranslator->hasDocumentation(n);
  }

  /* ------------------------------------------------------------
   * docstring()
   *
   * Get documentation comments, if any
   *
   * Return new documentation string to be deleted by caller (never NULL but
   * may be empty if there is no docstring).
   *--------------------------------------------------------------------*/

  String *docstring(Node *n, const char *indent = "") {
    String *docstr = NULL;

    if (doxygen && doxygenTranslator->hasDocumentation(n)) {
      docstr = doxygenTranslator->getDocumentation(n, 0);
    }

    if (!docstr)
      docstr = NewString("");

    // If there is more than one line then make docstrings like this:
    //
    //      This is line1
    //      And here is line2 followed by the rest of them
    //
    // otherwise, put it all on a single line
    if (Strchr(docstr, '\n')) {
      String *tmp = NewString("");
      Append(tmp, indent_docstring(docstr, indent));
      Delete(docstr);
      docstr = tmp;
    } else {
      String *tmp = NewString(indent);
      Append(tmp, "/// ");
      Append(tmp, docstr);
      Append(tmp, "\n");
      Delete(docstr);
      docstr = tmp;
    }

    return docstr;
  }

  /* ------------------------------------------------------------
   * indent_docstring()
   *
   * Format (indent) a CSharp docstring.
   * Remove leading whitespace from 'code' and re-indent using
   * the indentation string in 'indent'.
   * ------------------------------------------------------------ */

  String *indent_docstring(const String *code, const char *indent) {
    String *out = NewString("");
    String *temp;
    char *t;
    if (!indent)
      indent = "";

    temp = NewString(code);

    t = Char(temp);
    if (*t == '{') {
      Delitem(temp, 0);
      Delitem(temp, DOH_END);
    }

    /* Split the input text into lines */
    List *clist = SplitLines(temp);
    Delete(temp);

    Iterator si;

    int truncate_characters_count = INT_MAX;
    for (si = First(clist); si.item; si = Next(si)) {
      const char *c = Char(si.item);
      int i;
      for (i = 0; isspace((unsigned char)c[i]); i++) {
        // Scan forward until we find a non-space (which may be a null byte).
      }
      char ch = c[i];
      if (ch) {
        // Found a line which isn't just whitespace
        if (i < truncate_characters_count)
          truncate_characters_count = i;
      }
    }

    if (truncate_characters_count == INT_MAX)
      truncate_characters_count = 0;

    for (si = First(clist); si.item; si = Next(si)) {
      const char *c = Char(si.item);

      int i;
      for (i = 0; isspace((unsigned char)c[i]); i++) {
        // Scan forward until we find a non-space (which may be a null byte).
      }
      char ch = c[i];
      if (!ch) {
        // Line is just whitespace - emit an empty line.
        Printv(out, indent, "///", NIL);
        Putc('\n', out);
        continue;
      }

      Printv(out, indent, "/// ", c + truncate_characters_count, "\n", NIL);
    }
    Delete(clist);
    return out;
  }

  /*----------------------------------------------------------------------
   * nestedClassesSupport()
   *--------------------------------------------------------------------*/

  NestedClassSupport nestedClassesSupport() const {
    return NCS_Full;
  }
}; /* class CSHARP */

/* -----------------------------------------------------------------------------
 * swig_csharp()    - Instantiate module
 * ----------------------------------------------------------------------------- */

static Language *new_swig_csharp() {
  return new CSHARP();
}
extern "C" Language *swig_csharp(void) {
  return new_swig_csharp();
}

/* -----------------------------------------------------------------------------
 * Static member variables
 * ----------------------------------------------------------------------------- */

const char *CSHARP::usage = "\
C# Options (available with -csharp)\n\
     -dllimport <dl> - Override DllImport attribute name to <dl>\n\
     -namespace <nm> - Generate wrappers into C# namespace <nm>\n\
     -noproxy        - Generate the low-level functional interface instead\n\
                       of proxy classes\n\
     -oldvarnames    - Old intermediary method names for variable wrappers\n\
     -outfile <file> - Write all C# into a single <file> located in the output directory\n\
\n";
