/* ----------------------------------------------------------------------------- 
 * 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 http://www.swig.org/legal.html.
 *
 * typepass.cxx
 *
 * This module builds all of the internal type information by collecting
 * typedef declarations as well as registering classes, structures, and unions.
 * This information is needed to correctly handle shadow classes and other
 * advanced features.   This phase of compilation is also used to perform
 * type-expansion.  All types are fully qualified with namespace prefixes
 * and other information needed for compilation.
 * ----------------------------------------------------------------------------- */

#include "swigmod.h"
#include "cparse.h"

struct normal_node {
  Symtab *symtab;
  Hash *typescope;
  List *normallist;
  normal_node *next;
};

static normal_node *patch_list = 0;

/* Singleton class - all non-static methods in this class are private */
class TypePass:private Dispatcher {
  Node *inclass;
  Node *module;
  int importmode;
  String *nsname;
  String *nssymname;
  Hash *classhash;
  List *normalize;

  TypePass() :
    inclass(0),
    module(0),
    importmode(0),
    nsname(0),
    nssymname(0),
    classhash(0),
    normalize(0) {
  }

  /* Normalize a type. Replaces type with fully qualified version */
  void normalize_type(SwigType *ty) {
    SwigType *qty;
    if (CPlusPlus) {
      Replaceall(ty, "struct ", "");
      Replaceall(ty, "union ", "");
      Replaceall(ty, "class ", "");
    }

    qty = SwigType_typedef_qualified(ty);
    /*    Printf(stdout,"%s --> %s\n", ty, qty); */
    Clear(ty);
    Append(ty, qty);
    Delete(qty);
  }

  /* Normalize a parameter list */

  void normalize_parms(ParmList *p) {
    while (p) {
      SwigType *ty = Getattr(p, "type");
      normalize_type(ty);
      /* This is a check for a function type */
      {
	SwigType *qty = SwigType_typedef_resolve_all(ty);
	if (SwigType_isfunction(qty)) {
	  SwigType_add_pointer(ty);
	}
	Delete(qty);
      }

      String *value = Getattr(p, "value");
      if (value) {
	Node *n = Swig_symbol_clookup(value, 0);
	if (n) {
	  String *q = Swig_symbol_qualified(n);
	  if (q && Len(q)) {
	    String *vb = Swig_scopename_last(value);
	    Clear(value);
	    Printf(value, "%s::%s", SwigType_namestr(q), vb);
	    Delete(q);
	  }
	}
      }
      if (value && SwigType_istemplate(value)) {
	String *nv = SwigType_namestr(value);
	Setattr(p, "value", nv);
      }
      p = nextSibling(p);
    }
  }

  void normalize_later(ParmList *p) {
    while (p) {
      SwigType *ty = Getattr(p, "type");
      Append(normalize, ty);
      p = nextSibling(p);
    }
  }

  /* Walk through entries in normalize list and patch them up */
  void normalize_list() {
    Hash *currentsym = Swig_symbol_current();

    normal_node *nn = patch_list;
    normal_node *np;
    while (nn) {
      Swig_symbol_setscope(nn->symtab);
      SwigType_set_scope(nn->typescope);
      Iterator t;
      for (t = First(nn->normallist); t.item; t = Next(t)) {
	normalize_type(t.item);
      }
      Delete(nn->normallist);
      np = nn->next;
      delete(nn);
      nn = np;
    }
    Swig_symbol_setscope(currentsym);
  }

