/*******************************************************************************
 * Simplified Wrapper and Interface Generator  (SWIG)
 * 
 * Author : David Beazley
 *
 * Department of Computer Science        
 * University of Chicago
 * 1100 E 58th Street
 * Chicago, IL  60637
 * beazley@cs.uchicago.edu
 *
 * Please read the file LICENSE for the copyright and terms by which SWIG
 * can be used and distributed.
 *******************************************************************************/

#include "internal.h"
/*******************************************************************************
 * $Header: /cvsroot/SWIG1.1/SWIG/emit.cxx,v 1.3 1999/08/08 21:36:44 beazley Exp $
 *
 * File : emit.cxx
 *
 * This file contains some useful functions for emitting code that would be
 * common to all of the interface languages.  Mainly this function deals with
 * declaring functions external, creating lists of arguments, and making
 * function calls.
 *******************************************************************************/

// -----------------------------------------------------------------------------
// void emit_banner(FILE *f)
// 
// Emits the SWIG identifying banner in the wrapper file
//
// Inputs : f   = FILE handle
//
// Output : None
//
// Side Effects : None
// -----------------------------------------------------------------------------

void emit_banner(FILE *f) {

  extern char *get_time();
  extern char fn_header[];

  fprintf(f,
"/*\n\
 * FILE : %s\n\
 * \n\
 * This file was automatically generated by :\n\
 * Simplified Wrapper and Interface Generator (SWIG)\n\
 * Version %d.%d %s\n\
 * \n\
 * Portions Copyright (c) 1995-1998\n\
 * The University of Utah and The Regents of the University of California.\n\
 * Permission is granted to distribute this file in any manner provided\n\
 * this notice remains intact.\n\
 * \n\
 * Do not make changes to this file--changes will be lost!\n\
 *\n\
 */\n\n", fn_header, SWIG_MAJOR_VERSION, SWIG_MINOR_VERSION, SWIG_SPIN);

  fprintf(f,"\n#define SWIGCODE\n");

}

// -----------------------------------------------------------------------------
// emit_extern_var(char *decl, DataType *t, int extern_type, FILE *f)
// 
// Emits an external variables declaration.   Extern_type defines the
// type of external declaration.  Currently, only C/C++ declarations
// are allowed, but this might be extended to allow Fortran linkage
// someday
//
// Inputs :
//          decl        = Name of the declaration
//          t           = Datatype
//          extern_type = Numeric code indicating type of extern
//                              0   - No "extern"
//                              1,2 -  Normal extern (C/C++)
//          f           = FILE handle
//
// Output : None
//
// Side Effects : None
// -----------------------------------------------------------------------------

void emit_extern_var(char *decl, DataType *t, int extern_type, FILE *f) {
  char *arr = 0;

  if (t->arraystr) arr = t->arraystr;
  else arr = "";

  switch(extern_type) {

  case 0:
    // No extern.  Just a forward reference
    if (t->arraystr)
      t->is_pointer--;

    if (t->is_reference) {
      t->is_pointer--;
      fprintf(f,"%s& %s%s; \n", t->print_full(), decl, arr);
      t->is_pointer++;
    } else {
      fprintf(f,"%s %s%s; \n", t->print_full(), decl,arr);
    }
    if (t->arraystr)
      t->is_pointer++;
    break;
  case 1: case 2:
    if (t->arraystr)
      t->is_pointer--;

    // Normal C/C++ extern
//    fprintf(f,"#line %d \"%s\"\n", line_number, input_file);
    if (t->is_reference) {
      t->is_pointer--;
      fprintf(f,"extern %s& %s%s; \n", t->print_full(), decl,arr);
      t->is_pointer++;
    } else {
      fprintf(f,"extern %s %s%s; \n", t->print_full(), decl,arr);
    }
    if (t->arraystr)
      t->is_pointer++;

  default:
    break;
  }
}

