/* -----------------------------------------------------------------------------
 * 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.
 *
 * typesys.c
 *
 * SWIG type system management.   These functions are used to manage
 * the C++ type system including typenames, typedef, type scopes, 
 * inheritance, and namespaces.   Generation of support code for the
 * run-time type checker is also handled here.
 * ----------------------------------------------------------------------------- */

#include "swig.h"
#include "cparse.h"

/* -----------------------------------------------------------------------------
 * Synopsis
 *
 * The purpose of this module is to manage type names and scoping issues related
 * to the C++ type system.   The primary use is tracking typenames through typedef
 * and inheritance. 
 *
 * New typenames are introduced by typedef, class, and enum declarations.
 * Each type is declared in a scope.  This is either the global scope, a
 * class, or a namespace.  For example:
 *
 *  typedef int A;         // Typename A, in global scope
 *  namespace Foo {
 *    typedef int A;       // Typename A, in scope Foo::
 *  }
 *  class Bar {            // Typename Bar, in global scope
 *    typedef int A;       // Typename A, in scope Bar::
 *  }
 *
 * To manage scopes, the type system is constructed as a tree of hash tables.  Each 
 * hash table contains the following attributes:
 *
 *    "name"            -  Scope name
 *    "qname"           -  Fully qualified typename
 *    "typetab"         -  Type table containing typenames and typedef information
 *                         For a given key in the typetab table, the value is a fully
 *                         qualified name if not pointing to itself.
 *    "symtab"          -  Hash table of symbols defined in a scope
 *    "inherit"         -  List of inherited scopes       
 *    "parent"          -  Parent scope
 * 
 * The contents of these tables can be viewed for debugging using the -debug-typedef
 * option which calls SwigType_print_scope().
 *
 * Typedef information is stored in the "typetab" hash table.  For example,
 * if you have these declarations:
 *
 *      typedef int A;
 *      typedef A B;
 *      typedef B *C;
 *
 * typetab in scope '' contains:
 *      "A"     : "int"
 *      "B"     : "A"
 *      "C"     : "p.B"
 *
 * To resolve a type back to its root type, one repeatedly expands on the type base.
 * For example:
 *
 *     C *[40]   ---> a(40).p.C       (string type representation, see stype.c)
 *               ---> a(40).p.p.B     (C --> p.B)
 *               ---> a(40).p.p.A     (B --> A)
 *               ---> a(40).p.p.int   (A --> int)
 *
 *
 * Using declarations are stored in the "typetab" hash table. For example,
 *
 *      namespace NN {
 *        struct SS {};
 *      }
 *      namespace N {
 *        struct S {};
 *        using NN::SS;
 *      }
 *      using N::S;
 *
 * typetab in scope '' contains:
 *      "S" : "N::S"
 * 
 * and typetab in scope 'N' contains:
 *      "SS" : "NN::SS"
 *      "S" : "S"
 *
 *
 * For inheritance, SWIG tries to resolve types back to the base class. For instance, if
 * you have this:
 *
 *      class Foo {
 *      public:
 *         typedef int Integer;
 *      };
 *      struct Bar : public Foo {
 *        void blah(Integer x);
 *      };
 *
 * In this case typetab in scope '' contains:
 *      "Foo" : "Foo"
 *      "Bar" : "Bar"
 * and scope 'Foo' contains:
 *      "Integer" : "int"
 * and scope 'Bar' inherits from 'Foo' but is empty (observe that blah is not a scope or typedef)
 *
 * The argument type of Bar::blah will be set to Foo::Integer.   
 *
 *
 * The scope-inheritance mechanism is used to manage C++ using directives.
 *
 *      namespace XX {
 *        class CC {};
 *      }
 *      namespace X {
 *        class C {};
 *        using namespace XX;
 *      }
 *      using namespace X;
 *
 * typetab in scope '' inherits from 'X'
 * typetab in scope 'X' inherits from 'XX' and contains:
 *      "C" : "C"
 * typetab in scope 'XX' contains:
 *      "CC" : "CC"
 *
 *
 * The scope-inheritance mechanism is used to manage C++ namespace aliases.
 * For example, if you have this:
 *
 *      namespace Foo {
 *        typedef int Integer;
 *      }
 *
 *      namespace F = Foo;
 *
 * In this case, F is defined as a scope that "inherits" from Foo.  Internally,
 * F will merely be an empty scope that points to Foo.  SWIG will never 
 * place new type information into a namespace alias---attempts to do so
 * will generate a warning message (in the parser) and will place information into 
 * Foo instead.
 *
 *----------------------------------------------------------------------------- */

static Typetab *current_scope = 0;	/* Current type scope                           */
static Hash *current_typetab = 0;	/* Current type table                           */
static Hash *current_symtab = 0;	/* Current symbol table                         */
static Typetab *global_scope = 0;	/* The global scope                             */
static Hash *scopes = 0;	/* Hash table containing fully qualified scopes */

/* Performance optimization */
#define SWIG_TYPEDEF_RESOLVE_CACHE 
static Hash *typedef_resolve_cache = 0;
static Hash *typedef_all_cache = 0;
static Hash *typedef_qualified_cache = 0;

static Typetab *SwigType_find_scope(Typetab *s, const SwigType *nameprefix);

/* common attribute keys, to avoid calling find_key all the times */

/* 
   Enable this one if your language fully support SwigValueWrapper<T>.
   
   Leaving at '0' keeps the old swig behavior, which is not
   always safe, but is well known.

   Setting at '1' activates the new scheme, which is always safe but
   it requires all the typemaps to be ready for that.
  
*/
static int value_wrapper_mode = 0;
int Swig_value_wrapper_mode(int mode) {
  value_wrapper_mode = mode;
  return mode;
}


static void flush_cache(void) {
  typedef_resolve_cache = 0;
  typedef_all_cache = 0;
  typedef_qualified_cache = 0;
}

/* Initialize the scoping system */

void SwigType_typesystem_init(void) {
  if (global_scope)
    Delete(global_scope);
  if (scopes)
    Delete(scopes);

  current_scope = NewHash();
  global_scope = current_scope;

  Setattr(current_scope, "name", "");	/* No name for global scope */
  current_typetab = NewHash();
  Setattr(current_scope, "typetab", current_typetab);

  current_symtab = 0;
  scopes = NewHash();
  Setattr(scopes, "", current_scope);
}


/* -----------------------------------------------------------------------------
 * SwigType_typedef()
 *
 * Defines a new typedef in the current scope. Returns -1 if the type name is 
 * already defined.  
 * ----------------------------------------------------------------------------- */

int SwigType_typedef(const SwigType *type, const_String_or_char_ptr name) {
  /* Printf(stdout, "typedef %s %s\n", type, name); */
  if (Getattr(current_typetab, name))
    return -1;			/* Already defined */
  if (Strcmp(type, name) == 0) {	/* Can't typedef a name to itself */
    return 0;
  }

  /* Check if 'type' is already a scope.  If so, we create an alias in the type
     system for it.  This is needed to make strange nested scoping problems work
     correctly.  */
  {
    Typetab *t = SwigType_find_scope(current_scope, type);
    if (t) {
      SwigType_new_scope(name);
      SwigType_inherit_scope(t);
      SwigType_pop_scope();
    }
  }
  Setattr(current_typetab, name, type);
  flush_cache();
  return 0;
}


/* -----------------------------------------------------------------------------
 * SwigType_typedef_class()
 *
 * Defines a class in the current scope. 
 * ----------------------------------------------------------------------------- */

int SwigType_typedef_class(const_String_or_char_ptr name) {
  String *cname;
  /*  Printf(stdout,"class : '%s'\n", name); */
  if (Getattr(current_typetab, name))
    return -1;			/* Already defined */
  cname = NewString(name);
  Setmeta(cname, "class", "1");
  Setattr(current_typetab, cname, cname);
  Delete(cname);
  flush_cache();
  return 0;
}

/* -----------------------------------------------------------------------------
 * SwigType_scope_name()
 *
 * Returns the qualified scope name of a type table
 * ----------------------------------------------------------------------------- */

static String *SwigType_scope_name(Typetab *ttab) {
  String *qname = NewString(Getattr(ttab, "name"));
  ttab = Getattr(ttab, "parent");
  while (ttab) {
    String *pname = Getattr(ttab, "name");
    if (Len(pname)) {
      Insert(qname, 0, "::");
      Insert(qname, 0, pname);
    }
    ttab = Getattr(ttab, "parent");
  }
  return qname;
}

/* -----------------------------------------------------------------------------
 * SwigType_new_scope()
 *
 * Creates a new scope
 * ----------------------------------------------------------------------------- */