  /* generate C++ inheritance type-relationships */
  void cplus_inherit_types_impl(Node *first, Node *cls, String *clsname, const char *bases, const char *baselist, int ispublic, String *cast = 0) {

    if (first == cls)
      return;			/* The Marcelo check */
    if (!cls)
      cls = first;
    List *alist = 0;
    List *ilist = Getattr(cls, bases);
    if (!ilist) {
      List *nlist = Getattr(cls, baselist);
      if (nlist) {
	int len = Len(nlist);
	int i;
	for (i = 0; i < len; i++) {
	  Node *bcls = 0;
	  int clsforward = 0;
	  String *bname = Getitem(nlist, i);
	  String *sname = bname;
	  String *tname = 0;

	  /* Try to locate the base class.   We look in the symbol table and we chase 
	     typedef declarations to get to the base class if necessary */
	  Symtab *st = Getattr(cls, "sym:symtab");

	  if (SwigType_istemplate(bname)) {
	    tname = SwigType_typedef_resolve_all(bname);
	    sname = tname;
	  }
	  while (1) {
	    String *qsname = SwigType_typedef_qualified(sname);
	    bcls = Swig_symbol_clookup(qsname, st);
	    Delete(qsname);
	    if (bcls) {
	      if (Strcmp(nodeType(bcls), "class") != 0) {
		/* Not a class.   The symbol could be a typedef. */
		if (checkAttribute(bcls, "storage", "typedef")) {
		  SwigType *decl = Getattr(bcls, "decl");
		  if (!decl || !(Len(decl))) {
		    sname = Getattr(bcls, "type");
		    st = Getattr(bcls, "sym:symtab");
		    if (SwigType_istemplate(sname)) {
		      if (tname)
			Delete(tname);
		      tname = SwigType_typedef_resolve_all(sname);
		      sname = tname;
		    }
		    continue;
		  }
		  // A case when both outer and nested classes inherit from the same parent. Constructor may be found instead of the class itself.
		} else if (GetFlag(cls, "nested") && checkAttribute(bcls, "nodeType", "constructor")) {
		  bcls = Getattr(bcls, "parentNode");
		  if (Getattr(bcls, "typepass:visit")) {
		    if (!Getattr(bcls, "feature:onlychildren")) {
		      if (!ilist)
			ilist = alist = NewList();
		      Append(ilist, bcls);
		    } else {
		      if (!GetFlag(bcls, "feature:ignore")) {
			Swig_warning(WARN_TYPE_UNDEFINED_CLASS, Getfile(bname), Getline(bname), "Base class '%s' has no name as it is an empty template instantiated with '%%template()'. Ignored.\n", SwigType_namestr(bname));
			Swig_warning(WARN_TYPE_UNDEFINED_CLASS, Getfile(bcls), Getline(bcls), "The %%template directive must be written before '%s' is used as a base class and be declared with a name.\n", SwigType_namestr(bname));
		      }
		    }
		  }
		  break;
		}
		if (Strcmp(nodeType(bcls), "classforward") != 0) {
		  Swig_error(Getfile(bname), Getline(bname), "'%s' is not a valid base class.\n", SwigType_namestr(bname));
		  Swig_error(Getfile(bcls), Getline(bcls), "See definition of '%s'.\n", SwigType_namestr(bname));
		} else {
		  Swig_warning(WARN_TYPE_INCOMPLETE, Getfile(bname), Getline(bname), "Base class '%s' is incomplete.\n", SwigType_namestr(bname));
		  Swig_warning(WARN_TYPE_INCOMPLETE, Getfile(bcls), Getline(bcls), "Only forward declaration '%s' was found.\n", SwigType_namestr(bname));
		  clsforward = 1;
		}
		bcls = 0;
	      } else {
		if (Getattr(bcls, "typepass:visit")) {
		  if (!Getattr(bcls, "feature:onlychildren")) {
		    if (!ilist)
		      ilist = alist = NewList();
		    Append(ilist, bcls);
		  } else {
		    if (!GetFlag(bcls, "feature:ignore")) {
		      Swig_warning(WARN_TYPE_UNDEFINED_CLASS, Getfile(bname), Getline(bname), "Base class '%s' has no name as it is an empty template instantiated with '%%template()'. Ignored.\n", SwigType_namestr(bname));
		      Swig_warning(WARN_TYPE_UNDEFINED_CLASS, Getfile(bcls), Getline(bcls), "The %%template directive must be written before '%s' is used as a base class and be declared with a name.\n", SwigType_namestr(bname));
		    }
		  }
		} else {
		  Swig_warning(WARN_TYPE_UNDEFINED_CLASS, Getfile(bname), Getline(bname), "Base class '%s' undefined.\n", SwigType_namestr(bname));
		  Swig_warning(WARN_TYPE_UNDEFINED_CLASS, Getfile(bcls), Getline(bcls), "'%s' must be defined before it is used as a base class.\n", SwigType_namestr(bname));
		}
	      }
	    }
	    break;
	  }

	  if (tname)
	    Delete(tname);
	  if (!bcls) {
	    if (!clsforward && !GetFlag(cls, "feature:ignore")) {
	      if (ispublic && !Getmeta(bname, "already_warned")) {
		Swig_warning(WARN_TYPE_UNDEFINED_CLASS, Getfile(bname), Getline(bname), "Nothing known about base class '%s'. Ignored.\n", SwigType_namestr(bname));
		if (Strchr(bname, '<')) {
		  Swig_warning(WARN_TYPE_UNDEFINED_CLASS, Getfile(bname), Getline(bname), "Maybe you forgot to instantiate '%s' using %%template.\n", SwigType_namestr(bname));
		}
		Setmeta(bname, "already_warned", "1");
	      }
	    }
	    SwigType_inherit(clsname, bname, cast, 0);
	  }
	}
      }
      if (ilist) {
	Setattr(cls, bases, ilist);
      }
    }
    if (alist)
      Delete(alist);

    if (!ilist)
      return;
    int len = Len(ilist);
    int i;
    for (i = 0; i < len; i++) {
      Node *n = Getitem(ilist, i);
      String *bname = Getattr(n, "name");
      Node *bclass = n;		/* Getattr(n,"class"); */
      Hash *scopes = Getattr(bclass, "typescope");
      SwigType_inherit(clsname, bname, cast, 0);
      if (ispublic && !GetFlag(bclass, "feature:ignore")) {
	String *smartptr = Getattr(first, "feature:smartptr");
	if (smartptr) {
	  SwigType *smart = Swig_cparse_smartptr(first);
	  if (smart) {
	    /* Record a (fake) inheritance relationship between smart pointer
	       and smart pointer to base class, so that smart pointer upcasts
	       are automatically generated. */
	    SwigType *bsmart = Copy(smart);

	    // TODO: SwigType_typedef_resolve_all on a String instead of SwigType is incorrect for templates
	    SwigType *rclsname = SwigType_typedef_resolve_all(clsname);
	    SwigType *rbname = SwigType_typedef_resolve_all(bname);
	    int replace_count = Replaceall(bsmart, rclsname, rbname);
	    if (replace_count == 0) {
	      // If no replacement made, it will be because rclsname is fully resolved, but the
	      // type in the smartptr feature used a typedef or not fully resolved name.
	      String *firstname = Getattr(first, "name");
	      Replaceall(bsmart, firstname, rbname);
	    }
	    // The code above currently creates a smartptr of the base class by substitution, replacing Derived
	    // with Base resulting in something like: 'smartptr< Derived >' from 'smartptr< Base >'. Instead
	    // the feature:smartptr should be used as it also contains 'smartptr< Base >' as specified by the user.
	    // A similar fix should also be done in upcastsCode in java.cxx, csharp.cxx and writeClassUpcast in d.cxx.
	    // Printf(stdout, "smartcomparison %s <=> %s\n", SwigType_namestr(bsmart), Getattr(bclass, "feature:smartptr"));

	    Delete(rclsname);
	    Delete(rbname);
	    String *smartnamestr = SwigType_namestr(smart);
	    String *bsmartnamestr = SwigType_namestr(bsmart);
	    /* construct casting code */
	    String *convcode = NewStringf("\n    *newmemory = SWIG_CAST_NEW_MEMORY;\n    return (void *) new %s(*(%s *)$from);\n", bsmartnamestr, smartnamestr);
	    Delete(bsmartnamestr);
	    Delete(smartnamestr);
	    /* setup inheritance relationship between smart pointer templates */
	    SwigType_inherit(smart, bsmart, 0, convcode);
	    if (!GetFlag(bclass, "feature:smartptr"))
	      Swig_warning(WARN_LANG_SMARTPTR_MISSING, Getfile(first), Getline(first), "Base class '%s' of '%s' is not similarly marked as a smart pointer.\n", SwigType_namestr(Getattr(bclass, "name")), SwigType_namestr(Getattr(first, "name")));
	    Delete(convcode);
	    Delete(bsmart);
	  }
	  Delete(smart);
	} else {
	  if (GetFlag(bclass, "feature:smartptr"))
	    Swig_warning(WARN_LANG_SMARTPTR_MISSING, Getfile(first), Getline(first), "Derived class '%s' of '%s' is not similarly marked as a smart pointer.\n", SwigType_namestr(Getattr(first, "name")), SwigType_namestr(Getattr(bclass, "name")));
	}
      }
      if (!importmode) {
	String *btype = Copy(bname);
	SwigType_add_pointer(btype);
	SwigType_remember(btype);
	Delete(btype);
      }
      if (scopes) {
	SwigType_inherit_scope(scopes);
      }
      /* Set up inheritance in the symbol table */
      Symtab *st = Getattr(cls, "symtab");
      Symtab *bst = Getattr(bclass, "symtab");
      if (st == bst) {
	Swig_warning(WARN_PARSE_REC_INHERITANCE, Getfile(cls), Getline(cls), "Recursive scope inheritance of '%s'.\n", SwigType_namestr(Getattr(cls, "name")));
	continue;
      }
      Symtab *s = Swig_symbol_current();
      Swig_symbol_setscope(st);
      Swig_symbol_inherit(bst);
      Swig_symbol_setscope(s);

      /* Recursively hit base classes */
      String *namestr = SwigType_namestr(Getattr(bclass, "name"));
      String *newcast = NewStringf("(%s *)%s", namestr, cast);
      Delete(namestr);
      cplus_inherit_types_impl(first, bclass, clsname, bases, baselist, ispublic, newcast);
      Delete(newcast);
    }
  }

