/********************************************************************
 * Ruby module for SWIG
 *
 * $Id: ruby.cxx,v 1.8 2000/05/24 16:18:29 fukusima Exp $
 *
 * Copyright (C) 1999  Network Applied Communication Laboratory, Inc.
 *
 * Masaki Fukushima
 *
 ********************************************************************/

#include "swig.h"
#include "swigruby.h"

#include <ctype.h>
#include <string.h>

static char *usage = "\
Ruby Options\n\
     -module name     - Set module name\n\
     -toplevel        - Do not define module\n\
     -feature name    - Set feature name (used by `require')\n\
     -extconf         - Generate extconf.rb template\n";

static char *extconf_rb = "\
require 'mkmf'\n\

#%ext%_dir         = with_config('%ext%-dir')\n\
#%ext%_include_dir = %ext%_dir + '/include' if %ext%_dir\n\
#%ext%_include_dir = with_config('%ext%-include-dir', %ext%_include_dir)\n\
#%ext%_lib_dir     = %ext%_dir + '/lib' if %ext%_dir\n\
#%ext%_lib_dir     = with_config('%ext%-lib-dir', %ext%_lib_dir)\n\
#\n\
#$CFLAGS = \"-I#{%ext%_include_dir}\" if %ext%_include_dir\n\
#$LDFLAGS = \"-L#{%ext%_lib_dir}\" if %ext%_lib_dir\n\
\n\
#if have_header('%ext%.h') and have_library('%ext%', '%ext%_init')\n\
%swigrb%\
create_makefile('%ext%')\n\
#end\n";

static String& indent(String& s, char *sp = tab4) {
  sp >> s;
  String rep = sp;
  s.replace("\n", "\n" >> rep);
  return s;
}

static char **split(char *str, int *num) {
  // count number of tokens
  int n = 0;
  char *p = str, *e;
  while (*p) {
    while (isspace(*p)) p++;
    e = p;
    while (*e && !isspace(*e)) e++;
    if (e == p) break;
    n++;
    p = e;
  }
  char **array = new char *[n];
  *num = n;
  
  n = 0;
  p = str;
  while (*p) {
    while (isspace(*p)) p++;
    e = p;
    while (*e && !isspace(*e)) e++;
    if (e == p) break;
    array[n] = new char[(e-p)+1];
    strncpy(array[n], p, (e-p));
    array[n][e-p] = 0;
    n++;
    p = e;
  }
  return array;
}

// ---------------------------------------------------------------------
// RUBY::RUBY()
//
// Constructor
// ---------------------------------------------------------------------
RUBY::RUBY(void) { 
  module = 0;
  modvar = 0;
  feature = 0;
  extconf = 0;
  toplevel = 0;
  import_file = 0;
  current = NO_CPP;
  klass = 0;

  // Python style special method name.
  // Basic
  special_methods.add("__repr__", new String("inspect"));
  special_methods.add("__str__", new String("to_s"));
  special_methods.add("__cmp__", new String("<=>"));
  special_methods.add("__hash__", new String("hash"));
  special_methods.add("__nonzero__", new String("nonzero?"));
  // Callable
  special_methods.add("__call__", new String("call"));
  // Collection
  special_methods.add("__len__", new String("length"));
  special_methods.add("__getitem__", new String("[]"));
  special_methods.add("__setitem__", new String("[]="));
  // Numeric
  special_methods.add("__add__", new String("+"));
  special_methods.add("__sub__", new String("-"));
  special_methods.add("__mul__", new String("*"));
  special_methods.add("__div__", new String("/"));
  special_methods.add("__mod__", new String("%"));
  special_methods.add("__divmod__", new String("divmod"));
  special_methods.add("__pow__", new String("**"));
  special_methods.add("__lshift__", new String("<<"));
  special_methods.add("__rshift__", new String(">>"));
  special_methods.add("__and__", new String("&"));
  special_methods.add("__xor__", new String("^"));
  special_methods.add("__or__", new String("|"));
  special_methods.add("__neg__", new String("-@"));
  special_methods.add("__pos__", new String("+@"));
  special_methods.add("__abs__", new String("abs"));
  special_methods.add("__invert__", new String("~"));
  special_methods.add("__int__", new String("to_i"));
  special_methods.add("__float__", new String("to_f"));
  special_methods.add("__coerce__", new String("coerce"));
}

// ---------------------------------------------------------------------
// RUBY::parse_args(int argc, char *argv[])
//
// Parse command line options and initializes variables.
// ---------------------------------------------------------------------
void RUBY::parse_args(int argc, char *argv[]) {
  // Look for certain command line options
  for (int i = 1; i < argc; i++) {
    if (argv[i]) {
      if (strcmp(argv[i],"-module") == 0) {
	if (argv[i+1]) {
	  set_module(argv[i+1],0);
	  mark_arg(i);
	  mark_arg(i+1);
	  i++;
	} else {
	  arg_error();
	}
      } else if (strcmp(argv[i],"-feature") == 0) {
	if (argv[i+1]) {
	  char *name = argv[i+1];
	  feature = new char [strlen(name)+1];
	  strcpy(feature, name);
	  mark_arg(i);
	  mark_arg(i+1);
	  i++;
	} else {
	  arg_error();
	}
      } else if (strcmp(argv[i],"-extconf") == 0) {
	extconf = 1;
	mark_arg(i);
      } else if (strcmp(argv[i],"-toplevel") == 0) {
	toplevel = 1;
	mark_arg(i);
      } else if (strcmp(argv[i],"-help") == 0) {
	fprintf(stderr,"%s\n", usage);
      }
    }
  }
  // Check consistency
  if (module && toplevel)
    arg_error();

  // Set location of SWIG library
  strcpy(LibDir,"ruby");

  // Add a symbol to the parser for conditional compilation
  add_symbol("SWIGRUBY",0,0);

  // Add typemap definitions
  typemap_lang = "ruby";
}