void SwigType_new_scope(const_String_or_char_ptr name) {
  Typetab *s;
  Hash *ttab;
  String *qname;

  if (!name) {
    name = "<unnamed>";
  }
  s = NewHash();
  Setattr(s, "name", name);
  Setattr(s, "parent", current_scope);
  ttab = NewHash();
  Setattr(s, "typetab", ttab);

  /* Build fully qualified name */
  qname = SwigType_scope_name(s);
#if 1
  {
    /* TODO: only do with templates? What happens with non-templates with code below? */
    String *stripped_qname;
    stripped_qname = SwigType_remove_global_scope_prefix(qname);
    /* Use fully qualified name for hash key without unary scope prefix, qname may contain unary scope */
    Setattr(scopes, stripped_qname, s);
    Setattr(s, "qname", qname);
    /*
    Printf(stdout, "SwigType_new_scope stripped %s %s\n", qname, stripped_qname);
    */
    Delete(stripped_qname);
  }
#else
  Printf(stdout, "SwigType_new_scope %s\n", qname);
  Setattr(scopes, qname, s);
  Setattr(s, "qname", qname);
#endif
  Delete(qname);

  current_scope = s;
  current_typetab = ttab;
  current_symtab = 0;
  flush_cache();
}

/* -----------------------------------------------------------------------------
 * SwigType_inherit_scope()
 *
 * Makes the current scope inherit from another scope.  This is used for both
 * C++ class inheritance, namespaces, and namespace aliases.
 * ----------------------------------------------------------------------------- */

void SwigType_inherit_scope(Typetab *scope) {
  List *inherits;
  int i, len;
  inherits = Getattr(current_scope, "inherit");
  if (!inherits) {
    inherits = NewList();
    Setattr(current_scope, "inherit", inherits);
    Delete(inherits);
  }
  assert(scope != current_scope);

  len = Len(inherits);
  for (i = 0; i < len; i++) {
    Node *n = Getitem(inherits, i);
    if (n == scope)
      return;
  }
  Append(inherits, scope);
}

/* -----------------------------------------------------------------------------
 * SwigType_scope_alias()
 *
 * Creates a scope-alias.
 * ----------------------------------------------------------------------------- */

void SwigType_scope_alias(String *aliasname, Typetab *ttab) {
  String *q;
  /*  Printf(stdout,"alias: '%s' '%p'\n", aliasname, ttab); */
  q = SwigType_scope_name(current_scope);
  if (Len(q)) {
    Append(q, "::");
  }
  Append(q, aliasname);
  Setattr(scopes, q, ttab);
  flush_cache();
}

/* -----------------------------------------------------------------------------
 * SwigType_using_scope()
 *
 * Import another scope into this scope.
 * ----------------------------------------------------------------------------- */

void SwigType_using_scope(Typetab *scope) {
  SwigType_inherit_scope(scope);
  {
    List *ulist;
    int i, len;
    ulist = Getattr(current_scope, "using");
    if (!ulist) {
      ulist = NewList();
      Setattr(current_scope, "using", ulist);
      Delete(ulist);
    }
    assert(scope != current_scope);
    len = Len(ulist);
    for (i = 0; i < len; i++) {
      Typetab *n = Getitem(ulist, i);
      if (n == scope)
	return;
    }
    Append(ulist, scope);
  }
  flush_cache();
}

/* -----------------------------------------------------------------------------
 * SwigType_pop_scope()
 *
 * Pop off the last scope and perform a merge operation.  Returns the hash
 * table for the scope that was popped off.
 * ----------------------------------------------------------------------------- */

Typetab *SwigType_pop_scope(void) {
  Typetab *t, *old = current_scope;
  t = Getattr(current_scope, "parent");
  if (!t)
    t = global_scope;
  current_scope = t;
  current_typetab = Getattr(t, "typetab");
  current_symtab = Getattr(t, "symtab");
  flush_cache();
  return old;
}

/* -----------------------------------------------------------------------------
 * SwigType_set_scope()
 *
 * Set the scope.  Returns the old scope.
 * ----------------------------------------------------------------------------- */

Typetab *SwigType_set_scope(Typetab *t) {
  Typetab *old = current_scope;
  if (!t)
    t = global_scope;
  current_scope = t;
  current_typetab = Getattr(t, "typetab");
  current_symtab = Getattr(t, "symtab");
  flush_cache();
  return old;
}

/* -----------------------------------------------------------------------------
 * SwigType_attach_symtab()
 *
 * Attaches a symbol table to a type scope
 * ----------------------------------------------------------------------------- */

void SwigType_attach_symtab(Symtab *sym) {
  Setattr(current_scope, "symtab", sym);
  current_symtab = sym;
}

/* -----------------------------------------------------------------------------
 * SwigType_print_scope()
 *
 * Debugging function for printing out current scope
 * ----------------------------------------------------------------------------- */

void SwigType_print_scope(void) {
  Hash *ttab;
  Iterator i, j;

  Printf(stdout, "SCOPES start  =======================================\n");
  for (i = First(scopes); i.key; i = Next(i)) {
    Printf(stdout, "-------------------------------------------------------------\n");
    ttab = Getattr(i.item, "typetab");

    Printf(stdout, "Type scope '%s' (%p)\n", i.key, i.item);
    {
      List *inherit = Getattr(i.item, "inherit");
      if (inherit) {
	Iterator j;
	for (j = First(inherit); j.item; j = Next(j)) {
	  Printf(stdout, "    Inherits from '%s' (%p)\n", Getattr(j.item, "qname"), j.item);
	}
      }
    }
    for (j = First(ttab); j.key; j = Next(j)) {
      Printf(stdout, "%40s -> %s\n", j.key, j.item);
    }
  }
  Printf(stdout, "SCOPES finish =======================================\n");
}

static Typetab *SwigType_find_scope(Typetab *s, const SwigType *nameprefix) {
  Typetab *ss;
  Typetab *s_orig = s;
  String *nnameprefix = 0;
  static int check_parent = 1;
  int is_template = 0;

  if (Getmark(s))
    return 0;
  Setmark(s, 1);

  is_template = SwigType_istemplate(nameprefix);
  if (is_template) {
    nnameprefix = SwigType_typedef_resolve_all(nameprefix);
    nameprefix = nnameprefix;
  }

  ss = s;
  while (ss) {
    String *full;
    String *qname = Getattr(ss, "qname");
    if (qname) {
      full = NewStringf("%s::%s", qname, nameprefix);
      if (Strncmp(full, "enum ", 5) == 0) {
	Delslice(full, 0, 5);
      }
    } else {
      full = NewString(nameprefix);
    }
    s = Getattr(scopes, full);
    if (!s && is_template) {
      /* try look up scope with all the unary scope operators within the template parameter list removed */
      SwigType *full_stripped = SwigType_remove_global_scope_prefix(full);
      s = Getattr(scopes, full_stripped);
      Delete(full_stripped);
    }
    Delete(full);
    if (s) {
      if (nnameprefix)
	Delete(nnameprefix);
      Setmark(s_orig, 0);
      return s;
    }
    if (!s) {
      /* Check inheritance */
      List *inherit;
      inherit = Getattr(ss, "using");
      if (inherit) {
	Typetab *ttab;
	int i, len;
	len = Len(inherit);
	for (i = 0; i < len; i++) {
	  int oldcp = check_parent;
	  ttab = Getitem(inherit, i);
	  check_parent = 0;
	  s = SwigType_find_scope(ttab, nameprefix);
	  check_parent = oldcp;
	  if (s) {
	    if (nnameprefix)
	      Delete(nnameprefix);
	    Setmark(s_orig, 0);
	    return s;
	  }
	}
      }
    }
    if (!check_parent)
      break;
    ss = Getattr(ss, "parent");
  }
  if (nnameprefix)
    Delete(nnameprefix);
  Setmark(s_orig, 0);
  return 0;
}

/* ----------------------------------------------------------------------------- 
 * typedef_resolve()
 *
 * Resolves a typedef and returns a new type string.  Returns 0 if there is no
 * typedef mapping.  base is a name without qualification.
 * Internal function.
 * ----------------------------------------------------------------------------- */

static Typetab *resolved_scope = 0;

/* Internal function */

static SwigType *_typedef_resolve(Typetab *s, String *base, int look_parent) {
  Hash *ttab;
  SwigType *type = 0;
  List *inherit;
  Typetab *parent;

  /* if (!s) return 0; *//* now is checked below */
  /* Printf(stdout,"Typetab %s : %s\n", Getattr(s,"name"), base);  */

  if (!Getmark(s)) {
    Setmark(s, 1);

    ttab = Getattr(s, "typetab");
    type = Getattr(ttab, base);
    if (type) {
      resolved_scope = s;
      Setmark(s, 0);
    } else {
      /* Hmmm. Not found in my scope.  It could be in an inherited scope */
      inherit = Getattr(s, "inherit");
      if (inherit) {
	int i, len;
	len = Len(inherit);
	for (i = 0; i < len; i++) {
	  type = _typedef_resolve(Getitem(inherit, i), base, 0);
	  if (type) {
	    Setmark(s, 0);
	    break;
	  }
	}
      }
      if (!type) {
	/* Hmmm. Not found in my scope.  check parent */
	if (look_parent) {
	  parent = Getattr(s, "parent");
	  type = parent ? _typedef_resolve(parent, base, 1) : 0;
	}
      }
      Setmark(s, 0);
    }
  }
  return type;
}