  void append_list(List *lb, List *la) {
    if (la && lb) {
      for (Iterator bi = First(la); bi.item; bi = Next(bi)) {
	Append(lb, bi.item);
      }
    }
  }

  void cplus_inherit_types(Node *first, Node *cls, String *clsname, String *cast = 0) {
    cplus_inherit_types_impl(first, cls, clsname, "bases", "baselist", 1, cast);
    cplus_inherit_types_impl(first, cls, clsname, "protectedbases", "protectedbaselist", 0, cast);
    cplus_inherit_types_impl(first, cls, clsname, "privatebases", "privatebaselist", 0, cast);

    if (!cls)
      cls = first;

    List *allbases = NewList();
    append_list(allbases, Getattr(cls, "bases"));
    append_list(allbases, Getattr(cls, "protectedbases"));
    append_list(allbases, Getattr(cls, "privatebases"));
    if (Len(allbases)) {
      Setattr(cls, "allbases", allbases);
    }
    Delete(allbases);
  }

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

  virtual int top(Node *n) {
    importmode = 0;
    module = Getattr(n, "module");
    inclass = 0;
    normalize = 0;
    nsname = 0;
    nssymname = 0;
    classhash = Getattr(n, "classes");
    emit_children(n);
    normalize_list();
    SwigType_set_scope(0);
    return SWIG_OK;
  }


  /* ------------------------------------------------------------
   * moduleDirective()
   * ------------------------------------------------------------ */

  virtual int moduleDirective(Node *n) {
    if (!module) {
      module = n;
    }
    return SWIG_OK;
  }

  /* ------------------------------------------------------------
   * importDirective()
   * ------------------------------------------------------------ */

  virtual int importDirective(Node *n) {
    String *oldmodule = module;
    int oldimport = importmode;
    importmode = 1;
    module = 0;
    emit_children(n);
    importmode = oldimport;
    module = oldmodule;
    return SWIG_OK;
  }

  /* ------------------------------------------------------------
   * includeDirective()
   * externDirective()
   * extendDirective()
   * ------------------------------------------------------------ */

  virtual int includeDirective(Node *n) {
    return emit_children(n);
  }
  virtual int externDeclaration(Node *n) {
    return emit_children(n);
  }
  virtual int extendDirective(Node *n) {
    return emit_children(n);
  }

  /* ------------------------------------------------------------
   * classDeclaration()
   * ------------------------------------------------------------ */

