/* ----------------------------------------------------------------------------- 
 * 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.
 *
 * fragment.c
 *
 * This file manages named code fragments.  Code fragments are typically
 * used to hold helper-code that may or may not be included in the wrapper
 * file (depending on what features are actually used in the interface).
 *
 * By using fragments, it's possible to greatly reduce the amount of
 * wrapper code and to generate cleaner wrapper files. 
 * ----------------------------------------------------------------------------- */

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

static Hash *fragments = 0;
static Hash *looking_fragments = 0;
static int debug = 0;


/* -----------------------------------------------------------------------------
 * Swig_fragment_register()
 *
 * Add a fragment. Use the original Node*, so, if something needs to be
 * changed, lang.cxx doesn't need to be touched again.
 * ----------------------------------------------------------------------------- */

void Swig_fragment_register(Node *fragment) {
  if (Getattr(fragment, "emitonly")) {
    Swig_fragment_emit(fragment);
    return;
  } else {
    String *name = Copy(Getattr(fragment, "value"));
    String *type = Getattr(fragment, "type");
    if (type) {
      SwigType *rtype = SwigType_typedef_resolve_all(type);
      String *mangle = Swig_name_mangle_type(type);
      Append(name, mangle);
      Delete(mangle);
      Delete(rtype);
      if (debug)
	Printf(stdout, "register fragment %s %s\n", name, type);
    }
    if (!fragments) {
      fragments = NewHash();
    }
    if (!Getattr(fragments, name)) {
      String *section = Copy(Getattr(fragment, "section"));
      String *ccode = Copy(Getattr(fragment, "code"));
      Hash *kwargs = Getattr(fragment, "kwargs");
      Setmeta(ccode, "section", section);
      if (kwargs) {
	Setmeta(ccode, "kwargs", kwargs);
      }
      Setfile(ccode, Getfile(fragment));
      Setline(ccode, Getline(fragment));
      /* Replace $descriptor() macros */
      Swig_cparse_replace_descriptor(ccode);
      Setattr(fragments, name, ccode);
      if (debug)
	Printf(stdout, "registering fragment %s %s\n", name, section);
      Delete(section);
      Delete(ccode);
    }
    Delete(name);
  }
}

/* -----------------------------------------------------------------------------
 * Swig_fragment_emit()
 *
 * Emit a fragment
 * ----------------------------------------------------------------------------- */

static
char *char_index(char *str, char c) {
  while (*str && (c != *str))
    ++str;
  return (c == *str) ? str : 0;
}

void Swig_fragment_emit(Node *n) {
  String *code;
  char *pc, *tok;
  String *t;
  String *mangle = 0;
  String *name = 0;
  String *type = 0;

  name = Getattr(n, "value");
  if (!name) {
    name = n;
  }

  if (!fragments) {
    Swig_warning(WARN_FRAGMENT_NOT_FOUND, Getfile(n), Getline(n), "Fragment '%s' not found.\n", name);
    return;
  }

  type = Getattr(n, "type");
  if (type) {
    mangle = Swig_name_mangle_type(type);
  }

  if (debug)
    Printf(stdout, "looking fragment %s %s\n", name, type);
  t = Copy(name);
  tok = Char(t);
  pc = char_index(tok, ',');
  if (pc)
    *pc = 0;
  while (tok) {
    String *name = NewString(tok);
    if (mangle)
      Append(name, mangle);
    if (looking_fragments && Getattr(looking_fragments, name)) {
      return;
    }
    code = Getattr(fragments, name);
    if (debug)
      Printf(stdout, "looking subfragment %s\n", name);
    if (code && (Strcmp(code, "ignore") != 0)) {
      String *section = Getmeta(code, "section");
      Hash *nn = Getmeta(code, "kwargs");
      if (!looking_fragments)
	looking_fragments = NewHash();
      Setattr(looking_fragments, name, "1");
      while (nn) {
	if (Equal(Getattr(nn, "name"), "fragment")) {
	  if (debug)
	    Printf(stdout, "emitting fragment %s %s\n", nn, type);
	  Setfile(nn, Getfile(n));
	  Setline(nn, Getline(n));
	  Swig_fragment_emit(nn);
	}
	nn = nextSibling(nn);
      }
      if (section) {
	File *f = Swig_filebyname(section);
	if (!f) {
	  Swig_error(Getfile(code), Getline(code), "Bad section '%s' in %%fragment declaration for code fragment '%s'\n", section, name);
	} else {
	  if (debug)
	    Printf(stdout, "emitting subfragment %s %s\n", name, section);
	  if (debug)
	    Printf(f, "/* begin fragment %s */\n", name);
	  Printf(f, "%s\n", code);
	  if (debug)
	    Printf(f, "/* end fragment %s */\n\n", name);
	  Setattr(fragments, name, "ignore");
	  Delattr(looking_fragments, name);
	}
      }
    } else if (!code && type) {
      SwigType *rtype = SwigType_typedef_resolve_all(type);
      if (!Equal(type, rtype)) {
	String *name = Copy(Getattr(n, "value"));
	String *mangle = Swig_name_mangle_type(type);
	Append(name, mangle);
	Setfile(name, Getfile(n));
	Setline(name, Getline(n));
	Swig_fragment_emit(name);
	Delete(mangle);
	Delete(name);
      }
      Delete(rtype);
    }

    if (!code) {
      Swig_warning(WARN_FRAGMENT_NOT_FOUND, Getfile(n), Getline(n), "Fragment '%s' not found.\n", name);
    }
    tok = pc ? pc + 1 : 0;
    if (tok) {
      pc = char_index(tok, ',');
      if (pc)
	*pc = 0;
    }
    Delete(name);
  }
  Delete(t);
}