/* ----------------------------------------------------------------------------- 
 * template_parameters_resolve()
 *
 * For use with templates only. Attempts to resolve one template parameter.
 *
 * If one of the template parameters can be resolved, the type is returned with
 * just the one parameter resolved and the remaining parameters left as is.
 * If none of the template parameters can be resolved, zero is returned.
 * ----------------------------------------------------------------------------- */

static String *template_parameters_resolve(const String *base) {
  List *tparms;
  String *suffix;
  String *type;
  int i, sz;
  int rep = 0;
  type = SwigType_templateprefix(base);
  suffix = SwigType_templatesuffix(base);
  Append(type, "<(");
  tparms = SwigType_parmlist(base);
  sz = Len(tparms);
  for (i = 0; i < sz; i++) {
    SwigType *tpr;
    SwigType *tp = Getitem(tparms, i);
    if (!rep) {
      tpr = SwigType_typedef_resolve(tp);
    } else {
      tpr = 0;
    }
    if (tpr) {
      Append(type, tpr);
      Delete(tpr);
      rep = 1;
    } else {
      Append(type, tp);
    }
    if ((i + 1) < sz)
      Append(type, ",");
  }
  if (rep) {
    Append(type, ")>");
    Append(type, suffix);
  } else {
    Delete(type);
    type = 0;
  }
  Delete(suffix);
  Delete(tparms);
  return type;
}

static SwigType *typedef_resolve(Typetab *s, String *base) {
  return _typedef_resolve(s, base, 1);
}


/* ----------------------------------------------------------------------------- 
 * SwigType_typedef_resolve()
 *
 * Given a type declaration, this function looks to reduce/resolve the type via a
 * typedef (including via C++ using declarations).
 *
 * If it is able to find a typedef, the resolved type is returned. If no typedef
 * is found NULL is returned. The type name is resolved in the current scope.
 * The type returned is not always fully qualified for the global scope, it is
 * valid for use in the current scope. If the current scope is global scope, a
 * fully qualified type should be returned.
 *
 * Some additional notes are in Doc/Manual/Extending.html.
 * ----------------------------------------------------------------------------- */

/* #define SWIG_DEBUG */
SwigType *SwigType_typedef_resolve(const SwigType *t) {
  String *base;
  String *type = 0;
  String *r = 0;
  Typetab *s;
  Hash *ttab;
  String *namebase = 0;
  String *nameprefix = 0, *rnameprefix = 0;
  int newtype = 0;

  resolved_scope = 0;

#ifdef SWIG_TYPEDEF_RESOLVE_CACHE
  if (!typedef_resolve_cache) {
    typedef_resolve_cache = NewHash();
  }
  r = Getattr(typedef_resolve_cache, t);
  if (r) {
    resolved_scope = Getmeta(r, "scope");
    return Copy(r);
  }
#endif

  base = SwigType_base(t);

#ifdef SWIG_DEBUG
  Printf(stdout, "base = '%s' t='%s'\n", base, t);
#endif

  if (SwigType_issimple(base)) {
    s = current_scope;
    ttab = current_typetab;
    if (strncmp(Char(base), "::", 2) == 0) {
      s = global_scope;
      ttab = Getattr(s, "typetab");
      Delitem(base, 0);
      Delitem(base, 0);
    }
    /* Do a quick check in the local scope */
    type = Getattr(ttab, base);
    if (type) {
      resolved_scope = s;
    }
    if (!type) {
      /* Didn't find in this scope.   We need to do a little more searching */
      if (Swig_scopename_check(base)) {
	/* A qualified name. */
	Swig_scopename_split(base, &nameprefix, &namebase);
#ifdef SWIG_DEBUG
	Printf(stdout, "nameprefix = '%s'\n", nameprefix);
#endif
	if (nameprefix) {
	  rnameprefix = SwigType_typedef_resolve(nameprefix);
	  if(rnameprefix != NULL) {
#ifdef SWIG_DEBUG
	    Printf(stdout, "nameprefix '%s' is a typedef to '%s'\n", nameprefix, rnameprefix);
#endif
	    type = Copy(namebase);
	    Insert(type, 0, "::");
	    Insert(type, 0, rnameprefix);
	    if (Strncmp(type, "enum ", 5) == 0) {
	      Delslice(type, 0, 5);
	    }
	    if (Strncmp(type, "::", 2) == 0) {
	      Delslice(type, 0, 2);
	    }
	    newtype = 1;
	  } else {
	    /* Name had a prefix on it.   See if we can locate the proper scope for it */
	    String *rnameprefix = template_parameters_resolve(nameprefix);
	    nameprefix = rnameprefix ? Copy(rnameprefix) : nameprefix;
	    Delete(rnameprefix);
	    s = SwigType_find_scope(s, nameprefix);

	    /* Couldn't locate a scope for the type.  */
	    if (!s) {
	      Delete(base);
	      Delete(namebase);
	      Delete(nameprefix);
	      r = 0;
	      goto return_result;
	    }
	    /* Try to locate the name starting in the scope */
#ifdef SWIG_DEBUG
	    Printf(stdout, "namebase = '%s'\n", namebase);
#endif
	    type = typedef_resolve(s, namebase);
	    if (type && resolved_scope) {
	      /* we need to look for the resolved type, this will also
	         fix the resolved_scope if 'type' and 'namebase' are
	         declared in different scopes */
	      String *rtype = 0;
	      rtype = typedef_resolve(resolved_scope, type);
	      if (rtype)
	        type = rtype;
	    }
#ifdef SWIG_DEBUG
	    Printf(stdout, "%s type = '%s'\n", Getattr(s, "name"), type);
#endif
	    if ((type) && (!Swig_scopename_check(type)) && resolved_scope) {
	      Typetab *rtab = resolved_scope;
	      String *qname = Getattr(resolved_scope, "qname");
	      /* If qualified *and* the typename is defined from the resolved scope, we qualify */
	      if ((qname) && typedef_resolve(resolved_scope, type)) {
	        type = Copy(type);
	        Insert(type, 0, "::");
	        Insert(type, 0, qname);
#ifdef SWIG_DEBUG
	        Printf(stdout, "qual %s \n", type);
#endif
	        newtype = 1;
	      }
	      resolved_scope = rtab;
	    }
	  }
	} else {
	  /* Name is unqualified. */
	  type = typedef_resolve(s, base);
	}
      } else {
	/* Name is unqualified. */
	type = typedef_resolve(s, base);
      }
    }

    if (!type && SwigType_istemplate(base)) {
      String *tprefix = SwigType_templateprefix(base);
      String *rtprefix = SwigType_typedef_resolve(tprefix);
      /* We're looking for a using declaration on the template prefix to resolve the template prefix
       * in another scope. Using declaration do not have template parameters. */
      if (rtprefix && !SwigType_istemplate(rtprefix)) {
	String *tsuffix = SwigType_templatesuffix(base);
	String *targs = SwigType_templateargs(base);
	type = NewString(rtprefix);
	newtype = 1;
	Append(type, targs);
	Append(type, tsuffix);
	Delete(targs);
	Delete(tsuffix);
	Delete(rtprefix);
      }
      Delete(tprefix);
    }

    if (type && (Equal(base, type))) {
      if (newtype)
	Delete(type);
      Delete(base);
      Delete(namebase);
      Delete(nameprefix);
      r = 0;
      goto return_result;
    }

    /* If the type is a template, and no typedef was found, we need to check the
       template arguments one by one to see if they can be resolved. */

    if (!type && SwigType_istemplate(base)) {
      newtype = 1;
      type = template_parameters_resolve(base);
    }
    Delete(namebase);
    Delete(nameprefix);
  } else {
    if (SwigType_isfunction(base)) {
      List *parms;
      int i, sz;
      int rep = 0;
      type = NewString("f(");
      newtype = 1;
      parms = SwigType_parmlist(base);
      sz = Len(parms);
      for (i = 0; i < sz; i++) {
	SwigType *tpr;
	SwigType *tp = Getitem(parms, i);
	if (!rep) {
	  tpr = SwigType_typedef_resolve(tp);
	} else {
	  tpr = 0;
	}
	if (tpr) {
	  Append(type, tpr);
	  Delete(tpr);
	  rep = 1;
	} else {
	  Append(type, tp);
	}
	if ((i + 1) < sz)
	  Append(type, ",");
      }
      Append(type, ").");
      Delete(parms);
      if (!rep) {
	Delete(type);
	type = 0;
      }
    } else if (SwigType_ismemberpointer(base)) {
      String *rt;
      String *mtype = SwigType_parm(base);
      rt = SwigType_typedef_resolve(mtype);
      if (rt) {
	type = NewStringf("m(%s).", rt);
	newtype = 1;
	Delete(rt);
      }
      Delete(mtype);
    } else {
      type = 0;
    }
  }
  r = SwigType_prefix(t);
  if (!type) {
    if (r && Len(r)) {
      char *cr = Char(r);
      if ((strstr(cr, "f(") || (strstr(cr, "m(")))) {
	SwigType *rt = SwigType_typedef_resolve(r);
	if (rt) {
	  Delete(r);
	  Append(rt, base);
	  Delete(base);
	  r = rt;
	  goto return_result;
	}
      }
    }
    Delete(r);
    Delete(base);
    r = 0;
    goto return_result;
  }
  Delete(base);

  /* If 'type' is an array, then the right-most qualifier in 'r' should
     be added to 'type' after the array qualifier, so that given
       a(7).q(volatile).double myarray     // typedef volatile double[7] myarray;
     the type
       q(const).myarray                    // const myarray
     becomes
       a(7).q(const volatile).double       // const volatile double[7]
     and NOT
       q(const).a(7).q(volatile).double    // non-sensical type
  */
  if (r && Len(r) && SwigType_isarray(type)) {
    List *r_elem;
    String *r_qual;
    int r_sz;
    r_elem = SwigType_split(r);
    r_sz = Len(r_elem);
    r_qual = Getitem(r_elem, r_sz-1);
    if (SwigType_isqualifier(r_qual)) {
      String *new_r;
      String *new_type;
      List *type_elem;
      String *type_qual;
      String *r_qual_arg;
      int i, type_sz;

      type_elem = SwigType_split(type);
      type_sz = Len(type_elem);

      for (i = 0; i < type_sz; ++i) {
        String *e = Getitem(type_elem, i);
        if (!SwigType_isarray(e))
          break;
      }
      type_qual = Copy(Getitem(type_elem, i));
      r_qual_arg = SwigType_parm(r_qual);
      SwigType_add_qualifier(type_qual, r_qual_arg);
      Delete(r_qual_arg);
      Setitem(type_elem, i, type_qual);

      new_r = NewStringEmpty();
      for (i = 0; i < r_sz-1; ++i) {
        Append(new_r, Getitem(r_elem, i));
      }
      new_type = NewStringEmpty();
      for (i = 0; i < type_sz; ++i) {
        Append(new_type, Getitem(type_elem, i));
      }
#ifdef SWIG_DEBUG
      Printf(stdout, "r+type='%s%s' new_r+new_type='%s%s'\n", r, type, new_r, new_type);
#endif

      Delete(r);
      r = new_r;
      newtype = 1;
      type = new_type;
      Delete(type_elem);
    }
    Delete(r_elem);
  }

  Append(r, type);
  if (newtype) {
    Delete(type);
  }

return_result:
#ifdef SWIG_TYPEDEF_RESOLVE_CACHE
  {
    String *key = NewString(t);
    if (r) {
      SwigType *r1;
      Setattr(typedef_resolve_cache, key, r);
      Setmeta(r, "scope", resolved_scope);
      r1 = Copy(r);
      Delete(r);
      r = r1;
    }
    Delete(key);
  }
#endif
  return r;
}