// -----------------------------------------------------------------------------
// emit_extern_func(char *decl, DataType *t, ParmList *L, int extern_type,
//                  FILE *f)
//
// Emits an external function declaration (similiar to emit_extern_var).
// 
// Inputs : 
//          decl        = Name of declaration
//          t           = Return datatype
//          L           = parameter list
//          extern_type = Type of extern
//                           0 - No "extern"
//                           1 - extern
//                           2 - extern "C"
//                           3 - Function declaration (with arg names)
//          f           = FILE Handle
//
// Output : None
//
// Side Effects : None
// 
// -----------------------------------------------------------------------------

void emit_extern_func(char *decl, DataType *t, ParmList *L, int extern_type, FILE *f) {

  switch(extern_type) {
  case 0:
    if (t->is_reference) {
	t->is_pointer--;
	fprintf(f,"%s&", t->print_full());
	t->is_pointer++;
    } else {
      fprintf(f,"%s", t->print_full());
    }
    
    fprintf(f,"%s(", decl);
    L->print_types(f);
    fprintf(f,");\n");
    break;
  case 1:
    // Normal C/C++ extern
//    fprintf(f,"#line %d \"%s\"\n", line_number, input_file);
    if (t->is_reference) {
	t->is_pointer--;
	fprintf(f,"extern %s&", t->print_full());
	t->is_pointer++;
    } else {
      fprintf(f,"extern %s", t->print_full());
    }
    fprintf(f,"%s(", decl);
    L->print_types(f);
    fprintf(f,");\n");
    break;
  case 2:
    // A C++ --- > C Extern
//    fprintf(f,"#line %d \"%s\"\n", line_number, input_file);
    if (t->is_reference) {
	t->is_pointer--;
	fprintf(f,"extern \"C\" %s&", t->print_full());
	t->is_pointer++;
    } else {
      fprintf(f,"extern \"C\" %s", t->print_full());
    }
    fprintf(f,"%s(", decl);
    L->print_types(f);
    fprintf(f,");\n");
    break;
  case 3:
    // A function declaration (for inlining )
    if (t->is_reference) {
	t->is_pointer--;
	fprintf(f,"%s&", t->print_full());
	t->is_pointer++;
    } else {
      fprintf(f,"%s", t->print_full());
    }
    
    fprintf(f,"%s(", decl);
    L->print_args(f);
    fprintf(f,")\n");
    break;
  default:
    break;
  }
}  

// -----------------------------------------------------------------------------
// char *emit_local(int i)
//
// Returns the name of local variable for parameter i
//
// Inputs : i      = Parameter number
//
// Output : NULL terminated ASCII string
//
// Side Effects : Result is left in a static local variable.
// -----------------------------------------------------------------------------

char *emit_local(int i) {
  static char arg[64];

  sprintf(arg,"_arg%d", i);
  return arg;
}

// -----------------------------------------------------------------------------
// int emit_args(char *d, DataType *rt, ParmList *l, FILE *f)
//
// Creates a list of variable declarations for both the return value
// and function parameters.
//
// The return value is always called _result and arguments label as
// _arg0, _arg1, _arg2, etc...
//
// Returns the number of parameters associated with a function.
// 
// Inputs :
//          d     = Name of function
//          rt    = Return type
//          l     = Parameter list
//          f     = FILE Handle
//
// Output : Number of function arguments 
//
// Side Effects : None
//
// Note : This function is obsolete.  Use emit_args below...
// -----------------------------------------------------------------------------

