/* -----------------------------------------------------------------------------
 * 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.
 *
 * extend.c
 *
 * Extensions support (%extend)
 * ----------------------------------------------------------------------------- */

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

static Hash *extendhash = 0; /* Hash table of added methods */

/* -----------------------------------------------------------------------------
 * Swig_extend_hash()
 *
 * Access the extend hash
 * ----------------------------------------------------------------------------- */
Hash *Swig_extend_hash(void) {
  if (!extendhash)
    extendhash = NewHash();
  return extendhash;
}

/* -----------------------------------------------------------------------------
 * Swig_extend_merge()
 *
 * Extension merge.  This function is used to handle the %extend directive
 * when it appears before a class definition.   To handle this, the %extend
 * actually needs to take precedence.  Therefore, we will selectively nuke symbols
 * from the current symbol table, replacing them with the added methods.
 * ----------------------------------------------------------------------------- */

void Swig_extend_merge(Node *cls, Node *am) {
  Node *n;

  n = firstChild(am);
  while (n) {
    String *symname;
    if (Strcmp(nodeType(n), "constructor") == 0) {
      symname = Getattr(n, "sym:name");
      if (symname) {
        if (Strcmp(symname, Getattr(n, "name")) == 0) {
          /* If the name and the sym:name of a constructor are the same,
             then it hasn't been renamed.  However---the name of the class
             itself might have been renamed so we need to do a consistency
             check here */
          if (Getattr(cls, "sym:name")) {
            Setattr(n, "sym:name", Getattr(cls, "sym:name"));
          }
        }
      }
    }

    symname = Getattr(n, "sym:name");
    DohIncref(symname);
    if ((symname) && (!Getattr(n, "error"))) {
      Node *c;
      /* Remove node from its symbol table */
      Swig_symbol_remove(n);
      c = Swig_symbol_add(symname, n);
      if (c != n) {
        /* Conflict with previous definition.  Nuke previous definition */
        String *e = NewStringEmpty();
        String *en = NewStringEmpty();
        String *ec = NewStringEmpty();
        Printf(ec, "Redefinition of identifier '%s' by %%extend ignored,", symname);
        Printf(en, "%%extend definition of '%s'.", symname);
        SWIG_WARN_NODE_BEGIN(n);
        Swig_warning(WARN_PARSE_REDEFINED, Getfile(c), Getline(c), "%s\n", ec);
        Swig_warning(WARN_PARSE_REDEFINED, Getfile(n), Getline(n), "%s\n", en);
        SWIG_WARN_NODE_END(n);
        Printf(e, "%s:%d:%s\n%s:%d:%s\n", Getfile(c), Getline(c), ec, Getfile(n), Getline(n), en);
        Setattr(c, "error", e);
        Delete(e);
        Delete(en);
        Delete(ec);
        Swig_symbol_remove(c);       /* Remove class definition */
        Swig_symbol_add(symname, n); /* Insert extend definition */
      }
    }
    n = nextSibling(n);
  }
}

/* -----------------------------------------------------------------------------
 * Swig_extend_append_previous()
 * ----------------------------------------------------------------------------- */

void Swig_extend_append_previous(Node *cls, Node *am) {
  Node *n, *ne;
  Node *pe = 0;
  Node *ae = 0;

  if (!am)
    return;

  n = firstChild(am);
  while (n) {
    ne = nextSibling(n);
    set_nextSibling(n, 0);
    /* typemaps and fragments need to be prepended */
    if (((Cmp(nodeType(n), "typemap") == 0) || (Cmp(nodeType(n), "fragment") == 0))) {
      if (!pe)
        pe = Swig_cparse_new_node("extend");
      appendChild(pe, n);
    } else {
      if (!ae)
        ae = Swig_cparse_new_node("extend");
      appendChild(ae, n);
    }
    n = ne;
  }
  if (pe)
    prependChild(cls, pe);
  if (ae)
    appendChild(cls, ae);
}

/* -----------------------------------------------------------------------------
 * Swig_extend_unused_check()
 *
 * Check for unused %extend.  Special case, don't report unused
 * extensions for templates
 * ----------------------------------------------------------------------------- */

void Swig_extend_unused_check(void) {
  Iterator ki;

  if (!extendhash)
    return;
  for (ki = First(extendhash); ki.key; ki = Next(ki)) {
    if (!Strchr(ki.key, '<')) {
      SWIG_WARN_NODE_BEGIN(ki.item);
      Swig_warning(WARN_PARSE_EXTEND_UNDEF, Getfile(ki.item), Getline(ki.item), "%%extend defined for an undeclared class %s.\n", SwigType_namestr(ki.key));
      SWIG_WARN_NODE_END(ki.item);
    }
  }
}
