/* -----------------------------------------------------------------------------
 * 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.
 *
 * directors.cxx
 *
 * Director support functions.
 * Not all of these may be necessary, and some may duplicate existing functionality
 * in SWIG.  --MR
 * ----------------------------------------------------------------------------- */

#include "swigmod.h"

/* -----------------------------------------------------------------------------
 * Swig_csuperclass_call()
 *
 * Generates a fully qualified method call, including the full parameter list.
 * e.g. "base::method(i, j)"
 * ----------------------------------------------------------------------------- */

String *Swig_csuperclass_call(String *base, String *method, ParmList *l) {
  String *call = NewString("");
  int arg_idx = 0;
  Parm *p;
  if (base) {
    Printf(call, "%s::", base);
  }
  Printf(call, "%s(", method);
  for (p = l; p; p = nextSibling(p)) {
    String *pname = Getattr(p, "name");
    if (!pname && Cmp(Getattr(p, "type"), "void")) {
      pname = NewString("");
      Printf(pname, "arg%d", arg_idx++);
    }
    if (p != l)
      Printf(call, ", ");
    Printv(call, pname, NIL);
  }
  Printf(call, ")");
  return call;
}

/* -----------------------------------------------------------------------------
 * Swig_class_declaration()
 *
 * Generate the start of a class/struct declaration.
 * e.g. "class myclass"
 * ----------------------------------------------------------------------------- */

String *Swig_class_declaration(Node *n, String *name) {
  if (!name) {
    name = Getattr(n, "sym:name");
  }
  String *result = NewString("");
  String *kind = Getattr(n, "kind");
  Printf(result, "%s %s", kind, name);
  return result;
}

/* -----------------------------------------------------------------------------
 * Swig_class_name()
 * ----------------------------------------------------------------------------- */

String *Swig_class_name(Node *n) {
  String *name;
  name = Copy(Getattr(n, "sym:name"));
  return name;
}

/* -----------------------------------------------------------------------------
 * Swig_director_declaration()
 *
 * Generate the full director class declaration, complete with base classes.
 * e.g. "class SwigDirector_myclass : public myclass, public Swig::Director {"
 * ----------------------------------------------------------------------------- */

String *Swig_director_declaration(Node *n) {
  String *classname = Swig_class_name(n);
  String *directorname = Language::instance()->directorClassName(n);
  String *base = Getattr(n, "classtype");
  String *declaration = Swig_class_declaration(n, directorname);

  Printf(declaration, " : public %s, public Swig::Director {\n", base);
  Delete(classname);
  Delete(directorname);
  return declaration;
}


/* -----------------------------------------------------------------------------
 * Swig_method_call()
 * ----------------------------------------------------------------------------- */

String *Swig_method_call(const_String_or_char_ptr name, ParmList *parms) {
  String *func;
  int comma = 0;
  Parm *p = parms;
  SwigType *pt;
  String *nname;

  func = NewString("");
  nname = SwigType_namestr(name);
  Printf(func, "%s(", nname);
  while (p) {
    String *pname;
    pt = Getattr(p, "type");
    if ((SwigType_type(pt) != T_VOID)) {
      if (comma)
	Printf(func, ",");
      pname = Getattr(p, "name");
      Printf(func, "%s", pname);
      comma = 1;
    }
    p = nextSibling(p);
  }
  Printf(func, ")");
  return func;
}

/* -----------------------------------------------------------------------------
 * Swig_method_decl()
 *
 * Return a stringified version of a C/C++ declaration.
 * ----------------------------------------------------------------------------- */