int emit_args(DataType *rt, ParmList *l, FILE *f) {

  Parm *p;
  int   i;
  char  temp[64];
  String def;
  char  *tm;

  // Declare the return variable

  if ((rt->type != T_VOID) || (rt->is_pointer)) {
    if ((rt->type == T_USER) && (!rt->is_pointer)) {

      // Special case for return by "value"

      rt->is_pointer++;
      fprintf(f,"\t %s _result;\n", rt->print_type());
      rt->is_pointer--;
    } else {

      // Normal return value

      fprintf(f,"\t %s _result;\n", rt->print_type());
    }
  }

  // Emit function arguments

  i = 0;
  p = l->get_first();
  while (p != 0) {
    if ((p->t->type != T_VOID) || (p->t->is_pointer))  {
      sprintf(temp,"_arg%d", i);
      if (p->defvalue) {
	if ((p->t->is_reference) || ((p->t->type == T_USER) && (p->call_type == CALL_REFERENCE)))
	    fprintf(f,"\t %s _arg%d = &%s;\n", p->t->print_type(),i, p->defvalue);
	  else	
	    fprintf(f,"\t %s _arg%d = %s;\n", p->t->print_type(),i, p->defvalue);
      } else {
	fprintf(f,"\t %s _arg%d;\n", p->t->print_type(),i);
	tm = typemap_lookup("arginit", typemap_lang, p->t, p->name,"",temp);
	if (tm) {
	  def << tm;
	}
      }

      // Check for ignore or default typemaps
      
      tm = typemap_lookup("default",typemap_lang,p->t,p->name,"",temp);
      if (tm) 
	def << tm;
      tm = typemap_lookup("ignore",typemap_lang,p->t,p->name,"",temp);

      if (tm) {
	def << tm;
	p->ignore = 1;
      }
      tm = typemap_check("build",typemap_lang,p->t,p->name);
      if (tm) {
	p->ignore = 1;
      }
      i++;
    }
    p = l->get_next();
  }

  fprintf(f,"%s",def.get());

  // i now contains number of parameters
  
  return(i);

 }


// -----------------------------------------------------------------------------
// int emit_args(char *d, DataType *rt, ParmList *l, WrapperFunction &f)
//
// Creates a list of variable declarations for both the return value
// and function parameters.
//
// The return value is always called _result and arguments label as
// _arg0, _arg1, _arg2, etc...
//
// Returns the number of parameters associated with a function.
// 
// Inputs :
//          d     = Name of function
//          rt    = Return type
//          l     = Parameter list
//          f     = Wrapper function object
//
// Output : Number of function arguments 
//
// Side Effects : None
//
// -----------------------------------------------------------------------------

int emit_args(DataType *rt, ParmList *l, WrapperFunction &f) {

  Parm *p;
  int   i;
  char *tm;

  // Declare the return variable

  if ((rt->type != T_VOID) || (rt->is_pointer)) {
    if ((rt->type == T_USER) && (!rt->is_pointer)) {

      // Special case for return by "value"
      rt->is_pointer++;
      f.add_local(rt->print_type(), "_result");
      rt->is_pointer--;
    } else {

      // Normal return value

      f.add_local(rt->print_type(), "_result");
    }
  }

  // Emit function arguments

  i = 0;
  p = l->get_first();
  while (p != 0) {
    if ((p->t->type != T_VOID) || (p->t->is_pointer))  {
      char *temp = emit_local(i);
      // Figure out default values
      if (((p->t->is_reference) && (p->defvalue)) || 
	  ((p->t->type == T_USER) && (p->call_type == CALL_REFERENCE) && (p->defvalue))) {
	String deftmp;
	deftmp << "(" << p->t->print_type() << ") &" << p->defvalue;
	f.add_local(p->t->print_type(),temp,deftmp.get());
      } else {
	String deftmp;
	char *dv = 0;
	if (p->defvalue) {
	  deftmp << "(" << p->t->print_type() << ") " << p->defvalue;
	  dv = deftmp.get();
	}
	f.add_local(p->t->print_type(), temp, dv);
	tm = typemap_lookup("arginit", typemap_lang, p->t,p->name,"",temp,&f);
	if (tm) {
	  f.code << tm << "\n";
	}
      }
      // Check for ignore or default typemaps
      tm = typemap_lookup("default",typemap_lang,p->t,p->name,"",temp,&f);
      if (tm) 
	f.code << tm << "\n";
      tm = typemap_lookup("ignore",typemap_lang,p->t,p->name,"",temp,&f);
      if (tm) {
	f.code << tm << "\n";
	p->ignore = 1;
      }
      tm = typemap_check("build",typemap_lang,p->t,p->name);
      if (tm) {
	p->ignore = 1;
      }
      i++;
    }
    p = l->get_next();
  }

  // i now contains number of parameters
  return(i);
}