/* -----------------------------------------------------------------------------
 * SwigType_typedef_resolve_all()
 *
 * Fully resolve a type down to its most basic datatype
 * ----------------------------------------------------------------------------- */

SwigType *SwigType_typedef_resolve_all(const SwigType *t) {
  SwigType *n;
  SwigType *r;
  int count = 0;

  /* Check to see if the typedef resolve has been done before by checking the cache */
  if (!typedef_all_cache) {
    typedef_all_cache = NewHash();
  }
  r = Getattr(typedef_all_cache, t);
  if (r) {
    return Copy(r);
  }

#ifdef SWIG_DEBUG
  Printf(stdout, "SwigType_typedef_resolve_all start ... %s\n", t);
#endif
  /* Recursively resolve the typedef */
  r = NewString(t);
  while ((n = SwigType_typedef_resolve(r))) {
    Delete(r);
    r = n;
    if (++count >= 512) {
      Swig_error(Getfile(t), Getline(t), "Recursive typedef detected resolving '%s' to '%s' to '%s' and so on...\n", SwigType_str(t, 0), SwigType_str(SwigType_typedef_resolve(t), 0), SwigType_str(SwigType_typedef_resolve(SwigType_typedef_resolve(t)), 0));
      break;
    }
  }

  /* Add the typedef to the cache for next time it is looked up */
  {
    String *key;
    SwigType *rr = Copy(r);
    key = NewString(t);
    Setattr(typedef_all_cache, key, rr);
    Delete(key);
    Delete(rr);
  }
#ifdef SWIG_DEBUG
  Printf(stdout, "SwigType_typedef_resolve_all end   === %s => %s\n", t, r);
#endif
  return r;
}


/* -----------------------------------------------------------------------------
 * SwigType_typedef_qualified()
 *
 * Given a type declaration, this function tries to fully qualify it so that the
 * resulting type can be used in the global scope. The type name is resolved in
 * the current scope.
 *
 * It provides a fully qualified name, not necessarily a fully expanded name.
 * When a using declaration or using directive is found the type may not be fully
 * expanded, but it will be resolved and fully qualified for use in the global scope.
 *
 * This function is for looking up scopes to qualify a type. It does not resolve
 * C typedefs, it just qualifies them. See SwigType_typedef_resolve for resolving.
 *
 * If the unary scope operator (::) is used as a prefix to the type to denote global
 * scope, it is left in place.
 * ----------------------------------------------------------------------------- */

SwigType *SwigType_typedef_qualified(const SwigType *t) {
  List *elements;
  String *result;
  int i, len;

  if (!typedef_qualified_cache)
    typedef_qualified_cache = NewHash();
  result = Getattr(typedef_qualified_cache, t);
  if (result) {
    String *rc = Copy(result);
    return rc;
  }

  result = NewStringEmpty();
  elements = SwigType_split(t);
  len = Len(elements);
  for (i = 0; i < len; i++) {
    String *ty = 0;
    String *e = Getitem(elements, i);
    if (SwigType_issimple(e)) {
      if (!SwigType_istemplate(e)) {
	String *isenum = 0;
	if (SwigType_isenum(e)) {
	  isenum = NewString("enum ");
	  ty = NewString(Char(e) + 5);
	  e = ty;
	}
	resolved_scope = 0;
	if (typedef_resolve(current_scope, e) && resolved_scope) {
	  /* resolved_scope contains the scope that actually resolved the symbol */
	  String *qname = Getattr(resolved_scope, "qname");
	  if (qname) {
	    Insert(e, 0, "::");
	    Insert(e, 0, qname);
	  }
	} else {
	  if (Swig_scopename_check(e)) {
	    String *qlast;
	    String *qname;
	    Swig_scopename_split(e, &qname, &qlast);
	    if (qname) {
	      String *tqname = SwigType_typedef_qualified(qname);
	      Clear(e);
	      Printf(e, "%s::%s", tqname, qlast);
	      Delete(qname);
	      Delete(tqname);
	    }
	    Delete(qlast);

	    /* Automatic template instantiation might go here??? */
	  } else {
	    /* It's a bare name.  It's entirely possible, that the
	       name is part of a namespace. We'll check this by unrolling
	       out of the current scope */

	    Typetab *cs = current_scope;
	    if (cs) {
	      Typetab *found_scope = SwigType_find_scope(cs, e);
	      if (found_scope) {
		String *qs = SwigType_scope_name(found_scope);
		Clear(e);
		Append(e, qs);
		Delete(qs);
	      }
	    }
	  }
	}
	if (isenum) {
	  Insert(e, 0, isenum);
	  Delete(isenum);
	}
      } else {
	/* Template.  We need to qualify template parameters as well as the template itself */
	String *tprefix, *qprefix;
	String *tsuffix;
	Iterator pi;
	Parm *p;
	List *parms;
	ty = Swig_symbol_template_deftype(e, current_symtab);
	e = ty;
	parms = SwigType_parmlist(e);
	tprefix = SwigType_templateprefix(e);
	tsuffix = SwigType_templatesuffix(e);
	qprefix = SwigType_typedef_qualified(tprefix);
	Append(qprefix, "<(");
	pi = First(parms);
	while ((p = pi.item)) {
	  /* TODO: the logic here should be synchronised with that in symbol_template_qualify() in symbol.c */
	  String *qt = SwigType_typedef_qualified(p);
	  if (Equal(qt, p)) {	/*  && (!Swig_scopename_check(qt))) */
	    /* No change in value.  It is entirely possible that the parameter is an integer value.
	       If there is a symbol table associated with this scope, we're going to check for this */

	    if (current_symtab) {
	      Node *lastnode = 0;
	      String *value = Copy(p);
	      while (1) {
		Node *n = Swig_symbol_clookup(value, current_symtab);
		if (n == lastnode)
		  break;
		lastnode = n;
		if (n) {
		  char *ntype = Char(nodeType(n));
		  if (strcmp(ntype, "enumitem") == 0) {
		    /* An enum item.   Generate a fully qualified name */
		    String *qn = Swig_symbol_qualified(n);
		    if (Len(qn)) {
		      Append(qn, "::");
		      Append(qn, Getattr(n, "name"));
		      Delete(value);
		      value = qn;
		      continue;
		    } else {
		      Delete(qn);
		      break;
		    }
		  } else if ((strcmp(ntype, "cdecl") == 0) && (Getattr(n, "value"))) {
		    Delete(value);
		    value = Copy(Getattr(n, "value"));
		    continue;
		  }
		}
		break;
	      }
	      Append(qprefix, value);
	      Delete(value);
	    } else {
	      Append(qprefix, p);
	    }
	  } else {
	    Append(qprefix, qt);
	  }
	  Delete(qt);
	  pi = Next(pi);
	  if (pi.item) {
	    Append(qprefix, ",");
	  }
	}
	Append(qprefix, ")>");
	Append(qprefix, tsuffix);
	Delete(tsuffix);
	Clear(e);
	Append(e, qprefix);
	Delete(tprefix);
	Delete(qprefix);
	Delete(parms);
      }
      Append(result, e);
      Delete(ty);
    } else if (SwigType_isfunction(e)) {
      List *parms = SwigType_parmlist(e);
      String *s = NewString("f(");
      Iterator pi;
      pi = First(parms);
      while (pi.item) {
	String *pq = SwigType_typedef_qualified(pi.item);
	Append(s, pq);
	Delete(pq);
	pi = Next(pi);
	if (pi.item) {
	  Append(s, ",");
	}
      }
      Append(s, ").");
      Append(result, s);
      Delete(s);
      Delete(parms);
    } else if (SwigType_isarray(e)) {
      String *ndim;
      String *dim = SwigType_parm(e);
      ndim = Swig_symbol_string_qualify(dim, 0);
      Printf(result, "a(%s).", ndim);
      Delete(dim);
      Delete(ndim);
    } else {
      Append(result, e);
    }
  }
  Delete(elements);
  {
    String *key, *cresult;
    key = NewString(t);
    cresult = NewString(result);
    Setattr(typedef_qualified_cache, key, cresult);
    Delete(key);
    Delete(cresult);
  }
  return result;
}