// ---------------------------------------------------------------------
// RUBY::parse()
//
// Start parsing an interface file.
// ---------------------------------------------------------------------
void RUBY::parse() {
  printf("Generating wrappers for Ruby\n");
  headers();

  // Tell the parser to first include a typemap definition file
  line_number = 0; // for bug in scanner.cxx(scanner_file)
  if (include_file("lang.map") == -1) {
    fprintf(stderr,"Unable to find lang.map!\n");
    SWIG_exit(1);
  }

  // typedef void *VALUE
  DataType value; 
  strcpy(value.name, "void");
  value.type = T_VOID;
  value.is_pointer = 1;
  value.implicit_ptr = 0;
  value.typedef_add("VALUE");

  yyparse();       // Run the SWIG parser
}

// ---------------------------------------------------------------------
// RUBY::set_module(char *mod_name,char **mod_list)
//
// Sets the module name.  Does nothing if it's already set (so it can
// be overridden as a command line option).
//
// mod_list is a NULL-terminated list of additional modules.  This
// is really only useful when building static executables.
//----------------------------------------------------------------------

void RUBY::set_module(char *mod_name, char **mod_list) {
  if (import_file) {
    fprintf(f_init, "%srb_f_require(Qnil, rb_str_new2(\"%s\"));\n", tab4, mod_name);
    delete [] import_file;
    import_file = 0;
  }

  if (module) return;

  if (!feature) {
    feature = new char[strlen(mod_name)+1];
    strcpy(feature, mod_name);
  }

  module = new char[strlen(mod_name)+1];
  strcpy(module, mod_name);
  // module name must be a constant.
  module[0] = toupper(module[0]);

  modvar = new char[1+strlen(module)+1];
  modvar[0] = 'm';
  strcpy(modvar+1, module);

  if (mod_list) {
    other_extern << "#ifdef __cplusplus\n"
		 << "extern \"C\" {\n"
		 << "#endif\n";
    for (int i = 0; mod_list[i] != NULL; i++) {
      other_extern << "extern void Init_" << mod_list[i] << "(void);\n";
      other_init << tab4 << "Init_" << mod_list[i] << "();\n";
    }
    other_extern << "#ifdef __cplusplus\n"
		 << "}\n"
		 << "#endif\n";
  }
}

// ----------------------------------------------------------------------
// RUBY::headers(void)
//
// Generate the appropriate header files for RUBY interface.
// ----------------------------------------------------------------------
void RUBY::headers(void) {
  emit_banner(f_header);
  fprintf(f_header,"/* Implementation : RUBY */\n\n");
  fprintf(f_header,"#define SWIGRUBY\n");

  if (insert_file("ruby.swg", f_header) == -1) {
    fprintf(stderr,"SWIG : Fatal error. Unable to locate ruby.swg."
	    " (Possible installation problem).\n");
    SWIG_exit(1);
  }

  if (!NoInclude) {
    emit_hex(f_header);
  } else {
    fprintf(f_header, "#ifdef __cplusplus\n");
    fprintf(f_header, "extern \"C\" {\n");
    fprintf(f_header, "#endif\n");
    fprintf(f_header, "extern void SWIG_MakePtr(char *, void *, char *);\n");
    fprintf(f_header, "extern void SWIG_RegisterMapping(char *, char *, void *(*)(void *));\n");
    fprintf(f_header, "extern char *SWIG_GetPtr(char *, void **, char *);\n");
    fprintf(f_header, "#ifdef __cplusplus\n");
    fprintf(f_header, "}\n");
    fprintf(f_header, "#endif\n\n");
  }
}

// ---------------------------------------------------------------------
// RUBY::initialize(void)
//
// Produces an initialization function.   Assumes that the module
// name has already been specified.
// ---------------------------------------------------------------------
void RUBY::initialize() {
  if (!module) {
    fprintf(stderr,"SWIG : *** Warning. No module name specified.\n");
    set_module("swig", NULL);         // Pick a default name
  }

  fprintf(f_header,"#define SWIG_init    Init_%s\n", feature);
  fprintf(f_header,"#define SWIG_name    \"%s\"\n\n", module);
  if (!toplevel) fprintf(f_header,"static VALUE %s;\n", modvar);
  fprintf(f_header,"\n%s\n", (char *)other_extern);

  // Start generating the initialization function
  String s;
  s << "\n"
    << "#ifdef __cplusplus\n"
    << "extern \"C\"\n"
    <<"#endif\n"
    << "void Init_" << feature << "(void) {\n"
    << other_init;
  if (!toplevel) {
    s << tab4 << modvar << " = rb_define_module(\"" << module << "\");\n";
  }
  s << "\n";
  fprintf(f_init, s);
  klass = new RClass();

  if (extconf) {
    // generate extconf.rb
    FILE *f_extconf = fopen("extconf.rb", "r");
    if (f_extconf) {
      fprintf(stderr, "Unable to generate extconf.rb. File already exists.\n");
      fclose(f_extconf);
    } else {
      FILE *f_extconf = fopen("extconf.rb", "w");
      if (!f_extconf) {
	perror("Unable to generate extconf.rb.");
      } else {
	String s = extconf_rb;
	s.replace("%ext%", feature);
	if (NoInclude) {
	  s.replace("%swigrb%", "#have_library('swigrb', 'SWIG_MakePtr')\n");
	} else {
	  s.replace("%swigrb%", "");

	}
	fprintf(f_extconf, s);
	fclose(f_extconf);
      }
    }
  }
}