// -----------------------------------------------------------------------------
// int emit_func_call(char *decl, DataType *t, ParmList *l, FILE *f)
//
// Emits code for a function call.
// 
// Inputs :
//           decl   = name of function
//           t      = Return datatype
//           l      = Parameter list
//           f      = FILE Handle
//
// Output : None
//
// Side Effects : None
//
// Note : This function is obsolete
// -----------------------------------------------------------------------------

void emit_func_call(char *decl, DataType *t, ParmList *l, FILE *f) {

  int  i;
  Parm  *p;

//  fprintf(f,"#line %d \"%s\"\n", line_number, input_file);
  fprintf(f,"\t ");

  // First check if there is a return value

  if ((t->type != T_VOID) || (t->is_pointer)) {
    if ((t->type == T_USER) && (!t->is_pointer)) {

      // Special case for return by "value"
      // Caution : This *will* cause a memory leak if not
      // used properly.

      if (CPlusPlus) {
	fprintf(f,"_result = new %s(", t->print_type());
      } else {
	t->is_pointer++;
	fprintf(f,"_result = %s malloc(sizeof(", t->print_cast());
	t->is_pointer--;
	fprintf(f,"%s));\n", t->print_type());
	fprintf(f,"\t*(_result) = ");
      }
    } else {
      // Check if this is a C++ reference
      if (t->is_reference) {
	t->is_pointer--;
	fprintf(f,"%s& _result_ref = ", t->print_full());
	t->is_pointer++;
      } else {
	
      // Normal return values
	fprintf(f,"_result = %s", t->print_cast());
      }
    }
  }

  // Now print out function call

  fprintf(f,"%s(",decl);

  i = 0;
  p = l->get_first();
  while(p != 0) {
    if ((p->t->type != T_VOID) || (p->t->is_pointer)){
      fprintf(f,"%s",p->t->print_arraycast());
      if ((!p->t->is_reference) && (p->call_type & CALL_VALUE)) fprintf(f,"&");
      if ((!(p->call_type & CALL_VALUE)) &&
	  ((p->t->is_reference) || (p->call_type & CALL_REFERENCE)))
	fprintf(f,"*");
      fprintf(f,"_arg%d",i);
      i++;
    }
    p = l->get_next();
    if (p != 0)
      fprintf(f,",");
  }

  fprintf(f,")");
  if ((t->type == T_USER) && (!t->is_pointer)) {
    if (CPlusPlus) {
      fprintf(f,")");
    }
  }
  fprintf(f,";\n");
  if (t->is_reference) {
    fprintf(f,"\t _result = %s &_result_ref;\n", t->print_cast());
  }
}



// -----------------------------------------------------------------------------
// int emit_func_call(char *decl, DataType *t, ParmList *l, WrapperFunction &f)
//
// Emits code for a function call (new version).
// 
// Exception handling support :
//
//     -  This function checks to see if any sort of exception mechanism
//        has been defined.  If so, we emit the function call in an exception
//        handling block.
//
// Inputs :
//           decl   = name of function
//           t      = Return datatype
//           l      = Parameter list
//           f      = WrapperFunction object
//
// Output : None
//
// Side Effects : None
//
// -----------------------------------------------------------------------------