  virtual int classDeclaration(Node *n) {
    String *name = Getattr(n, "name");
    String *tdname = Getattr(n, "tdname");
    String *unnamed = Getattr(n, "unnamed");
    String *storage = Getattr(n, "storage");
    String *kind = Getattr(n, "kind");
    save_value<Node*> oldinclass(inclass);
    List *olist = normalize;
    Symtab *symtab;
    String *nname = 0;
    String *fname = 0;
    String *scopename = 0;
    String *template_default_expanded = 0;

    normalize = NewList();

    if (name) {
      if (SwigType_istemplate(name)) {
	// We need to fully resolve the name and expand default template parameters to make templates work correctly */
	Node *cn;
	SwigType *resolved_name = SwigType_typedef_resolve_all(name);
	SwigType *deftype_name = Swig_symbol_template_deftype(resolved_name, 0);
	fname = Copy(resolved_name);
	if (!Equal(resolved_name, deftype_name))
	  template_default_expanded = Copy(deftype_name);
	if (!Equal(fname, name) && (cn = Swig_symbol_clookup_local(fname, 0))) {
	  if ((n == cn)
	      || (Strcmp(nodeType(cn), "template") == 0)
	      || (Getattr(cn, "feature:onlychildren") != 0)
	      || (Getattr(n, "feature:onlychildren") != 0)) {
	    Swig_symbol_cadd(fname, n);
	    if (template_default_expanded)
	      Swig_symbol_cadd(template_default_expanded, n);
	    SwigType_typedef_class(fname);
	    scopename = Copy(fname);
	  } else {
	    Swig_warning(WARN_TYPE_REDEFINED, Getfile(n), Getline(n), "Template '%s' was already wrapped,\n", SwigType_namestr(name));
	    Swig_warning(WARN_TYPE_REDEFINED, Getfile(cn), Getline(cn), "previous wrap of '%s'.\n", SwigType_namestr(Getattr(cn, "name")));
	    scopename = 0;
	  }
	} else {
	  Swig_symbol_cadd(fname, n);
	  SwigType_typedef_class(fname);
	  scopename = Copy(fname);
	}
	Delete(deftype_name);
	Delete(resolved_name);
      } else {
	if ((CPlusPlus) || (unnamed)) {
	  SwigType_typedef_class(name);
	} else {
	  SwigType_typedef_class(NewStringf("%s %s", kind, name));
	}
	scopename = Copy(name);
      }
    } else {
      scopename = 0;
    }

    Setattr(n, "typepass:visit", "1");

    /* Need to set up a typedef if unnamed */
    if (unnamed && tdname && (Cmp(storage, "typedef") == 0)) {
      SwigType_typedef(unnamed, tdname);
    }
    // name of the outer class should already be patched to contain its outer classes names, but not to contain namespaces
    // namespace name (if present) is added after processing child nodes
    if (Getattr(n, "nested:outer") && name) {
      String *outerName = Getattr(Getattr(n, "nested:outer"), "name");
      name = NewStringf("%s::%s", outerName, name);
      Setattr(n, "name", name);
      if (tdname) {
	tdname = NewStringf("%s::%s", outerName, tdname);
	Setattr(n, "tdname", tdname);
      }
    }

    if (nsname && name) {
      nname = NewStringf("%s::%s", nsname, name);
      String *tdname = Getattr(n, "tdname");
      if (tdname) {
	tdname = NewStringf("%s::%s", nsname, tdname);
	Setattr(n, "tdname", tdname);
      }
    }
    if (nssymname) {
      if (GetFlag(n, "feature:nspace"))
	Setattr(n, "sym:nspace", nssymname);
    }
    SwigType_new_scope(scopename);
    SwigType_attach_symtab(Getattr(n, "symtab"));

    /* Inherit type definitions into the class */
    if (name && !(GetFlag(n, "nested") && !checkAttribute(n, "access", "public") && 
      (GetFlag(n, "feature:flatnested") || Language::instance()->nestedClassesSupport() == Language::NCS_None))) {
      cplus_inherit_types(n, 0, nname ? nname : (fname ? fname : name));
    }

    inclass = n;
    symtab = Swig_symbol_setscope(Getattr(n, "symtab"));
    emit_children(n);
    Swig_symbol_setscope(symtab);

    Hash *ts = SwigType_pop_scope();
    Setattr(n, "typescope", ts);
    Delete(ts);
    Setattr(n, "module", module);

    // When a fully qualified templated type with default parameters is used in the parsed code, 
    // the following additional symbols and scopes are needed for successful lookups
    if (template_default_expanded) {
      Swig_symbol_alias(template_default_expanded, Getattr(n, "symtab"));
      SwigType_scope_alias(template_default_expanded, Getattr(n, "typescope"));
    }

    /* Normalize deferred types */
    {
      normal_node *nn = new normal_node();
      nn->normallist = normalize;
      nn->symtab = Getattr(n, "symtab");
      nn->next = patch_list;
      nn->typescope = Getattr(n, "typescope");
      patch_list = nn;
    }

    normalize = olist;

    /* If in a namespace, patch the class name */
    if (nname) {
      Setattr(n, "name", nname);
      Delete(nname);
    }
    Delete(fname);
    return SWIG_OK;
  }

  /* ------------------------------------------------------------
   * templateDeclaration()
   * ------------------------------------------------------------ */

  virtual int templateDeclaration(Node *n) {
    String *name = Getattr(n, "name");
    String *ttype = Getattr(n, "templatetype");
    if (Strcmp(ttype, "class") == 0) {
      String *rname = SwigType_typedef_resolve_all(name);
      SwigType_typedef_class(rname);
      Delete(rname);
    } else if (Strcmp(ttype, "classforward") == 0) {
      String *rname = SwigType_typedef_resolve_all(name);
      SwigType_typedef_class(rname);
      Delete(rname);
      /*      SwigType_typedef_class(name); */
    } else if (Strcmp(ttype, "cdecl") == 0) {
      String *rname = SwigType_typedef_resolve_all(name);
      SwigType_typedef_class(rname);
      Delete(rname);
    }
    return SWIG_OK;
  }

  /* ------------------------------------------------------------
   * lambdaDeclaration()
   * ------------------------------------------------------------ */

  virtual int lambdaDeclaration(Node *) {
    return SWIG_OK;
  }

  /* ------------------------------------------------------------
   * classforwardDeclaration()
   * ------------------------------------------------------------ */

  virtual int classforwardDeclaration(Node *n) {

    /* Can't do inside a C struct because it breaks C nested structure wrapping */
    if ((!inclass) || (CPlusPlus)) {
      String *name = Getattr(n, "name");
      SwigType_typedef_class(name);
    }
    return SWIG_OK;
  }

  /* ------------------------------------------------------------
   * namespaceDeclaration()
   * ------------------------------------------------------------ */