/* ----------------------------------------------------------------------------- 
 * SwigType_istypedef()
 *
 * Checks a typename to see if it is a typedef.
 * ----------------------------------------------------------------------------- */

int SwigType_istypedef(const SwigType *t) {
  String *type;

  type = SwigType_typedef_resolve(t);
  if (type) {
    Delete(type);
    return 1;
  } else {
    return 0;
  }
}


/* -----------------------------------------------------------------------------
 * SwigType_typedef_using()
 *
 * Processes a 'using' declaration to import types from one scope into another.
 * Name is a qualified name like A::B.
 * ----------------------------------------------------------------------------- */

int SwigType_typedef_using(const_String_or_char_ptr name) {
  String *base;
  String *td;
  String *prefix;
  Typetab *s;
  Typetab *tt = 0;

  String *defined_name = 0;

  /* Printf(stdout, "using %s\n", name); */

  if (!Swig_scopename_check(name))
    return -1;			/* Not properly qualified */
  base = Swig_scopename_last(name);

  /* See if the base is already defined in this scope */
  if (Getattr(current_typetab, base)) {
    Delete(base);
    return -1;
  }

  /* See if the using name is a scope */
  /*  tt = SwigType_find_scope(current_scope,name);
     Printf(stdout,"tt = %p, name = '%s'\n", tt, name); */

  /* We set up a typedef  B --> A::B */
  Setattr(current_typetab, base, name);

  /* Find the scope name where the symbol is defined */
  td = SwigType_typedef_resolve(name);
  /*  Printf(stdout,"td = '%s' %p\n", td, resolved_scope); */
  if (resolved_scope) {
    defined_name = Getattr(resolved_scope, "qname");
    if (defined_name) {
      defined_name = Copy(defined_name);
      Append(defined_name, "::");
      Append(defined_name, base);
      /*  Printf(stdout,"defined_name = '%s'\n", defined_name); */
      tt = SwigType_find_scope(current_scope, defined_name);
    }
  }
  if (td)
    Delete(td);


  /* Figure out the scope the using directive refers to */
  {
    prefix = Swig_scopename_prefix(name);
    if (prefix) {
      s = SwigType_find_scope(current_scope, prefix);
      if (s) {
	Hash *ttab = Getattr(s, "typetab");
	if (!Getattr(ttab, base) && defined_name) {
	  Setattr(ttab, base, defined_name);
	}
      }
    }
  }

  if (tt) {
    /* Using directive had its own scope.  We need to create a new scope for it */
    SwigType_new_scope(base);
    SwigType_inherit_scope(tt);
    SwigType_pop_scope();
  }

  if (defined_name)
    Delete(defined_name);
  Delete(prefix);
  Delete(base);
  return 0;
}

/* -----------------------------------------------------------------------------
 * SwigType_isclass()
 *
 * Determines if a type defines a class or not.   A class is defined by
 * its type-table entry maps to itself.  Note: a pointer to a class is not
 * a class.
 * ----------------------------------------------------------------------------- */

int SwigType_isclass(const SwigType *t) {
  SwigType *qty, *qtys;
  int isclass = 0;

  qty = SwigType_typedef_resolve_all(t);
  qtys = SwigType_strip_qualifiers(qty);
  if (SwigType_issimple(qtys)) {
    String *td = SwigType_typedef_resolve(qtys);
    if (td) {
      Delete(td);
    }
    if (resolved_scope) {
      isclass = 1;
    }
    /* Hmmm. Not a class.  If a template, it might be uninstantiated */
    if (!isclass) {
      String *tp = SwigType_istemplate_templateprefix(qtys);
      if (tp && Strcmp(tp, t) != 0) {
	isclass = SwigType_isclass(tp);
      }
      Delete(tp);
    }
  }
  Delete(qty);
  Delete(qtys);
  return isclass;
}

/* -----------------------------------------------------------------------------
 * SwigType_type()
 *
 * Returns an integer code describing the datatype.  This is only used for
 * compatibility with SWIG1.1 language modules and is likely to go away once
 * everything is based on typemaps.
 * ----------------------------------------------------------------------------- */

int SwigType_type(const SwigType *t) {
  char *c;
  /* Check for the obvious stuff */
  c = Char(t);

  if (strncmp(c, "p.", 2) == 0) {
    if (SwigType_type(c + 2) == T_CHAR)
      return T_STRING;
    else if (SwigType_type(c + 2) == T_WCHAR)
      return T_WSTRING;
    else
      return T_POINTER;
  }
  if (strncmp(c, "a(", 2) == 0)
    return T_ARRAY;
  if (strncmp(c, "r.", 2) == 0)
    return T_REFERENCE;
  if (strncmp(c, "z.", 2) == 0)
    return T_RVALUE_REFERENCE;
  if (strncmp(c, "m(", 2) == 0)
    return T_MPOINTER;
  if (strncmp(c, "q(", 2) == 0) {
    while (*c && (*c != '.'))
      c++;
    if (*c)
      return SwigType_type(c + 1);
    Printf(stderr, "*** Internal error: Invalid type string '%s'\n", t);
    Exit(EXIT_FAILURE);
  }
  if (strncmp(c, "f(", 2) == 0)
    return T_FUNCTION;

  /* Look for basic types */
  if (strcmp(c, "int") == 0)
    return T_INT;
  if (strcmp(c, "long") == 0)
    return T_LONG;
  if (strcmp(c, "short") == 0)
    return T_SHORT;
  if (strcmp(c, "unsigned") == 0)
    return T_UINT;
  if (strcmp(c, "unsigned short") == 0)
    return T_USHORT;
  if (strcmp(c, "unsigned long") == 0)
    return T_ULONG;
  if (strcmp(c, "unsigned int") == 0)
    return T_UINT;
  if (strcmp(c, "char") == 0)
    return T_CHAR;
  if (strcmp(c, "signed char") == 0)
    return T_SCHAR;
  if (strcmp(c, "unsigned char") == 0)
    return T_UCHAR;
  if (strcmp(c, "wchar_t") == 0)
    return T_WCHAR;
  if (strcmp(c, "float") == 0)
    return T_FLOAT;
  if (strcmp(c, "double") == 0)
    return T_DOUBLE;
  if (strcmp(c, "long double") == 0)
    return T_LONGDOUBLE;
  if (!cparse_cplusplus && (strcmp(c, "float _Complex") == 0))
    return T_FLTCPLX;
  if (!cparse_cplusplus && (strcmp(c, "double _Complex") == 0))
    return T_DBLCPLX;
  if (!cparse_cplusplus && (strcmp(c, "_Complex") == 0))
    return T_COMPLEX;
  if (strcmp(c, "void") == 0)
    return T_VOID;
  if (strcmp(c, "bool") == 0)
    return T_BOOL;
  if (strcmp(c, "long long") == 0)
    return T_LONGLONG;
  if (strcmp(c, "unsigned long long") == 0)
    return T_ULONGLONG;
  if (strncmp(c, "enum ", 5) == 0)
    return T_INT;
  if (strcmp(c, "auto") == 0)
    return T_AUTO;

  if (strcmp(c, "v(...)") == 0)
    return T_VARARGS;
  /* Hmmm. Unknown type */
  if (SwigType_istypedef(t)) {
    int r;
    SwigType *nt = SwigType_typedef_resolve(t);
    r = SwigType_type(nt);
    Delete(nt);
    return r;
  }
  return T_USER;
}