String *Swig_method_decl(SwigType *return_base_type, SwigType *decl, const_String_or_char_ptr id, List *args, int default_args) {
  String *result = NewString("");
  bool conversion_operator = Strstr(id, "operator ") != 0 && !return_base_type;

  Parm *parm = args;
  int arg_idx = 0;
  while (parm) {
    String *type = Getattr(parm, "type");
    String *name = Getattr(parm, "name");
    if (!name && Cmp(type, "void")) {
      name = NewString("");
      Printf(name, "arg%d", arg_idx++);
      Setattr(parm, "name", name);
    }
    parm = nextSibling(parm);
  }

  String *rettype = Copy(decl);
  String *quals = SwigType_pop_function_qualifiers(rettype);
  String *qualifiers = 0;
  if (quals)
    qualifiers = SwigType_str(quals, 0);

  String *popped_decl = SwigType_pop_function(rettype);
  if (return_base_type)
    Append(rettype, return_base_type);

  if (!conversion_operator) {
    SwigType *rettype_stripped = SwigType_strip_qualifiers(rettype);
    String *rtype = SwigType_str(rettype, 0);
    Append(result, rtype);
    if ((SwigType_issimple(rettype_stripped) && return_base_type) || SwigType_isqualifier(rettype))
      Append(result, " ");
    Delete(rtype);
    Delete(rettype_stripped);
  }

  if (id)
    Append(result, id);

  String *args_string = default_args ? ParmList_str_defaultargs(args) : ParmList_str(args);
  Printv(result, "(", args_string, ")", NIL);

  if (qualifiers)
    Printv(result, " ", qualifiers, NIL);

  Delete(args_string);
  Delete(popped_decl);
  Delete(qualifiers);
  Delete(quals);
  Delete(rettype);
  return result;
}

/* -----------------------------------------------------------------------------
 * Swig_director_emit_dynamic_cast()
 *
 * In order to call protected virtual director methods from the target language, we need
 * to add an extra dynamic_cast to call the public C++ wrapper in the director class. 
 * Also for non-static protected members when the allprotected option is on.
 * ----------------------------------------------------------------------------- */

void Swig_director_emit_dynamic_cast(Node *n, Wrapper *f) {
  // TODO: why is the storage element removed in staticmemberfunctionHandler ??
  if ((!is_public(n) && (is_member_director(n) || GetFlag(n, "explicitcall"))) || 
      (is_non_virtual_protected_access(n) && !(Swig_storage_isstatic_custom(n, "staticmemberfunctionHandler:storage") || 
                                               Swig_storage_isstatic(n))
                                          && !Equal(nodeType(n), "constructor"))) {
    Node *parent = Getattr(n, "parentNode");
    String *dirname;
    String *dirdecl;
    dirname = Language::instance()->directorClassName(parent);
    dirdecl = NewStringf("%s *darg = 0", dirname);
    Wrapper_add_local(f, "darg", dirdecl);
    Printf(f->code, "darg = dynamic_cast<%s *>(arg1);\n", dirname);
    Delete(dirname);
    Delete(dirdecl);
  }
}

/* -----------------------------------------------------------------------------
 * Swig_director_parms_fixup()
 *
 * For each parameter in the C++ member function, copy the parameter name
 * to its "lname"; this ensures that Swig_typemap_attach_parms() will do
 * the right thing when it sees strings like "$1" in "directorin" typemaps.
 * ----------------------------------------------------------------------------- */

void Swig_director_parms_fixup(ParmList *parms) {
  Parm *p;
  int i;
  for (i = 0, p = parms; p; p = nextSibling(p), ++i) {
    String *arg = Getattr(p, "name");
    String *lname = 0;

    if (!arg && !Equal(Getattr(p, "type"), "void")) {
      lname = NewStringf("arg%d", i);
      Setattr(p, "name", lname);
    } else
      lname = Copy(arg);

    Setattr(p, "lname", lname);
    Delete(lname);
  }
}

/* -----------------------------------------------------------------------------
 * Swig_director_can_unwrap()
 *
 * Determine whether a function's return type can be returned as an existing
 * target language object instead of creating a new target language object.
 * Must be a director class and only for return by pointer or reference only
 * (not by value or by pointer to pointer etc).
 * ----------------------------------------------------------------------------- */

bool Swig_director_can_unwrap(Node *n) {

  // FIXME: this will not try to unwrap directors returned as non-director
  //        base class pointers!

  bool unwrap = false;

  String *type = Getattr(n, "type");
  SwigType *t = SwigType_typedef_resolve_all(type);
  SwigType *t1 = SwigType_strip_qualifiers(t);
  SwigType *prefix = SwigType_prefix(t1);

  if (Strcmp(prefix, "p.") == 0 || Strcmp(prefix, "r.") == 0) {
    Node *parent = Swig_methodclass(n);
    Node *module = Getattr(parent, "module");
    Node *target = Swig_directormap(module, t1);
    if (target)
      unwrap = true;
  }

  Delete(prefix);
  Delete(t1);
  Delete(t);

  return unwrap;
}