void emit_func_call(char *decl, DataType *t, ParmList *l, WrapperFunction &f) {

  int  i;
  Parm  *p;
  String fcall;
  String exc;
  char *tm;

//  f.code << "#line " << line_number << " \"" << input_file << "\"\n";
  fcall << tab4;

  // First check if there is a return value

  if ((t->type != T_VOID) || (t->is_pointer)) {
    if ((t->type == T_USER) && (!t->is_pointer)) {

      // Special case for return by "value"
      // Caution : This *will* cause a memory leak if not
      // used properly.

      if (CPlusPlus) {
	fcall << "_result = new " << t->print_type() << "(";
      } else {
	t->is_pointer++;
	fcall << "_result = " << t->print_cast() << " malloc(sizeof(";
	t->is_pointer--;
	fcall << t->print_type() << "));\n";
	fcall << tab4 << "*(_result) = ";
      }
    } else {
      // Check if this is a C++ reference
      if (t->is_reference) {
	t->is_pointer--;
	fcall << t->print_full() << "& _result_ref = ";
	t->is_pointer++;
      } else {

	// Normal return value
	fcall << "_result = " << t->print_cast();
      }
    }
  }

  // Now print out function call

  fcall << decl << "(";

  i = 0;
  p = l->get_first();
  while(p != 0) {
    if ((p->t->type != T_VOID) || (p->t->is_pointer)){
      fcall << p->t->print_arraycast();
      if ((!p->t->is_reference) && (p->call_type & CALL_VALUE))
	fcall << "&";
      if ((!(p->call_type & CALL_VALUE)) &&
	  ((p->t->is_reference) || (p->call_type & CALL_REFERENCE)))
	fcall << "*";
      fcall << emit_local(i);
      i++;
    }
    p = l->get_next();
    if (p != 0)
      fcall << ",";
  }
  fcall << ")";

  if ((t->type == T_USER) && (!t->is_pointer)) {
    if (CPlusPlus) {
      fcall << ")";
    }
  }
  fcall << ";\n";

  if (t->is_reference) {
    fcall << tab4 << "_result = "<< t->print_cast() << " &_result_ref;\n";
  }
  // Check for exception handling

  if ((tm = typemap_lookup("except",typemap_lang,t,decl,"_result",""))) {
    // Found a type-specific mapping
    exc << tm;
    exc.replace("$function",fcall);
    exc.replace("$name",decl);
    f.code << exc;
  } else if ((tm = fragment_lookup("except",typemap_lang, t->id))) {
    exc << tm;
    exc.replace("$function",fcall);
    exc.replace("$name",decl);
    f.code << exc;
  } else {
    f.code << fcall;
  }
}

// -----------------------------------------------------------------------------
// void emit_hex(FILE *f)
// 
// Emits the default C-code to handle pointers.   This is normally contained
// in the SWIG library file 'swigptr.swg'
//
// Inputs : f   = FILE handle
//
// Output : None
//
// Side Effects : None
// -----------------------------------------------------------------------------

void emit_hex(FILE *f) {
  
  int stat;

   // Look for a pointer configuration file

   stat = insert_file("swigptr.swg", f);

   if (stat == -1) {
     fprintf(stderr,"** Fatal error.  Unable to locate 'swigptr.swg'\n");
     SWIG_exit(1);
   }
}

// -----------------------------------------------------------------------------
// void emit_set_get(char *name, char *iname, DataType *type)
//
// Emits a pair of functions to set/get the value of a variable.
// This should be used as backup in case the target language can't
// provide variable linking.
// 
// double foo;
//
// Gets translated into the following :
//
// double foo_set(double x) {
//      return foo = x;
// }
//
// double foo_get() {
//      return foo;
// }
//
// Need to handle special cases for char * and for user
// defined types. 
//
// 1.  char *
//
//     Will free previous contents (if any) and allocate
//     new storage.   Could be risky, but it's a reasonably
//     natural thing to do.
//
// 2.  User_Defined
//     Will assign value from a pointer. 
//     Will return a pointer to current value.
// 
// 
// Inputs :
//          name    = Name of variable
//          iname   = Renamed version of variable
//          type    = Datatype of the variable
//
// Output : None
//
// Side Effects : None
// -----------------------------------------------------------------------------