  virtual int namespaceDeclaration(Node *n) {
    Symtab *symtab;
    String *name = Getattr(n, "name");
    String *alias = Getattr(n, "alias");
    List *olist = normalize;
    normalize = NewList();
    if (alias) {
      Typetab *ts = Getattr(n, "typescope");
      if (!ts) {
	/* Create an empty scope for the alias */
	Node *ns = Getattr(n, "namespace");
	SwigType_scope_alias(name, Getattr(ns, "typescope"));
	ts = Getattr(ns, "typescope");
	Setattr(n, "typescope", ts);
      }
      /* Namespace alias */
      return SWIG_OK;
    } else {
      if (name) {
	Node *nn = Swig_symbol_clookup(name, n);
	Hash *ts = 0;
	if (nn)
	  ts = Getattr(nn, "typescope");
	if (!ts) {
	  SwigType_new_scope(name);
	  SwigType_attach_symtab(Getattr(n, "symtab"));
	} else {
	  SwigType_set_scope(ts);
	}
      }
      String *oldnsname = nsname;
      String *oldnssymname = nssymname;
      nsname = Swig_symbol_qualified(Getattr(n, "symtab"));
      nssymname = Swig_symbol_qualified_language_scopename(Getattr(n, "symtab"));
      symtab = Swig_symbol_setscope(Getattr(n, "symtab"));
      emit_children(n);
      Swig_symbol_setscope(symtab);

      if (name) {
	Hash *ts = SwigType_pop_scope();
	Setattr(n, "typescope", ts);
	Delete(ts);
      }

      /* Normalize deferred types */
      {
	normal_node *nn = new normal_node();
	nn->normallist = normalize;
	nn->symtab = Getattr(n, "symtab");
	nn->next = patch_list;
	nn->typescope = Getattr(n, "typescope");
	patch_list = nn;
      }
      normalize = olist;

      Delete(nssymname);
      nssymname = oldnssymname;
      Delete(nsname);
      nsname = oldnsname;
      return SWIG_OK;
    }
  }

  /* ------------------------------------------------------------
   * cDeclaration()
   * ------------------------------------------------------------ */

  virtual int cDeclaration(Node *n) {
    if (NoExcept) {
      Delattr(n, "throws");
    }

    /* Normalize types. */
    SwigType *ty = Getattr(n, "type");
    if (!ty) {
      return SWIG_OK;
    }
    normalize_type(ty);
    SwigType *decl = Getattr(n, "decl");
    if (decl) {
      normalize_type(decl);
    }
    normalize_parms(Getattr(n, "parms"));
    normalize_parms(Getattr(n, "throws"));
    if (GetFlag(n, "conversion_operator")) {
      /* The call to the operator in the generated wrapper must be fully qualified in order to compile */
      SwigType *name = Getattr(n, "name");
      SwigType *qualifiedname = Swig_symbol_string_qualify(name, 0);
      Clear(name);
      Append(name, qualifiedname);
      Delete(qualifiedname);
    }

    if (checkAttribute(n, "storage", "typedef")) {
      String *name = Getattr(n, "name");
      ty = Getattr(n, "type");
      decl = Getattr(n, "decl");
      SwigType *t = Copy(ty);
      {
	/* If the typename is qualified, make sure the scopename is fully qualified when making a typedef */
	if (Swig_scopename_check(t) && strncmp(Char(t), "::", 2)) {
	  String *base, *prefix, *qprefix;
	  base = Swig_scopename_last(t);
	  prefix = Swig_scopename_prefix(t);
	  qprefix = SwigType_typedef_qualified(prefix);
	  Delete(t);
	  t = NewStringf("%s::%s", qprefix, base);
	  Delete(base);
	  Delete(prefix);
	  Delete(qprefix);
	}
      }
      SwigType_push(t, decl);
      if (CPlusPlus) {
	Replaceall(t, "struct ", "");
	Replaceall(t, "union ", "");
	Replaceall(t, "class ", "");
      }
      SwigType_typedef(t, name);
    }
    /* If namespaces are active.  We need to patch the name with a namespace prefix */
    if (nsname && !inclass) {
      String *name = Getattr(n, "name");
      if (name) {
	String *nname = NewStringf("%s::%s", nsname, name);
	Setattr(n, "name", nname);
	Delete(nname);
      }
    }
    clean_overloaded(n);
    return SWIG_OK;
  }


  /* ------------------------------------------------------------
   * constructorDeclaration()
   * ------------------------------------------------------------ */

  virtual int constructorDeclaration(Node *n) {
    if (NoExcept) {
      Delattr(n, "throws");
    }

    normalize_parms(Getattr(n, "parms"));
    normalize_parms(Getattr(n, "throws"));

    clean_overloaded(n);
    return SWIG_OK;
  }

  /* ------------------------------------------------------------
   * destructorDeclaration()
   * ------------------------------------------------------------ */

  virtual int destructorDeclaration(Node *) {
    return SWIG_OK;
  }

  /* ------------------------------------------------------------
   * constantDirective()
   * ------------------------------------------------------------ */

  virtual int constantDirective(Node *n) {
    SwigType *ty = Getattr(n, "type");
    if (ty) {
      Setattr(n, "type", SwigType_typedef_qualified(ty));
    }
    return SWIG_OK;
  }


  /* ------------------------------------------------------------
   * enumDeclaration()
   * ------------------------------------------------------------ */