/* -----------------------------------------------------------------------------
 * SwigType_alttype()
 *
 * Returns the alternative value type needed in C++ for class value
 * types. When swig is not sure about using a plain $ltype value,
 * since the class doesn't have a default constructor, or it can't be
 * assigned, you will get back 'SwigValueWrapper<(type)>'.
 *
 * This is the default behavior unless:
 *
 *  1.- swig detects a default_constructor and 'allocate:default_constructor'
 *      attribute.
 *
 *  2.- swig doesn't mark 'type' as non-assignable.
 *
 *  3.- the user specifies that the value wrapper is not needed by using
 *      %feature("novaluewrapper") like so:
 *
 *        %feature("novaluewrapper") MyOpaqueClass;
 *        class MyOpaqueClass;
 *
 * The user can also force the use of the value wrapper with
 * %feature("valuewrapper").
 * ----------------------------------------------------------------------------- */

SwigType *SwigType_alttype(const SwigType *t, int local_tmap) {
  Node *n;
  SwigType *w = 0;
  int use_wrapper = 0;
  SwigType *td = 0;

  if (!cparse_cplusplus)
    return 0;

  if (value_wrapper_mode == 0) {
    /* old partial use of SwigValueTypes, it can fail for opaque types */
    if (local_tmap)
      return 0;
    if (SwigType_isclass(t)) {
      SwigType *ftd = SwigType_typedef_resolve_all(t);
      td = SwigType_strip_qualifiers(ftd);
      Delete(ftd);
      n = Swig_symbol_clookup(td, 0);
      if (n) {
	if (GetFlag(n, "feature:valuewrapper")) {
	  use_wrapper = 1;
	} else {
	  if (Checkattr(n, "nodeType", "class")
	      && (!Getattr(n, "allocate:default_constructor")
		  || (Getattr(n, "allocate:noassign")))) {
	    use_wrapper = !GetFlag(n, "feature:novaluewrapper") || GetFlag(n, "feature:nodefault");
	  }
	}
      } else {
	if (SwigType_issimple(td) && SwigType_istemplate(td)) {
	  use_wrapper = 1;
	}
      }
    }
  } else {
    /* safe use of SwigValueTypes, it can fail with some typemaps */
    SwigType *ftd = SwigType_typedef_resolve_all(t);
    td = SwigType_strip_qualifiers(ftd);
    Delete(ftd);
    if (SwigType_type(td) == T_USER) {
      use_wrapper = 1;
      n = Swig_symbol_clookup(td, 0);
      if (n) {
	if ((Checkattr(n, "nodeType", "class")
	     && !Getattr(n, "allocate:noassign")
	     && (Getattr(n, "allocate:default_constructor")))
	    || (GetFlag(n, "feature:novaluewrapper"))) {
	  use_wrapper = GetFlag(n, "feature:valuewrapper");
	}
      }
    }
  }

  if (use_wrapper) {
    w = NewStringf("SwigValueWrapper<(%s)>", td);
  }
  Delete(td);
  return w;
}

/* ----------------------------------------------------------------------------
 *                         * * * WARNING * * *                            ***
 *                                                                        ***
 * Don't even think about modifying anything below this line unless you   ***
 * are completely on top of *EVERY* subtle aspect of the C++ type system  ***
 * and you are prepared to suffer endless hours of agony trying to        ***
 * debug the SWIG run-time type checker after you break it.               ***
 * ------------------------------------------------------------------------- */

/* -----------------------------------------------------------------------------
 * SwigType_remember()
 *
 * This function "remembers" a datatype that was used during wrapper code generation
 * so that a type-checking table can be generated later on. It is up to the language
 * modules to actually call this function--it is not done automatically.
 *
 * Type tracking is managed through two separate hash tables.  The hash 'r_mangled'
 * is mapping between mangled type names (used in the target language) and 
 * fully-resolved C datatypes used in the source input.   The second hash 'r_resolved'
 * is the inverse mapping that maps fully-resolved C datatypes to all of the mangled
 * names in the scripting languages.  For example, consider the following set of
 * typedef declarations:
 *
 *      typedef double Real;
 *      typedef double Float;
 *      typedef double Point[3];
 *
 * Now, suppose that the types 'double *', 'Real *', 'Float *', 'double[3]', and
 * 'Point' were used in an interface file and "remembered" using this function.
 * The hash tables would look like this:
 *
 * r_mangled {
 *   _p_double : [ p.double, a(3).double ]
 *   _p_Real   : [ p.double ]
 *   _p_Float  : [ p.double ]
 *   _Point    : [ a(3).double ]
 *
 * r_resolved {
 *   p.double     : [ _p_double, _p_Real, _p_Float ]
 *   a(3).double  : [ _p_double, _Point ]
 * }
 *
 * Together these two hash tables can be used to determine type-equivalency between
 * mangled typenames.  To do this, we view the two hash tables as a large graph and
 * compute the transitive closure.
 * ----------------------------------------------------------------------------- */

static Hash *r_mangled = 0;	/* Hash mapping mangled types to fully resolved types */
static Hash *r_resolved = 0;	/* Hash mapping resolved types to mangled types       */
static Hash *r_ltype = 0;	/* Hash mapping mangled names to their local c type   */
static Hash *r_clientdata = 0;	/* Hash mapping resolved types to client data         */
static Hash *r_mangleddata = 0;	/* Hash mapping mangled types to client data         */
static Hash *r_remembered = 0;	/* Hash of types we remembered already */

static void (*r_tracefunc) (const SwigType *t, String *mangled, String *clientdata) = 0;

void SwigType_remember_mangleddata(String *mangled, const_String_or_char_ptr clientdata) {
  if (!r_mangleddata) {
    r_mangleddata = NewHash();
  }
  Setattr(r_mangleddata, mangled, clientdata);
}


void SwigType_remember_clientdata(const SwigType *t, const_String_or_char_ptr clientdata) {
  String *mt;
  SwigType *lt;
  Hash *h;
  SwigType *fr;
  SwigType *qr;
  String *tkey;
  String *cd;
  Hash *lthash;

  if (!r_mangled) {
    r_mangled = NewHash();
    r_resolved = NewHash();
    r_ltype = NewHash();
    r_clientdata = NewHash();
    r_remembered = NewHash();
  }

  {
    String *last;
    last = Getattr(r_remembered, t);
    if (last && (Cmp(last, clientdata) == 0))
      return;
  }

  tkey = Copy(t);
  cd = clientdata ? NewString(clientdata) : NewStringEmpty();
  Setattr(r_remembered, tkey, cd);
  Delete(tkey);
  Delete(cd);

  mt = SwigType_manglestr(t);	/* Create mangled string */

  if (r_tracefunc) {
    (*r_tracefunc) (t, mt, (String *) clientdata);
  }

  if (SwigType_istypedef(t)) {
    lt = Copy(t);
  } else {
    lt = SwigType_ltype(t);
  }

  lthash = Getattr(r_ltype, mt);
  if (!lthash) {
    lthash = NewHash();
    Setattr(r_ltype, mt, lthash);
  }
  Setattr(lthash, lt, "1");
  Delete(lt);

  fr = SwigType_typedef_resolve_all(t);	/* Create fully resolved type */
  qr = SwigType_typedef_qualified(fr);
  Delete(fr);

  /* Added to deal with possible table bug */
  fr = SwigType_strip_qualifiers(qr);
  Delete(qr);

  /*Printf(stdout,"t = '%s'\n", t);
     Printf(stdout,"fr= '%s'\n\n", fr); */

  h = Getattr(r_mangled, mt);
  if (!h) {
    h = NewHash();
    Setattr(r_mangled, mt, h);
    Delete(h);
  }
  Setattr(h, fr, mt);

  h = Getattr(r_resolved, fr);
  if (!h) {
    h = NewHash();
    Setattr(r_resolved, fr, h);
    Delete(h);
  }
  Setattr(h, mt, fr);

  if (clientdata) {
    String *cd = Getattr(r_clientdata, fr);
    if (cd) {
      if (Strcmp(clientdata, cd) != 0) {
	Printf(stderr, "*** Internal error. Inconsistent clientdata for type '%s'\n", SwigType_str(fr, 0));
	Printf(stderr, "*** '%s' != '%s'\n", clientdata, cd);
	assert(0);
      }
    } else {
      String *cstr = NewString(clientdata);
      Setattr(r_clientdata, fr, cstr);
      Delete(cstr);
    }
  }

  /* If the remembered type is a reference, we also remember the pointer version.
     This is to prevent odd problems with mixing pointers and references--especially
     when different functions are using different typenames (via typedef). */

  if (SwigType_isreference(t)) {
    SwigType *tt = Copy(t);
    SwigType_del_reference(tt);
    SwigType_add_pointer(tt);
    SwigType_remember_clientdata(tt, clientdata);
  } else if (SwigType_isrvalue_reference(t)) {
    SwigType *tt = Copy(t);
    SwigType_del_rvalue_reference(tt);
    SwigType_add_pointer(tt);
    SwigType_remember_clientdata(tt, clientdata);
  }
}

void SwigType_remember(const SwigType *ty) {
  SwigType_remember_clientdata(ty, 0);
}

void (*SwigType_remember_trace(void (*tf) (const SwigType *, String *, String *))) (const SwigType *, String *, String *) {
  void (*o) (const SwigType *, String *, String *) = r_tracefunc;
  r_tracefunc = tf;
  return o;
}