// ---------------------------------------------------------------------
// RUBY::close(void)
//
// Finish the initialization function. Close any additional files and
// resources in use.
// ---------------------------------------------------------------------
void RUBY::close(void) {
  // Finish off our init function
  fprintf(f_init, "\n");
  emit_ptr_equivalence(f_init);
  fprintf(f_init,"}\n");
}

// ---------------------------------------------------------------------
// RUBY::make_wrapper_name(char *cname)
//
// Creates a name for a wrapper function
//              cname = Name of the C function
// ---------------------------------------------------------------------
char *RUBY::make_wrapper_name(char *cname) {
  return name_wrapper(cname,"");
}

// ---------------------------------------------------------------------
// RUBY::create_command(char *cname, char *iname)
//
// Creates a new command from a C function.
//              cname = Name of the C function
//              iname = Name of function in scripting language
//              argc  = Number of arguments
// ---------------------------------------------------------------------
void RUBY::create_command(char *cname, char *iname, int argc) {
  String wname = make_wrapper_name(cname);
  if (CPlusPlus) {
    "VALUEFUNC(" >> wname << ")";
  }
  if (current != NO_CPP)
    iname = klass->strip(iname);
  if (special_methods.lookup(iname)) {
    iname = (char *)(*(String *)special_methods.lookup(iname));
  }

  String s, temp;
  switch (current) {
  case MEMBER_FUNC:
    klass->init << tab4 << "rb_define_method(" << klass->vname << ", \""
		<< iname << "\", " << wname << ", " << argc << ");\n";
    break;
  case CONSTRUCTOR:
    s << tab4 << "rb_define_singleton_method(" << klass->vname
      << ", \"new\", " << wname << ", " << argc << ");\n";
    klass->init.replace("$constructor", s);
    break;
  case MEMBER_VAR:
    temp = iname;
    temp.replace("_set", "=");
    temp.replace("_get", "");
    klass->init << tab4 << "rb_define_method(" << klass->vname << ", \""
		<< temp << "\", " << wname << ", " << argc << ");\n";
    break;
  case STATIC_FUNC:
    klass->init << tab4 << "rb_define_singleton_method(" << klass->vname
		<< ", \"" << iname << "\", " << wname << ", " << argc
		<< ");\n";
    break;
  default:
    if (toplevel) {
      s << tab4 << "rb_define_global_function(\""
	<< iname << "\", " << wname << ", " << argc << ");\n";
    } else {
      s << tab4 << "rb_define_module_function(" << modvar << ", \""
	<< iname << "\", " << wname << ", " << argc << ");\n";
    }
    fprintf(f_init, s);
    break;
  }
}