  virtual int enumDeclaration(Node *n) {
    String *name = Getattr(n, "name");

    if (name) {
      String *scope = 0;

      // Add a typedef to the type table so that we can use 'enum Name' as well as just 'Name'
      if (nsname || inclass) {

	// But first correct the name and tdname to contain the fully qualified scopename
	if (nsname && inclass) {
	  scope = NewStringf("%s::%s", nsname, Getattr(inclass, "name"));
	} else if (nsname) {
	  scope = NewStringf("%s", nsname);
	} else if (inclass) {
	  scope = NewStringf("%s", Getattr(inclass, "name"));
	}

	String *nname = NewStringf("%s::%s", scope, name);
	Setattr(n, "name", nname);

	String *tdname = Getattr(n, "tdname");
	if (tdname) {
	  tdname = NewStringf("%s::%s", scope, tdname);
	  Setattr(n, "tdname", tdname);
	}

	SwigType *t = NewStringf("enum %s", nname);
	SwigType_typedef(t, name);
      } else {
	SwigType *t = NewStringf("enum %s", name);
	SwigType_typedef(t, name);
      }
      Delete(scope);
    }

    String *tdname = Getattr(n, "tdname");
    String *unnamed = Getattr(n, "unnamed");
    String *storage = Getattr(n, "storage");

    // Construct enumtype - for declaring an enum of this type with SwigType_ltype() etc
    String *enumtype = 0;
    if (unnamed && tdname && (Cmp(storage, "typedef") == 0)) {
      enumtype = Copy(Getattr(n, "tdname"));
    } else if (name) {
      enumtype = NewStringf("%s%s", CPlusPlus ? "" : "enum ", Getattr(n, "name"));
    } else {
      // anonymous enums
      enumtype = Copy(Getattr(n, "type"));
    }
    Setattr(n, "enumtype", enumtype);

    if (nssymname) {
      if (GetFlag(n, "feature:nspace"))
	Setattr(n, "sym:nspace", nssymname);
    }

    // This block of code is for dealing with %ignore on an enum item where the target language
    // attempts to use the C enum value in the target language itself and expects the previous enum value
    // to be one more than the previous value... the previous enum item might not exist if it is ignored!
    // - It sets the first non-ignored enum item with the "firstenumitem" attribute.
    // - It adds an enumvalue attribute if the previous enum item is ignored
    {
      Node *c;
      int count = 0;
      String *previous = 0;
      bool previous_ignored = false;
      bool firstenumitem = false;
      for (c = firstChild(n); c; c = nextSibling(c)) {
	assert(strcmp(Char(nodeType(c)), "enumitem") == 0);

	bool reset;
	String *enumvalue = Getattr(c, "enumvalue");
	if (GetFlag(c, "feature:ignore") || !Getattr(c, "sym:name")) {
	  reset = enumvalue ? true : false;
	  previous_ignored = true;
	} else {
	  if (!enumvalue && previous_ignored) {
	    if (previous)
	      Setattr(c, "enumvalue", NewStringf("(%s) + %d", previous, count+1));
	    else
	      Setattr(c, "enumvalue", NewStringf("%d", count));
	    SetFlag(c, "virtenumvalue"); // identify enumvalue as virtual, ie not from the parsed source
	  }
	  if (!firstenumitem) {
	    SetFlag(c, "firstenumitem");
	    firstenumitem = true;
	  }
	  reset = true;
	  previous_ignored = false;
	}
	if (reset) {
	  previous = enumvalue ? enumvalue : Getattr(c, "name");
	  count = 0;
	} else {
	  count++;
	}
      }
    }

    emit_children(n);
    return SWIG_OK;
  }

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

  virtual int enumvalueDeclaration(Node *n) {
    String *name = Getattr(n, "name");
    String *value = Getattr(n, "value");
    String *scopedenum = Getattr(parentNode(n), "scopedenum");
    if (!value)
      value = name;
    if (Strcmp(value, name) == 0) {
      String *new_value;
      if ((nsname || inclass || scopedenum) && cparse_cplusplus) {
	new_value = NewStringf("%s::%s", SwigType_namestr(Swig_symbol_qualified(n)), value);
      } else {
	new_value = NewString(value);
      }
      if ((nsname || inclass || scopedenum) && !cparse_cplusplus) {
	String *cppvalue = NewStringf("%s::%s", SwigType_namestr(Swig_symbol_qualified(n)), value);
	Setattr(n, "cppvalue", cppvalue); /* for target languages that always generate C++ code even when wrapping C code */
      }
      Setattr(n, "value", new_value);
      Delete(new_value);
    }
    Node *next = nextSibling(n);

    // Make up an enumvalue if one was not specified in the parsed code (not designed to be used on enum items and %ignore - enumvalue will be set instead)
    if (!GetFlag(n, "feature:ignore")) {
      if (Getattr(n, "_last") && !Getattr(n, "enumvalue")) {	// Only the first enum item has _last set (Note: first non-ignored enum item has firstenumitem set)
	Setattr(n, "enumvalueex", "0");
      }
      if (next && !Getattr(next, "enumvalue")) {
	Setattr(next, "enumvalueex", NewStringf("%s + 1", Getattr(n, "sym:name")));
      }
    }

    return SWIG_OK;
  }

  /* ------------------------------------------------------------
   * enumforwardDeclaration()
   * ------------------------------------------------------------ */

  virtual int enumforwardDeclaration(Node *n) {

    // Use enumDeclaration() to do all the hard work.
    // Note that no children can be emitted in a forward declaration as there aren't any.
    int result = enumDeclaration(n);
    if (result == SWIG_OK) {
      // Detect when the real enum matching the forward enum declaration has not been parsed/declared
      SwigType *ty = SwigType_typedef_resolve_all(Getattr(n, "type"));
      Replaceall(ty, "enum ", "");
      Node *nn = Swig_symbol_clookup(ty, 0);

      String *nodetype = nn ? nodeType(nn) : 0;
      if (nodetype) {
	if (Equal(nodetype, "enumforward")) {
	  SetFlag(nn, "enumMissing");
	} // if a real enum was declared this would be an "enum" node type
      }
      Delete(ty);
    }
    return result;
  }

#ifdef DEBUG_OVERLOADED
  static void show_overloaded(Node *n) {
    Node *c = Getattr(n, "sym:overloaded");
    Node *checkoverloaded = c;
    Printf(stdout, "-------------------- overloaded start %s sym:overloaded():%p -------------------------------\n", Getattr(n, "name"), c);
    while (c) {
      if (Getattr(c, "error")) {
        c = Getattr(c, "sym:nextSibling");
        continue;
      }
      if (Getattr(c, "sym:overloaded") != checkoverloaded) {
        Printf(stdout, "sym:overloaded error c:%p checkoverloaded:%p\n", c, checkoverloaded);
        Swig_print_node(c);
        SWIG_exit(EXIT_FAILURE);
      }

      String *decl = Strcmp(nodeType(c), "using") == 0 ? NewString("------") : Getattr(c, "decl");
      Printf(stdout, "  show_overloaded %s::%s(%s)          [%s] nodeType:%s\n", parentNode(c) ? Getattr(parentNode(c), "name") : "NOPARENT", Getattr(c, "name"), decl, Getattr(c, "sym:overname"), nodeType(c));
      if (!Getattr(c, "sym:overloaded")) {
        Printf(stdout, "sym:overloaded error.....%p\n", c);
        Swig_print_node(c);
        SWIG_exit(EXIT_FAILURE);
      }
      c = Getattr(c, "sym:nextSibling");
    }
    Printf(stdout, "-------------------- overloaded end   %s -------------------------------\n", Getattr(n, "name"));
  }
#endif