/* -----------------------------------------------------------------------------
 * SwigType_equivalent_mangle()
 *
 * Return a list of all of the mangled typenames that are equivalent to another
 * mangled name.   This works as follows: For each fully qualified C datatype
 * in the r_mangled hash entry, we collect all of the mangled names from the
 * r_resolved hash and combine them together in a list (removing duplicate entries).
 * ----------------------------------------------------------------------------- */

static List *SwigType_equivalent_mangle(String *ms, Hash *checked, Hash *found) {
  List *l;
  Hash *h;
  Hash *ch;
  Hash *mh;

  if (found) {
    h = found;
  } else {
    h = NewHash();
  }
  if (checked) {
    ch = checked;
  } else {
    ch = NewHash();
  }
  if (Getattr(ch, ms))
    goto check_exit;		/* Already checked this type */
  Setattr(h, ms, "1");
  Setattr(ch, ms, "1");
  mh = Getattr(r_mangled, ms);
  if (mh) {
    Iterator ki;
    ki = First(mh);
    while (ki.key) {
      Hash *rh;
      if (Getattr(ch, ki.key)) {
	ki = Next(ki);
	continue;
      }
      Setattr(ch, ki.key, "1");
      rh = Getattr(r_resolved, ki.key);
      if (rh) {
	Iterator rk;
	rk = First(rh);
	while (rk.key) {
	  Setattr(h, rk.key, "1");
	  SwigType_equivalent_mangle(rk.key, ch, h);
	  rk = Next(rk);
	}
      }
      ki = Next(ki);
    }
  }
check_exit:
  if (!found) {
    l = Keys(h);
    Delete(h);
    Delete(ch);
    return l;
  } else {
    return 0;
  }
}

/* -----------------------------------------------------------------------------
 * SwigType_clientdata_collect()
 *
 * Returns the clientdata field for a mangled type-string.
 * ----------------------------------------------------------------------------- */

static
String *SwigType_clientdata_collect(String *ms) {
  Hash *mh;
  String *clientdata = 0;

  if (r_mangleddata) {
    clientdata = Getattr(r_mangleddata, ms);
    if (clientdata)
      return clientdata;
  }

  mh = Getattr(r_mangled, ms);
  if (mh) {
    Iterator ki;
    ki = First(mh);
    while (ki.key) {
      clientdata = Getattr(r_clientdata, ki.key);
      if (clientdata)
	break;
      ki = Next(ki);
    }
  }
  return clientdata;
}




/* -----------------------------------------------------------------------------
 * SwigType_inherit()
 *
 * Record information about inheritance.  We keep a hash table that keeps
 * a mapping between base classes and all of the classes that are derived
 * from them.
 *
 * subclass is a hash that maps base-classes to all of the classes derived from them.
 *
 * derived - name of derived class
 * base - name of base class
 * cast - additional casting code when casting from derived to base
 * conversioncode - if set, overrides the default code in the function when casting
 *                from derived to base
 * ----------------------------------------------------------------------------- */

static Hash *subclass = 0;
static Hash *conversions = 0;

void SwigType_inherit(String *derived, String *base, String *cast, String *conversioncode) {
  Hash *h;
  String *dd = 0;
  String *bb = 0;
  if (!subclass)
    subclass = NewHash();

  /* Printf(stdout,"'%s' --> '%s'  '%s'\n", derived, base, cast); */

  if (SwigType_istemplate(derived)) {
    String *ty = SwigType_typedef_resolve_all(derived);
    dd = SwigType_typedef_qualified(ty);
    derived = dd;
    Delete(ty);
  }
  if (SwigType_istemplate(base)) {
    String *ty = SwigType_typedef_resolve_all(base);
    bb = SwigType_typedef_qualified(ty);
    base = bb;
    Delete(ty);
  }

  /* Printf(stdout,"'%s' --> '%s'  '%s'\n", derived, base, cast); */

  h = Getattr(subclass, base);
  if (!h) {
    h = NewHash();
    Setattr(subclass, base, h);
    Delete(h);
  }
  if (!Getattr(h, derived)) {
    Hash *c = NewHash();
    if (cast)
      Setattr(c, "cast", cast);
    if (conversioncode)
      Setattr(c, "convcode", conversioncode);
    Setattr(h, derived, c);
    Delete(c);
  }

  Delete(dd);
  Delete(bb);
}

/* -----------------------------------------------------------------------------
 * SwigType_issubtype()
 *
 * Determines if a t1 is a subtype of t2, ie, is t1 derived from t2
 * ----------------------------------------------------------------------------- */

int SwigType_issubtype(const SwigType *t1, const SwigType *t2) {
  SwigType *ft1, *ft2;
  String *b1, *b2;
  Hash *h;
  int r = 0;

  if (!subclass)
    return 0;

  ft1 = SwigType_typedef_resolve_all(t1);
  ft2 = SwigType_typedef_resolve_all(t2);
  b1 = SwigType_base(ft1);
  b2 = SwigType_base(ft2);

  h = Getattr(subclass, b2);
  if (h) {
    if (Getattr(h, b1)) {
      r = 1;
    }
  }
  Delete(ft1);
  Delete(ft2);
  Delete(b1);
  Delete(b2);
  /* Printf(stdout, "issubtype(%s,%s) --> %d\n", t1, t2, r); */
  return r;
}

/* -----------------------------------------------------------------------------
 * SwigType_inherit_equiv()
 *
 * Modify the type table to handle C++ inheritance
 * ----------------------------------------------------------------------------- */

static void SwigType_inherit_equiv(File *out) {
  String *ckey;
  String *prefix, *base;
  String *mprefix, *mkey;
  Hash *sub;
  Hash *rh;
  List *rlist;
  List *r_resolved_sorted_keys;
  Iterator rk, bk, ck;

  if (!conversions)
    conversions = NewHash();
  if (!subclass)
    subclass = NewHash();

  r_resolved_sorted_keys = SortedKeys(r_resolved, Strcmp);
  rk = First(r_resolved_sorted_keys);
  while (rk.item) {
    List *sub_sorted_keys;
    /* rkey is a fully qualified type.  We strip all of the type constructors off of it just to get the base */
    base = SwigType_base(rk.item);
    /* Check to see whether the base is recorded in the subclass table */
    sub = Getattr(subclass, base);
    Delete(base);
    if (!sub) {
      rk = Next(rk);
      continue;
    }

    /* This type has subclasses.  We now need to walk through these subtypes and generate pointer conversion functions */

    rh = Getattr(r_resolved, rk.item);
    rlist = NewList();
    for (ck = First(rh); ck.key; ck = Next(ck)) {
      Append(rlist, ck.key);
    }
    /*    Printf(stdout,"rk.item = '%s'\n", rk.item);
       Printf(stdout,"rh = %p '%s'\n", rh,rh); */

    sub_sorted_keys = SortedKeys(sub, Strcmp);
    bk = First(sub_sorted_keys);
    while (bk.item) {
      prefix = SwigType_prefix(rk.item);
      Append(prefix, bk.item);
      /*      Printf(stdout,"set %p = '%s' : '%s'\n", rh, SwigType_manglestr(prefix),prefix); */
      mprefix = SwigType_manglestr(prefix);
      Setattr(rh, mprefix, prefix);
      mkey = SwigType_manglestr(rk.item);
      ckey = NewStringf("%s+%s", mprefix, mkey);
      if (!Getattr(conversions, ckey)) {
	String *convname = NewStringf("%sTo%s", mprefix, mkey);
	String *lkey = SwigType_lstr(rk.item, 0);
	String *lprefix = SwigType_lstr(prefix, 0);
        Hash *subhash = Getattr(sub, bk.item);
        String *convcode = Getattr(subhash, "convcode");
        if (convcode) {
          char *newmemoryused = Strstr(convcode, "newmemory"); /* see if newmemory parameter is used in order to avoid unused parameter warnings */
          String *fn = Copy(convcode);
          Replaceall(fn, "$from", "x");
          Printf(out, "static void *%s(void *x, int *%s) {", convname, newmemoryused ? "newmemory" : "SWIGUNUSEDPARM(newmemory)");
          Printf(out, "%s", fn);
        } else {
          String *cast = Getattr(subhash, "cast");
          Printf(out, "static void *%s(void *x, int *SWIGUNUSEDPARM(newmemory)) {", convname);
          Printf(out, "\n    return (void *)((%s) ", lkey);
          if (cast)
            Printf(out, "%s", cast);
          Printf(out, " ((%s) x));\n", lprefix);
        }
	Printf(out, "}\n");
	Setattr(conversions, ckey, convname);
	Delete(ckey);
	Delete(lkey);
	Delete(lprefix);

	/* This inserts conversions for typedefs */
	{
	  Hash *r = Getattr(r_resolved, prefix);
	  if (r) {
	    Iterator rrk;
	    rrk = First(r);
	    while (rrk.key) {
	      Iterator rlk;
	      String *rkeymangle;

	      /* Make sure this name equivalence is not due to inheritance */
	      if (Cmp(prefix, Getattr(r, rrk.key)) == 0) {
		rkeymangle = Copy(mkey);
		ckey = NewStringf("%s+%s", rrk.key, rkeymangle);
		if (!Getattr(conversions, ckey)) {
		  Setattr(conversions, ckey, convname);
		}
		Delete(ckey);
		for (rlk = First(rlist); rlk.item; rlk = Next(rlk)) {
		  ckey = NewStringf("%s+%s", rrk.key, rlk.item);
		  Setattr(conversions, ckey, convname);
		  Delete(ckey);
		}
		Delete(rkeymangle);
		/* This is needed to pick up other alternative names for the same type.
		   Needed to make templates work */
		Setattr(rh, rrk.key, rrk.item);
	      }
	      rrk = Next(rrk);
	    }
	  }
	}
	Delete(convname);
      }
      Delete(prefix);
      Delete(mprefix);
      Delete(mkey);
      bk = Next(bk);
    }
    Delete(sub_sorted_keys);
    rk = Next(rk);
    Delete(rlist);
  }
  Delete(r_resolved_sorted_keys);
}