// ---------------------------------------------------------------------
// RUBY::create_function(char *name, char *iname, DataType *d, ParmList *l)
//
// Create a function declaration and register it with the interpreter.
//              name = Name of real C function
//              iname = Name of function in scripting language
//              t = Return datatype
//              l = Function parameters
// ---------------------------------------------------------------------
void RUBY::create_function(char *name, char *iname, DataType *t, ParmList *l) {
  String source, target;
  char *tm;
  String cleanup, outarg;
  WrapperFunction  f;

  // Ruby needs no destructor wrapper
  if (current == DESTRUCTOR)
    return;

  char *wname = make_wrapper_name(name);
  String mname, inamebuf;
  int predicate = 0;
  switch (current) {
  case MEMBER_FUNC:
  case MEMBER_VAR:
  case STATIC_FUNC:
    mname = klass->strip(iname);
    if (klass->predmethods.lookup(mname)) {
      predicate = 1;
      inamebuf = iname;
      inamebuf << "?";
      iname = (char *)inamebuf;
    }
    break;
  }

  // Get number of arguments
  int numarg = l->numarg();
  int numopt = l->numopt();
  int numignore = l->nparms - numarg;
  int start = 0;
  int use_self = 0;
  switch (current) {
  case MEMBER_FUNC:
  case MEMBER_VAR:
    numarg--;
    start++;
    use_self = 1;
    break;
  }

  int numreq = 0;
  int numoptreal = 0;
  for (int i = start; i < l->nparms; i++) {
    if (!l->get(i)->ignore) {
      if (i >= l->nparms - numopt) numoptreal++;
      else numreq++;
    }
  }
  int vararg = (numoptreal != 0);

  // Now write the wrapper function itself
  f.def << "static VALUE\n" << wname << "(";
  if (vararg) {
    f.def << "int argc, VALUE *argv, VALUE self";
  } else {
    f.def << "VALUE self";
    for (int i = start; i < l->nparms; i++) {
      if (!l->get(i)->ignore) {
	f.def << ", VALUE varg" << i;
      }
    }
  }
  f.def << ") {";

  // Emit all of the local variables for holding arguments.
  if (vararg) {
    for (int i = start; i < l->nparms; i++) {
      if (!l->get(i)->ignore) {
	String s;
	s << "varg" << i;
	f.add_local("VALUE", s);
      }
    }
  }
  f.add_local("VALUE", "vresult", "Qnil");
  int pcount = emit_args(t,l,f);

#if 0
  String temp;
  temp << "/* " << name << " */" << br;
  temp << "/* nparms     = " << l->nparms << " */" << br;
  temp << "/* numarg     = " << numarg << " */" << br;
  temp << "/* numopt     = " << numopt << " */" << br;
  temp << "/* numignore  = " << numignore << " */" << br;
  temp << "/* numoptreal = " << numoptreal << " */" << br;
  temp << "/* pcount     = " << pcount << " */" << br;
  temp << "/* rb_scan_args(argc, argv, \""
       << (numarg-numoptreal) << numoptreal << "\") */" << br;
  temp << br;
  if (numignore != 0) fprintf(stderr, temp);
  f.code << temp;
#endif
  // Emit count to check the number of arguments
  if (vararg) {
    f.code << tab4 << "rb_scan_args(argc, argv, \""
	   << (numarg-numoptreal) << numoptreal << "\"";
    for (int i = start; i < l->nparms; i++) {
      if (!l->get(i)->ignore) {
	f.code << ", &varg" << i;
      }
    }
    f.code << ");\n";
  }

  // Now walk the function parameter list and generate code
  // to get arguments
  int j = 0;                // Total number of non-optional arguments

  for (int i = 0; i < pcount ; i++) {
    Parm &p = (*l)[i];         // Get the ith argument
    source = "";
    target = "";

    // Produce string representation of source and target arguments
    int selfp = (use_self && i == 0);
    if (selfp)
      source << "self";
    else
      source << "varg" << i;
    target << "_arg" << i;

    if (!p.ignore) {
      char *tab = tab4;
      if (j >= (pcount-numopt)) { // Check if parsing an optional argument
	f.code << tab4 << "if (argc > " << j -  start << ") {\n";
	tab = tab8;
      }

      // Get typemap for this argument
#if 0
      tm = ruby_typemap_lookup("in",p.t,(selfp?"self":p.name),source,target,&f);
#else
      tm = ruby_typemap_lookup("in",p.t,p.name,source,target,&f);
#endif
      if (tm) {
	String s = tm;
	f.code << indent(s, tab) << "\n";
	f.code.replace("$arg",source);   // Perform a variable replacement
      } else {
	fprintf(stderr,"%s : Line %d. No typemapping for datatype %s\n",
		input_file,line_number, p.t->print_type());
      }
      if (j >= (pcount-numopt))
	f.code << tab4 << "} \n";
      j++;
    }

    // Check to see if there was any sort of a constaint typemap
    tm = ruby_typemap_lookup("check",p.t,p.name,source,target);
    if (tm) {
      String s = tm;
      f.code << indent(s) << "\n";
      f.code.replace("$arg",source);
    }

    // Check if there was any cleanup code (save it for later)
    tm = ruby_typemap_lookup("freearg",p.t,p.name,target,source);
    if (tm) {
      String s = tm;
      cleanup << indent(s) << "\n";
      cleanup.replace("$arg",source);
    }

    tm = ruby_typemap_lookup("argout",p.t,p.name,target,"vresult");
    if (tm) {
      String s = tm;
      outarg << indent(s) << "\n";
      outarg.replace("$arg",source);
    }
  }

  // Now write code to make the function call
  emit_func_call(name,t,l,f);

  // Return value if necessary
  if ((t->type != T_VOID) || (t->is_pointer)) {
    if (predicate) {
      f.code << tab4 << "vresult = (_result ? Qtrue : Qfalse);\n";
    } else {
      tm = ruby_typemap_lookup("out",t,name,"_result","vresult");
      if (tm) {
	String s = tm;
	f.code << indent(s) << "\n";
      } else {
	fprintf(stderr,"%s : Line %d. No return typemap for datatype %s\n",
		input_file,line_number,t->print_type());
      }
    }
  }

  // Dump argument output code;
  f.code << outarg;

  // Dump the argument cleanup code
  f.code << cleanup;

  // Look for any remaining cleanup.  This processes the %new directive
  if (NewObject) {
    tm = ruby_typemap_lookup("newfree",t,name,"_result","");
    if (tm) {
      String s = tm;
      f.code << indent(s) << "\n";
    }
  }

  // free pragma
  if (current == MEMBER_FUNC && klass->freemethods.lookup(mname)) {
    f.code << tab4 << "DATA_PTR(self) = 0;\n";
  }

  // Special processing on return value.
  tm = ruby_typemap_lookup("ret",t,name,"_result","");
  if (tm) {
    String s = tm;
    f.code << indent(s) << "\n";
  }

  // Wrap things up (in a manner of speaking)
  f.code << tab4 << "return vresult;\n}\n";

  // Substitute the cleanup code
  f.code.replace("$cleanup",cleanup);

  // Emit the function
  f.print(f_wrappers);

  // Now register the function with the language
  create_command(name, iname, (vararg ? -1 : numarg));
}