  /* ------------------------------------------------------------
   * usingDeclaration()
   * ------------------------------------------------------------ */

  virtual int usingDeclaration(Node *n) {
    if (Getattr(n, "namespace")) {
      /* using namespace id */

      /* For a namespace import.   We set up inheritance in the type system */
      Node *ns = Getattr(n, "node");
      if (ns) {
	Typetab *ts = Getattr(ns, "typescope");
	if (ts) {
	  SwigType_using_scope(ts);
	}
      }
      return SWIG_OK;
    } else {
      Node *ns;
      /* using id */
      Symtab *stab = Getattr(n, "sym:symtab");
      if (stab) {
	String *uname = Getattr(n, "uname");
	ns = Swig_symbol_clookup(uname, stab);
	if (!ns && SwigType_istemplate(uname)) {
	  String *tmp = Swig_symbol_template_deftype(uname, 0);
	  if (!Equal(tmp, uname)) {
	    ns = Swig_symbol_clookup(tmp, stab);
	  }
	  Delete(tmp);
	}
      } else {
	ns = 0;
      }
      if (!ns) {
	if (is_public(n)) {
	  Swig_warning(WARN_PARSE_USING_UNDEF, Getfile(n), Getline(n), "Nothing known about '%s'.\n", SwigType_namestr(Getattr(n, "uname")));
	}
      } else {
	/* Only a single symbol is being used.  There are only a few symbols that
	   we actually care about.  These are typedef, class declarations, and enum */
	String *ntype = nodeType(ns);
	if (Strcmp(ntype, "cdecl") == 0) {
	  if (checkAttribute(ns, "storage", "typedef")) {
	    /* A typedef declaration */
	    String *uname = Getattr(n, "uname");
	    SwigType_typedef_using(uname);
	  } else {
	    /* A normal C declaration. */
	    if ((inclass) && (!GetFlag(n, "feature:ignore")) && (Getattr(n, "sym:name"))) {
	      Node *c = ns;
	      Node *unodes = 0, *last_unodes = 0;
	      int ccount = 0;
	      String *symname = Getattr(n, "sym:name");
	      while (c) {
		if (Strcmp(nodeType(c), "cdecl") == 0) {
		  if (!(Swig_storage_isstatic(c)
			|| checkAttribute(c, "storage", "typedef")
			|| checkAttribute(c, "storage", "friend")
			|| (Getattr(c, "feature:extend") && !Getattr(c, "code"))
			|| GetFlag(c, "feature:ignore"))) {

		    /* Don't generate a method if the method is overridden in this class, 
		     * for example don't generate another m(bool) should there be a Base::m(bool) :
		     * struct Derived : Base { 
		     *   void m(bool);
		     *   using Base::m;
		     * };
		     */
		    String *csymname = Getattr(c, "sym:name");
		    if (!csymname || (Strcmp(csymname, symname) == 0)) {
		      {
			String *decl = Getattr(c, "decl");
			Node *over = Getattr(n, "sym:overloaded");
			int match = 0;
			while (over) {
			  String *odecl = Getattr(over, "decl");
			  if (Cmp(decl, odecl) == 0) {
			    match = 1;
			    break;
			  }
			  over = Getattr(over, "sym:nextSibling");
			}
			if (match) {
			  c = Getattr(c, "csym:nextSibling");
			  continue;
			}
		      }
		      Node *nn = copyNode(c);
		      Delattr(nn, "access");	// access might be different from the method in the base class
		      Setattr(nn, "access", Getattr(n, "access"));
		      if (!Getattr(nn, "sym:name"))
			Setattr(nn, "sym:name", symname);

		      if (!GetFlag(nn, "feature:ignore")) {
			ParmList *parms = CopyParmList(Getattr(c, "parms"));
			int is_pointer = SwigType_ispointer_return(Getattr(nn, "decl"));
			int is_void = checkAttribute(nn, "type", "void") && !is_pointer;
			Setattr(nn, "parms", parms);
			Delete(parms);
			if (Getattr(n, "feature:extend")) {
			  String *ucode = is_void ? NewStringf("{ self->%s(", Getattr(n, "uname")) : NewStringf("{ return self->%s(", Getattr(n, "uname"));

			  for (ParmList *p = parms; p;) {
			    Append(ucode, Getattr(p, "name"));
			    p = nextSibling(p);
			    if (p)
			      Append(ucode, ",");
			  }
			  Append(ucode, "); }");
			  Setattr(nn, "code", ucode);
			  Delete(ucode);
			}
			ParmList *throw_parm_list = Getattr(c, "throws");
			if (throw_parm_list)
			  Setattr(nn, "throws", CopyParmList(throw_parm_list));
			ccount++;
			if (!last_unodes) {
			  last_unodes = nn;
			  unodes = nn;
			} else {
			  Setattr(nn, "previousSibling", last_unodes);
			  Setattr(last_unodes, "nextSibling", nn);
			  Setattr(nn, "sym:previousSibling", last_unodes);
			  Setattr(last_unodes, "sym:nextSibling", nn);
			  Setattr(nn, "sym:overloaded", unodes);
			  Setattr(unodes, "sym:overloaded", unodes);
			  last_unodes = nn;
			}
		      } else {
			Delete(nn);
		      }
		    }
		  }
		}
		c = Getattr(c, "csym:nextSibling");
	      }
	      if (unodes) {
		set_firstChild(n, unodes);
		if (ccount > 1) {
		  if (!Getattr(n, "sym:overloaded")) {
		    Setattr(n, "sym:overloaded", n);
		    Setattr(n, "sym:overname", "_SWIG_0");
		  }
		}
	      }

	      /* Hack the parse tree symbol table for overloaded methods. Replace the "using" node with the
	       * list of overloaded methods we have just added in as child nodes to the "using" node.
	       * The node will still exist, it is just the symbol table linked list of overloaded methods
	       * which is hacked. */
	      if (Getattr(n, "sym:overloaded")) {
		int cnt = 0;
#ifdef DEBUG_OVERLOADED
		Node *debugnode = n;
		show_overloaded(n);
#endif
		if (!firstChild(n)) {
		  // Remove from overloaded list ('using' node does not actually end up adding in any methods)
		  Node *ps = Getattr(n, "sym:previousSibling");
		  Node *ns = Getattr(n, "sym:nextSibling");
		  if (ps) {
		    Setattr(ps, "sym:nextSibling", ns);
		  }
		  if (ns) {
		    Setattr(ns, "sym:previousSibling", ps);
		  }
		} else {
		  // The 'using' node results in methods being added in - slot in the these methods here 
		  Node *ps = Getattr(n, "sym:previousSibling");
		  Node *ns = Getattr(n, "sym:nextSibling");
		  Node *fc = firstChild(n);
		  Node *pp = fc;

		  Node *firstoverloaded = Getattr(n, "sym:overloaded");
		  if (firstoverloaded == n) {
		    // This 'using' node we are cutting out was the first node in the overloaded list. 
		    // Change the first node in the list to its first sibling
		    Delattr(firstoverloaded, "sym:overloaded");
		    Node *nnn = Getattr(firstoverloaded, "sym:nextSibling");
		    firstoverloaded = fc;
		    while (nnn) {
		      Setattr(nnn, "sym:overloaded", firstoverloaded);
		      nnn = Getattr(nnn, "sym:nextSibling");
		    }
		  }
		  while (pp) {
		    Node *ppn = Getattr(pp, "sym:nextSibling");
		    Setattr(pp, "sym:overloaded", firstoverloaded);
		    Setattr(pp, "sym:overname", NewStringf("%s_%d", Getattr(n, "sym:overname"), cnt++));
		    if (ppn)
		      pp = ppn;
		    else
		      break;
		  }
		  if (ps) {
		    Setattr(ps, "sym:nextSibling", fc);
		    Setattr(fc, "sym:previousSibling", ps);
		  }
		  if (ns) {
		    Setattr(ns, "sym:previousSibling", pp);
		    Setattr(pp, "sym:nextSibling", ns);
		  }
#ifdef DEBUG_OVERLOADED
		  debugnode = firstoverloaded;
#endif
		}
		Delattr(n, "sym:previousSibling");
		Delattr(n, "sym:nextSibling");
		Delattr(n, "sym:overloaded");
		Delattr(n, "sym:overname");
#ifdef DEBUG_OVERLOADED
		show_overloaded(debugnode);
#endif
		clean_overloaded(n);	// Needed?
	      }
	    }
	  }
	} else if ((Strcmp(ntype, "class") == 0) || ((Strcmp(ntype, "classforward") == 0))) {
	  /* We install the using class name as kind of a typedef back to the original class */
	  String *uname = Getattr(n, "uname");
	  /* Import into current type scope */
	  SwigType_typedef_using(uname);
	} else if (Strcmp(ntype, "enum") == 0) {
	  SwigType_typedef_using(Getattr(n, "uname"));
	} else if (Strcmp(ntype, "template") == 0) {
	  SwigType_typedef_using(Getattr(n, "uname"));
	}
      }
    }
    return SWIG_OK;
  }