/* -----------------------------------------------------------------------------
 * SwigType_type_table()
 *
 * Generate the type-table for the type-checker.
 * ----------------------------------------------------------------------------- */

void SwigType_emit_type_table(File *f_forward, File *f_table) {
  Iterator ki;
  String *types, *table, *cast, *cast_init, *cast_temp;
  Hash *imported_types;
  List *mangled_list;
  List *table_list = NewList();
  int i = 0;

  if (!r_mangled) {
    r_mangled = NewHash();
    r_resolved = NewHash();
  }

  Printf(f_table, "\n/* -------- TYPE CONVERSION AND EQUIVALENCE RULES (BEGIN) -------- */\n\n");

  SwigType_inherit_equiv(f_table);

   /*#define DEBUG 1*/
#ifdef DEBUG
  Printf(stdout, "---r_mangled---\n");
  Swig_print(r_mangled, 2);

  Printf(stdout, "---r_resolved---\n");
  Swig_print(r_resolved, 2);

  Printf(stdout, "---r_ltype---\n");
  Swig_print(r_ltype, 2);

  Printf(stdout, "---subclass---\n");
  Swig_print(subclass, 2);

  Printf(stdout, "---conversions---\n");
  Swig_print(conversions, 2);

  Printf(stdout, "---r_clientdata---\n");
  Swig_print(r_clientdata, 2);

#endif
  table = NewStringEmpty();
  types = NewStringEmpty();
  cast = NewStringEmpty();
  cast_init = NewStringEmpty();
  imported_types = NewHash();

  Printf(table, "static swig_type_info *swig_type_initial[] = {\n");
  Printf(cast_init, "static swig_cast_info *swig_cast_initial[] = {\n");

  Printf(f_forward, "\n/* -------- TYPES TABLE (BEGIN) -------- */\n\n");

  mangled_list = SortedKeys(r_mangled, Strcmp);
  for (ki = First(mangled_list); ki.item; ki = Next(ki)) {
    List *el;
    Iterator ei;
    String *nt;
    String *ln;
    String *rn;
    const String *cd;
    Hash *lthash;
    Iterator ltiter;
    Hash *nthash;
    String *cast_temp_conv;
    String *resolved_lstr = 0;
    List *ntlist;

    cast_temp = NewStringEmpty();
    cast_temp_conv = NewStringEmpty();

    Printv(types, "static swig_type_info _swigt_", ki.item, " = {", NIL);
    Append(table_list, ki.item);
    Printf(cast_temp, "static swig_cast_info _swigc_%s[] = {", ki.item);
    i++;

    cd = SwigType_clientdata_collect(ki.item);
    if (!cd)
      cd = "0";

    lthash = Getattr(r_ltype, ki.item);
    nt = 0;
    nthash = NewHash();
    ltiter = First(lthash);
    while (ltiter.key) {
      SwigType *lt = ltiter.key;
      SwigType *rt = SwigType_typedef_resolve_all(lt);
      /* we save the original type and the fully resolved version */
      ln = SwigType_lstr(lt, 0);
      rn = SwigType_lstr(rt, 0);
      if (Equal(ln, rn)) {
        Setattr(nthash, ln, "1");
      } else {
	Setattr(nthash, rn, "1");
	Setattr(nthash, ln, "1");
      }
      if (!resolved_lstr) {
	resolved_lstr = Copy(rn);
      } else if (Len(rn) < Len(resolved_lstr)) {
	Delete(resolved_lstr);
	resolved_lstr = Copy(rn);
      }
      if (SwigType_istemplate(rt)) {
        String *dt = Swig_symbol_template_deftype(rt, 0);
        String *dn = SwigType_lstr(dt, 0);
        if (!Equal(dn, rn) && !Equal(dn, ln)) {
          Setattr(nthash, dn, "1");
        }
        Delete(dt);
        Delete(dn);
      }
      Delete(rt);
      Delete(rn);
      Delete(ln);

      ltiter = Next(ltiter);
    }

    /* now build nt */
    ntlist = SortedKeys(nthash, Strcmp);
    ltiter = First(ntlist);
    nt = 0;
    while (ltiter.item) {
      if (!Equal(resolved_lstr, ltiter.item)) {
	if (nt) {
	   Printf(nt, "|%s", ltiter.item);
	} else {
	   nt = NewString(ltiter.item);
	}
      }
      ltiter = Next(ltiter);
    }
    /* Last in list is a resolved type used by SWIG_TypePrettyName.
     * There can be more than one resolved type and the chosen one is simply the
     * shortest in length, arguably the most user friendly/readable. */
    if (nt) {
       Printf(nt, "|%s", resolved_lstr);
    } else {
       nt = NewString(resolved_lstr);
    }
    Delete(ntlist);
    Delete(nthash);
    Delete(resolved_lstr);

    Printf(types, "\"%s\", \"%s\", 0, 0, (void*)%s, 0};\n", ki.item, nt, cd);

    el = SwigType_equivalent_mangle(ki.item, 0, 0);
    SortList(el, Strcmp);
    for (ei = First(el); ei.item; ei = Next(ei)) {
      String *ckey;
      String *conv;
      ckey = NewStringf("%s+%s", ei.item, ki.item);
      conv = Getattr(conversions, ckey);
      if (conv) {
	Printf(cast_temp_conv, "  {&_swigt_%s, %s, 0, 0},", ei.item, conv);
      } else {
	Printf(cast_temp, "  {&_swigt_%s, 0, 0, 0},", ei.item);
      }
      Delete(ckey);

      if (!Getattr(r_mangled, ei.item) && !Getattr(imported_types, ei.item)) {
	Printf(types, "static swig_type_info _swigt_%s = {\"%s\", 0, 0, 0, 0, 0};\n", ei.item, ei.item);
	Append(table_list, ei.item);

	Printf(cast, "static swig_cast_info _swigc_%s[] = {{&_swigt_%s, 0, 0, 0},{0, 0, 0, 0}};\n", ei.item, ei.item);
	i++;

	Setattr(imported_types, ei.item, "1");
      }
    }
    Delete(el);
    Printf(cast, "%s%s{0, 0, 0, 0}};\n", cast_temp, cast_temp_conv);
    Delete(cast_temp_conv);
    Delete(cast_temp);
    Delete(nt);
  }
  /* print the tables in the proper order */
  SortList(table_list, Strcmp);
  i = 0;
  for (ki = First(table_list); ki.item; ki = Next(ki)) {
    Printf(f_forward, "#define SWIGTYPE%s swig_types[%d]\n", ki.item, i++);
    Printf(table, "  &_swigt_%s,\n", ki.item);
    Printf(cast_init, "  _swigc_%s,\n", ki.item);
  }
  if (i == 0) {
    /* empty arrays are not allowed by ISO C */
    Printf(table, "  NULL\n");
    Printf(cast_init, "  NULL\n");
  }

  Delete(table_list);

  Delete(mangled_list);

  Printf(table, "};\n");
  Printf(cast_init, "};\n");
  Printf(f_table, "%s\n", types);
  Printf(f_table, "%s\n", table);
  Printf(f_table, "%s\n", cast);
  Printf(f_table, "%s\n", cast_init);
  Printf(f_table, "\n/* -------- TYPE CONVERSION AND EQUIVALENCE RULES (END) -------- */\n\n");

  Printf(f_forward, "static swig_type_info *swig_types[%d];\n", i + 1);
  Printf(f_forward, "static swig_module_info swig_module = {swig_types, %d, 0, 0, 0, 0};\n", i);
  Printf(f_forward, "#define SWIG_TypeQuery(name) SWIG_TypeQueryModule(&swig_module, &swig_module, name)\n");
  Printf(f_forward, "#define SWIG_MangledTypeQuery(name) SWIG_MangledTypeQueryModule(&swig_module, &swig_module, name)\n");
  Printf(f_forward, "\n/* -------- TYPES TABLE (END) -------- */\n\n");

  Delete(types);
  Delete(table);
  Delete(cast);
  Delete(cast_init);
  Delete(imported_types);
}