// ---------------------------------------------------------------------
// RUBY::link_variable(char *name, char *iname, DataType *t)
//
// Create a link to a C variable.
//              name = Name of C variable
//              iname = Name of variable in scripting language
//              t = Datatype of the variable
// ---------------------------------------------------------------------
void RUBY::link_variable(char *name, char *iname, DataType *t) {
  char *tm;
  String getfname, setfname;
  WrapperFunction getf, setf;
  int mod_attr = 0;

  // module attribute?
  if (current == STATIC_VAR || !toplevel)
    mod_attr = 1;

  // create getter
  getfname = name_get(name);
  getf.def << "static VALUE\n" << getfname << "(";
  if (mod_attr) getf.def << "VALUE self";
  getf.def << ") {";
  getf.add_local("VALUE", "_val");
  tm = ruby_typemap_lookup("varout",t,name,name,"_val");
  if (!tm)
    tm = ruby_typemap_lookup("out",t,name,name,"_val");
  if (tm) {
    String s = tm;
    getf.code << indent(s) << "\n";
  } else {
    fprintf(stderr,"%s: Line %d. Unable to link with variable type %s\n",
	    input_file,line_number,t->print_type());
  }
  getf.code << tab4 << "return _val;\n}\n";
  getf.print(f_wrappers);

  if (Status & STAT_READONLY) {
    setfname = "NULL";
  } else {
    // create setter
    setfname = name_set(name);
    if (mod_attr)
      setf.def << "static VALUE\n" << setfname << "(VALUE self, ";
    else
      setf.def << "static void\n" << setfname << "(";
    setf.def << "VALUE _val) {";
    tm = ruby_typemap_lookup("varin",t,name,"_val",name);
    if (!tm)
      tm = ruby_typemap_lookup("in",t,name,"_val",name);
    if (tm) {
      String s = tm;
      setf.code << indent(s) << "\n";
    } else {
      fprintf(stderr,"%s: Line %d. Unable to link with variable type %s\n",
	      input_file,line_number,t->print_type());
    }
    if (mod_attr)
      setf.code << tab4 << "return _val;\n";
    setf.code << "}\n";
    setf.print(f_wrappers);
  }

  // define accessor method
  if (CPlusPlus) {
    "VALUEFUNC(" >> getfname << ")";
    "VALUEFUNC(" >> setfname << ")";
  }

  String s;
  switch (current) {
  case STATIC_VAR:
    // C++ class variable
    s << tab4 << "rb_define_singleton_method(" << klass->vname << ", \""
      << klass->strip(iname) << "\", " << getfname << ", 0);\n";
    if (!(Status & STAT_READONLY)) {
      s << tab4 << "rb_define_singleton_method(" << klass->vname << ", \""
	<< klass->strip(iname) << "=\", " << setfname << ", 1);\n";
    }
    klass->init << s;
    break;
  default:
    // C global variable
    if (toplevel) {
      // wrapped in Ruby global variable
      s << tab4 << "rb_define_virtual_variable(\"" << iname << "\", "
	<< getfname << ", " << setfname << ");\n";
    } else {
      // wrapped in Ruby module attribute
      s << tab4 << "rb_define_singleton_method(" << modvar << ", \""
	<< iname << "\", " << getfname << ", 0);\n";
      if (!(Status & STAT_READONLY)) {
	s << tab4 << "rb_define_singleton_method(" << modvar << ", \""
	  << iname << "=\", " << setfname << ", 1);\n";
      }
    }
    fputs(s, f_init);
    break;
  }
}


// ---------------------------------------------------------------------
// RUBY::validate_const_name(char *name)
//
// Validate constant name.
// ---------------------------------------------------------------------
char *RUBY::validate_const_name(char *name) {
  if (!name || name[0] == '\0')
    return name;

  if (isupper(name[0]))
    return name;

  if (islower(name[0])) {
    name[0] = toupper(name[0]);
    fprintf(stderr,"%s : Line %d. Wrong constant/class/module name "
	    "(corrected to `%s')\n", input_file, line_number, name);
    return name;
  }

  fprintf(stderr,"%s : Line %d. Wrong constant/class/module name\n",
	  input_file, line_number);
  return name;
}

// ---------------------------------------------------------------------
// RUBY::declare_const(char *name, char *iname, DataType *type, char *value)
//
// Makes a constant. 
//              name = Name of the constant
//              iname = Scripting language name of constant
//              type = Datatype of the constant
//              value = Constant value (as a string)
// ---------------------------------------------------------------------
void RUBY::declare_const(char *name, char *iname, DataType *type, char *value) {
  char *tm;

  if (current == CLASS_CONST)
    iname = klass->strip(iname);

  tm = ruby_typemap_lookup("const",type,name,value,iname);
  if (tm) {
    String str = tm;
    str.replace("$value",value);
    if (current == CLASS_CONST) {
      str.replace("$module", klass->vname);
      indent(str);
      klass->init << str << "\n";
    } else {
      if (toplevel)
	str.replace("$module", "rb_cObject");
      else
	str.replace("$module", modvar);
      indent(str);
      fprintf(f_init,"%s\n", (char *)str);
    }
  } else {
    fprintf(stderr,"%s : Line %d. Unable to create constant %s = %s\n",
	    input_file, line_number, type->print_type(), value);
  }
}