void emit_set_get(char *name, char *iname, DataType *t) {

    Parm *p;
    ParmList *l;
    String new_name;
    String new_iname;
    String wname;

    // First write a function to set the variable of the variable

    if (!(Status & STAT_READONLY)) {
      if ((t->type == T_USER) && (!t->is_pointer)) {
	t->is_pointer++;
	fprintf(f_header,"static %s %s(%s val) {\n",
		t->print_type(), name_set(name), t->print_type());
	t->is_pointer--;
      } else {
	fprintf(f_header,"static %s %s(%s val) {\n",
		t->print_type(), name_set(name), t->print_type());
      }
      
      if ((t->type != T_VOID) || (t->is_pointer)) {
	if (!t->is_pointer) {
	  
	  // Have a real value here 
	  // If it's a user defined type, we'll do something special.
	  // Otherwise, just assign it.
	  
	  if (t->type != T_USER) {
	    fprintf(f_header,"\t return (%s) (%s = val);\n", t->print_type(), name);
	  } else {
	    fprintf(f_header,"\t %s = *(val);\n", name);
	    t->is_pointer++;
	    fprintf(f_header,"\t return (%s) &%s;\n", t->print_type(),name);
	    t->is_pointer--;
	  }
	} else {
	  
	  // Is a pointer type here.  If string, we do something
	  // special.  Otherwise. No problem.
	  
	  if ((t->type == T_CHAR) && (t->is_pointer == 1)) {
	    if (CPlusPlus) {
	      fprintf(f_header,"\t if (%s) delete %s;\n", name,name);
	      fprintf(f_header,"\t %s = new char[strlen(val)+1];\n",name);
	      fprintf(f_header,"\t strcpy(%s,val);\n", name);
	      fprintf(f_header,"\t return %s;\n", name);
	    } else {
	      fprintf(f_header,"\t if (%s) free(%s);\n", name,name);
	      fprintf(f_header,"\t %s = (char *) malloc(strlen(val)+1);\n",name);
	      fprintf(f_header,"\t strcpy(%s,val);\n", name);
	      fprintf(f_header,"\t return %s;\n", name);
	    }
	  } else {
	    fprintf(f_header,"\t return (%s) (%s = val);\n", t->print_type(), name);
	  }
	}
      }

      fprintf(f_header,"}\n");

      // Now wrap it.

      l = new ParmList;
      p = new Parm(t,0);
      if ((t->type == T_USER) && (!t->is_pointer)) p->t->is_pointer++;
      p->name = new char[1];
      p->name[0] = 0;
      l->append(p);
      
      new_name = name_set(name);
      new_iname = name_set(iname);
      
      if ((t->type == T_USER) && (!t->is_pointer)) {
	t->is_pointer++;
	lang->create_function(new_name, new_iname, t, l);
	t->is_pointer--;
      } else {
	lang->create_function(new_name, new_iname, t, l);
      }
      delete l;
      delete p;
      if (doc_entry) doc_entry->usage << "\n";
    }

    // Now write a function to get the value of the variable

    if ((t->type == T_USER) && (!t->is_pointer)) {
      t->is_pointer++;
      fprintf(f_header,"static %s %s() { \n",
	      t->print_type(), name_get(name));
      fprintf(f_header,"\t return (%s) &%s;\n", t->print_type(), name);
      t->is_pointer--;
    } else {
      fprintf(f_header,"static %s %s() { \n",
	      t->print_type(), name_get(name));
      fprintf(f_header,"\t return (%s) %s;\n", t->print_type(), name);
    }

    fprintf(f_header,"}\n");
    
    // Wrap this function

    l = new ParmList;

    new_name = name_get(name);
    new_iname = name_get(iname);

    if ((t->type == T_USER) && (!t->is_pointer)) {
      t->is_pointer++;
      lang->create_function(new_name, new_iname, t, l);
      t->is_pointer--;
    } else {
      lang->create_function(new_name, new_iname, t, l);
    }
    delete l;
}
  