  /* ------------------------------------------------------------
   * typemapDirective()
   * ------------------------------------------------------------ */

  virtual int typemapDirective(Node *n) {
    if (inclass || nsname) {
      Node *items = firstChild(n);
      while (items) {
	Parm *pattern = Getattr(items, "pattern");
	Parm *parms = Getattr(items, "parms");
	normalize_later(pattern);
	normalize_later(parms);
	items = nextSibling(items);
      }
    }
    return SWIG_OK;
  }


  /* ------------------------------------------------------------
   * typemapcopyDirective()
   * ------------------------------------------------------------ */

  virtual int typemapcopyDirective(Node *n) {
    if (inclass || nsname) {
      Node *items = firstChild(n);
      ParmList *pattern = Getattr(n, "pattern");
      normalize_later(pattern);
      while (items) {
	ParmList *npattern = Getattr(items, "pattern");
	normalize_later(npattern);
	items = nextSibling(items);
      }
    }
    return SWIG_OK;
  }

  /* ------------------------------------------------------------
   * applyDirective()
   * ------------------------------------------------------------ */

  virtual int applyDirective(Node *n) {
    if (inclass || nsname) {
      ParmList *pattern = Getattr(n, "pattern");
      normalize_later(pattern);
      Node *items = firstChild(n);
      while (items) {
	Parm *apattern = Getattr(items, "pattern");
	normalize_later(apattern);
	items = nextSibling(items);
      }
    }
    return SWIG_OK;
  }

  /* ------------------------------------------------------------
   * clearDirective()
   * ------------------------------------------------------------ */

  virtual int clearDirective(Node *n) {
    if (inclass || nsname) {
      Node *p;
      for (p = firstChild(n); p; p = nextSibling(p)) {
	ParmList *pattern = Getattr(p, "pattern");
	normalize_later(pattern);
      }
    }
    return SWIG_OK;
  }

public:
  static void pass(Node *n) {
    TypePass t;
    t.top(n);
  }
};

void Swig_process_types(Node *n) {
  if (!n)
    return;
  TypePass::pass(n);
}