// ---------------------------------------------------------------------
// RUBY::ruby_typemap_lookup(char *op, DataType *type, char *pname, char *source, char *target, WrapperFunction *f = 0)
//
// Ruby specific typemap_lookup.
//              op     = string code for type of mapping
//              type   = the datatype
//              pname  = an optional parameter name
//              source = a string with the source variable
//              target = a string containing the target value
//              f      = a wrapper function object (optional)
// ---------------------------------------------------------------------
char *RUBY::ruby_typemap_lookup(char *op, DataType *type, char *pname, char *source, char *target, WrapperFunction *f = 0) {
  static String s;
  char *tm;
  String target_replace = target;
  target = target_replace;

  RClass *cls = (RClass *)classes.lookup(type->name);
  s = "";

  if ((strcmp("out", op) == 0 || strcmp("in", op) == 0)
      && strcmp(type->name, "VALUE") == 0) {
    s << "$target = $source;";
  } else if (strcmp("out", op) == 0 && type->type == T_USER &&
	     type->is_pointer == 1 && cls) {
    const char *vname = (current == CONSTRUCTOR ? "self" : (char *)cls->vname);
    s << "$target = Wrap_" << cls->cname << "(" << vname << ", $source);";
  } else if (strcmp("in", op)==0 && type->type == T_USER &&
	     type->is_pointer == 1 && cls) {
    s << "Get_" << cls->cname << "($source, $target);";
  } else {
    tm = typemap_lookup(op, typemap_lang, type, pname, source, target, f);
    if (tm) {
      return tm;
    }

    s = "";
    if (strcmp("in", op) == 0) {
      char *v = from_VALUE(type, "$source");
      if (v)
	s << "$target = " << v << ";";
    } else if (strcmp("out", op) == 0) {
      char *v = to_VALUE(type, "$source");
      if (v)
	s << "$target = " << v << ";";
    } else if (strcmp("const", op) == 0) {
      char *v = to_VALUE(type, "$value", 1);
      if (v) {
	s << "rb_define_const($module, \"$target\", " << v << ");";
	validate_const_name(target);
      }
    }
  }

  if (strlen(s) == 0) {
    return NULL;
  }

  if (source && strlen(source) > 0)
    s.replace("$source", source);
  if (target && strlen(target) > 0)
    s.replace("$target", target);
  s.replace("$type", type->print_type());
  return s;
}

// ---------------------------------------------------------------------
// RUBY::to_VALUE(DataType *type, char *value, literal)
//
// Makes a VALUE (as a string)
//              type = Datatype of the C value
//              value = C value (as a string)
//              raw = value is raw string (not quoted) ? 
// ---------------------------------------------------------------------
char *RUBY::to_VALUE(DataType *type, char *value, int raw = 0) {
  static String str;

  str = "";
  if (type->is_pointer == 0) {
    switch(type->type) {
    case T_INT:case T_SINT:
    case T_SHORT: case T_SSHORT:
    case T_LONG: case T_SLONG:
    case T_SCHAR:
      str << "INT2NUM(" << value << ")";
      break;
    case T_UINT:
    case T_USHORT:
    case T_ULONG:
    case T_UCHAR:
      str << "UINT2NUM(" << value << ")";
      break;
    case T_DOUBLE:
    case T_FLOAT:
      str << "rb_float_new(" << value << ")";
      break;
    case T_CHAR:
      str << "rb_str_new(&" << value << ", 1)";
      break;
    case T_BOOL:
      str << "(" << value << " ? Qtrue : Qfalse)";
      break;
    default:
      break;
    }
  } else if ((type->type == T_CHAR) && (type->is_pointer == 1)) {
    if (raw)
      str << "rb_str_new2(\"" << value << "\")";
    else
      str << "rb_str_new2(" << value << ")";
  }

  if (strlen(str) == 0)
    return NULL;
  return str;
}

// ---------------------------------------------------------------------
// RUBY::from_VALUE(DataType *type, char *value)
//
// extract VALUE
//              type  = Datatype of the C value
//              value = Ruby VALUE (as a string)
// ---------------------------------------------------------------------
char *RUBY::from_VALUE(DataType *type, char *value) {
  static String str;

  str = "";
  if (type->is_pointer == 0) {
    switch(type->type) {
    case T_INT:case T_SINT:
      str << "NUM2INT(" << value << ")";
      break;
    case T_LONG: case T_SLONG:
      str << "NUM2LONG(" << value << ")";
      break;
    case T_SHORT: case T_SSHORT:
      str << "NUM2SHRT(" << value << ")";
      break;
    case T_UINT:
      str << "NUM2UINT(" << value << ")";
      break;
    case T_ULONG:
      str << "NUM2ULONG(" << value << ")";
      break;
    case T_USHORT:
      str << "NUM2USHRT(" << value << ")";
      break;
    case T_DOUBLE:
    case T_FLOAT:
      str << "NUM2DBL(" << value << ")";
      break;
    case T_CHAR: case T_SCHAR: case T_UCHAR:
      str << "NUM2CHR(" << value << ")";
      break;
    case T_BOOL:
      str << "RTEST(" << value << ")";
      break;
    default:
      break;
    }
  } else if ((type->type == T_CHAR) && (type->is_pointer == 1)) {
    str << "STR2CSTR(" << value << ")";
  }

  if (strlen(str) == 0)
    return NULL;
  return str;
}

// ----------------------------------------------------------------------
// void RUBY::cpp_open_class(char *classname, char *classrename, char *ctype, int strip)
//
// Open a new C++ class.
//
// INPUTS:
//      name   = Real name of the C++ class
//      rename = New name of the class (if %name was used)
//      ctype  = Class type (struct, class, union, etc...)
//      strip  = Flag indicating whether we should strip the
//               class type off
//
// This function is in charge of creating a new class.  The SWIG parser has
// already processed the entire class definition prior to calling this
// function (which should simplify things considerably).
//
// ----------------------------------------------------------------------
void RUBY::cpp_open_class(char *cname, char *rename, char *ctype, int strip) {
  this->Language::cpp_open_class(cname, rename, ctype, strip);

  klass = (RClass *)classes.lookup(cname);

  if (strip) {
    klass->type = "";
    klass->type << klass->cname;
  } else {
    klass->type = "";
    klass->type << ctype << " " << klass->cname;
  }

  klass->header << "\nVALUE " << klass->vname << ";\n";
  klass->init << "\n" <<  tab4;
  if (toplevel) {
    klass->init << klass->vname << " = rb_define_class("
		<< "\"" << klass->name << "\", $super);\n";
  } else {
    klass->init << klass->vname << " = rb_define_class_under(" << modvar
		<< ", \"" << klass->name << "\", $super);\n";
  }
  klass->includes.replace("$class", klass->vname);
  klass->init << klass->includes;
  klass->init << "$constructor";

  klass->header
    << "$markproto"
    << "$freeproto"
    << "#define Wrap_" << klass->cname << "(klass, ptr) (\\\n"
    << tab4 << "(ptr) ? Data_Wrap_Struct(klass"
    << ", $markfunc, $freefunc, ptr) : Qnil )\n"
    << "#define Get_" << klass->cname << "(val, ptr) {\\\n"
    << tab4 << "if (NIL_P(val)) ptr = NULL;\\\n"
    << tab4 << "else {\\\n"
    << tab8 << "if (!rb_obj_is_kind_of(val, " << klass->vname << "))\\\n"
    << tab8 << tab4 << "rb_raise(rb_eTypeError, \"wrong argument type"
    <<                 " (expected " << klass->name << ")\");\\\n"
    << tab8 << "Data_Get_Struct(val, " << klass->type << ", ptr);\\\n"
    << tab8 << "if (!ptr) rb_raise(rb_eRuntimeError, \""
    <<         "This " << klass->name << " already released\");\\\n"
    << tab4 << "}\\\n"
    << "}\n";
}

// ---------------------------------------------------------------------
// void RUBY::cpp_close_class()
//
// Close the current class
// ---------------------------------------------------------------------
void RUBY::cpp_close_class() {
  this->Language::cpp_close_class();

  klass->header.replace("$markfunc", "0");
  klass->header.replace("$markproto", "");
  if (!klass->destructor_defined) {
    klass->header.replace("$freefunc", "0");
    klass->header.replace("$freeproto", "");
  }
  fprintf(f_header, klass->header);

  klass->aliases.replace("$class", klass->vname);
  klass->init << klass->aliases;

  String s;
  s << tab4 << "rb_undef_method(CLASS_OF(" << klass->vname
    << "), \"new\");\n";
  klass->init.replace("$constructor", s);
  klass->init.replace("$super", "rb_cObject");

  fprintf(f_init, klass->init);

  klass = 0;
}

// ----------------------------------------------------------------------
// void RUBY::cpp_inherit(char **baseclass, int mode)
//
// Inherit attributes from given baseclass.  
//
// INPUT:
//     baseclass       = NULL terminate list of baseclasses
//
// ---------------------------------------------------------------------

void RUBY::cpp_inherit(char **baseclass, int mode) {
  if (!baseclass)
    return;

  for (int i = 0; baseclass[i]; i++) {
    RClass *super = (RClass *)classes.lookup(baseclass[i]);
    if (super) {
      klass->init.replace("$super", super->vname);
      break; // ignore multiple inheritance
    }
  }
}

// ----------------------------------------------------------------------
// void RUBY::cpp_member_func(char *name, char *iname, DataType *t, ParmList *l)
//
// Method for adding C++ member function
//
// INPUTS:
//      name        - name of the member function
//      iname       - renaming (if given)
//      t           - Return datatype
//      l           - Parameter list
//
// By default, we're going to create a function of the form :
//
//         Foo_bar(this,args)
//
// Where Foo is the classname, bar is the member name and the this pointer
// is explicitly attached to the beginning.
//
// The renaming only applies to the member function part, not the full
// classname.
//
// ---------------------------------------------------------------------
void RUBY::cpp_member_func(char *name, char *iname, DataType *t, ParmList *l) {
  current = MEMBER_FUNC;
  this->Language::cpp_member_func(name, iname, t, l);
  current = NO_CPP;
}

// ---------------------------------------------------------------------
// void RUBY::cpp_constructor(char *name, char *iname, ParmList *l)
//
// Method for adding C++ member constructor
//
// INPUTS:
//      name     - Name of the constructor (usually same as classname)
//      iname    - Renamed version
//      l        - parameters
// --------------------------------------------------------------------
void RUBY::cpp_constructor(char *name, char *iname, ParmList *l) {
  current = CONSTRUCTOR;
  this->Language::cpp_constructor(name, iname, l);
  current = NO_CPP;
}

// ---------------------------------------------------------------------
// void RUBY::cpp_destructor(char *name, char *iname)
//
// Method for adding C++ member destructor
//
// INPUT:
//     name        -  Name of the destructor (classname)
//     iname       -  Renamed destructor
//
// --------------------------------------------------------------------
void RUBY::cpp_destructor(char *name, char *newname) {
  current = DESTRUCTOR;
  this->Language::cpp_destructor(name, newname);

  String freefunc, freeproto, freebody;
  freefunc << "free_" << klass->cname;
  freeproto << "static void " << freefunc << "(" << klass->type << " *);\n";
  freebody  << "static void\n"
	    << freefunc << "(" << klass->type << " *ptr) {\n"
	    << tab4 << name_destroy(name) << "(ptr);\n"
	    << "}\n";
  if (CPlusPlus) {
    "VOIDFUNC(" >> freefunc << ")";
  }

  klass->header.replace("$freefunc", freefunc);
  klass->header.replace("$freeproto", freeproto);
  fprintf(f_wrappers, freebody);

  klass->destructor_defined = 1;
  current = NO_CPP;
}

// ---------------------------------------------------------------------
// void RUBY::cpp_variable(char *name, char *iname, DataType *t)
//
// Wrap a C++ data member
//
// INPUTS :
//      name       = Name of the C++ member
//     iname       = Name as used in the interpreter
//         t       = Datatype
//
// This creates a pair of functions to set/get the variable of a member.
// --------------------------------------------------------------------
void RUBY::cpp_variable(char *name, char *iname, DataType *t) {
  current = MEMBER_VAR;
  this->Language::cpp_variable(name, iname, t);
  current = NO_CPP;
}

// -----------------------------------------------------------------------
// void RUBY::cpp_static_func(char *name, char *iname, DataType *t, ParmList *l)
//
// Wrap a static C++ function
//
// INPUTS:
//      name           = Real name of the function
//     iname           = New name in interpreter
//      t              = Return datatype
//      l              = Parameters
// ----------------------------------------------------------------------
void RUBY::cpp_static_func(char *name, char *iname, DataType *t, ParmList *l) {
  current = STATIC_FUNC;
  this->Language::cpp_static_func(name, iname, t, l);
  current = NO_CPP;
}

// ----------------------------------------------------------------------
// void RUBY::cpp_declare_const(char *name, char *iname, DataType *t, char *value)
//
// Create a C++ constant
//
// INPUTS :
//       name          = Real name of the constant
//       iname         = new name
//       t             = Datatype
//       value         = value as a string
//
// ---------------------------------------------------------------------

void RUBY::cpp_declare_const(char *name, char *iname, DataType *type, char *value) {
  current = CLASS_CONST;
  this->Language::cpp_declare_const(name, iname, type, value);
  current = NO_CPP;
}

// ---------------------------------------------------------------------
// void RUBY::cpp_static_var(char *name, char *iname, DataType *t)
//
// Wrap a static C++ variable
//
// INPUT :
//      name        = name of the variable
//     iname        = interpreter name
//         t        = Datatype
//
// ---------------------------------------------------------------------
void RUBY::cpp_static_var(char *name, char *iname, DataType *t) {
  current = STATIC_VAR;
  this->Language::cpp_static_var(name, iname, t);
  current = NO_CPP;
}

// -----------------------------------------------------------------------
// RUBY::cpp_class_decl(char *name, char *rename, char *type)
//
// A forward class declaration
// -----------------------------------------------------------------------
void RUBY::cpp_class_decl(char *cname, char *rename, char *type) {
  String valid_name = (rename ? rename : cname);
  validate_const_name(valid_name);
  klass->set_name(cname, rename, valid_name);
  classes.add(cname, klass);
  if (type && strlen(type) > 0) {
    String temp;
    temp << type << " " << cname;
    classes.add(temp, klass);
  }
  String s;
  s << "extern VALUE " << klass->vname << ";\n";
  fprintf(f_header, s);

  klass = new RClass();
}

// --------------------------------------------------------------------
// void RUBY::pragma(char *target, char *var, char *value)
//
// A pragma declaration
// --------------------------------------------------------------------
void RUBY::pragma(char *lang, char *cmd, char *value) {
  if (strcmp(lang, "ruby") != 0)
    return;

  if (strcmp(cmd, "free") == 0) {
    char name[64];
    if (sscanf(value, " %s ", name) != 1) {
      fprintf(stderr, "%s : Line %d. Invalid free pragma.\n",
	      input_file, line_number);
      return;
    }
    klass->freemethods.add(name, name);
  } else if (strcmp(cmd, "include") == 0) {
    char name[64];
    if (sscanf(value, " %s ", name) != 1) {
      fprintf(stderr, "%s : Line %d. Invalid include pragma.\n",
	      input_file, line_number);
      return;
    }
    klass->includes << tab4 << "rb_include_module($class, "
		    << "rb_eval_string(\"" << name << "\"));\n";
  } else if (strcmp(cmd, "alias") == 0) {
    char alias[64], name[64];
    if (sscanf(value, " %s %s ", alias, name) != 2) {
      fprintf(stderr, "%s : Line %d. Invalid alias pragma.\n",
	      input_file, line_number);
      return;
    }
    klass->aliases << tab4 << "rb_define_alias($class, "
		  << "\"" << alias << "\", \"" << name << "\");\n";
  } else if (strcmp(cmd, "pred") == 0) {
    char *tok;
    tok = strtok(value, " \t");
    while (tok) {
      klass->predmethods.add(tok, tok, 0);
      tok = strtok(0, " \t");
    }
  } else if (strcmp(cmd, "debug") == 0) {
    fprintf(f_header, "/* %s */\n", value);
    fprintf(f_wrappers, "/* %s */\n", value);
    fprintf(f_init, "/* %s */\n", value);
    fprintf(stderr, "%s\n", value);
  } else {
    fprintf(stderr, "%s : Line %d. Unrecognized pragma.\n",
	    input_file, line_number);
  }
}

// ---------------------------------------------------------------------
// RUBY::import(char *filename)
//
// Imports a SWIG module as a separate file.
//----------------------------------------------------------------------

void RUBY::import(char *filename) {
  if (import_file) delete [] import_file;
  import_file = copy_string(filename);
}

/*
 * Local Variables:
 * c-basic-offset: 2
 * End:
 */
