/* $Id: ctoobj.c,v 1.2 2009/01/04 15:49:31 kiesling Exp $ */

/*
  This file is part of Ctalk.
  Copyright © 2005-2009 Robert Kiesling, ctalk@ctalklang.org.
  Permission is granted to copy this software provided that this copyright 
  notice is included in all source code modules.

  This program is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation; either version 2 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program; if not, write to the Free Software Foundation, 
  Inc., 51 Franklin St., Fifth Floor, Boston, MA 02110-1301 USA.
*/

/*
 *   Translate C function calls to objects.
 */

#include <stdio.h>
#include <string.h>
#include "object.h"
#include "message.h"
#include "ctalk.h"

/*
 *  At the moment, we can use the c_message stack to parse 
 *  a C function call.
 */

extern MESSAGE *c_messages[N_MESSAGES + 1];  /* Declared in cparse.c.  */
extern int c_message_ptr;                   

extern CFUNC *functions;                     /* Declared in rt_cvar.c. */

extern HASHTAB declared_typedefs;

/*
 *  Call with arg_object of class, "Expr," with the name and 
 *  the value the complete function call.  When changing this
 *  function, also change fmt_c_to_obj_call (), below.
 */

int generate_c_to_obj_call (OBJECT *rcvr_obj, METHOD *method,
			    OBJECT *arg_object) {

  int i,
    start_idx,
    end_idx;
  CFUNC *fn;
  MESSAGE *m_arg;
  char *return_class;

  start_idx = get_stack_top (c_messages);
  end_idx = tokenize (c_message_push, arg_object -> __o_name);

  for (i = start_idx, fn = NULL; i > end_idx; i--) {

    m_arg = c_messages[i];

    if ((m_arg -> tokentype == WHITESPACE) ||
	(m_arg -> tokentype == NEWLINE))
      continue;

    switch (m_arg -> tokentype)
      {
      case LABEL:
	/*
	 *  Use the same IS_FN() macro as in eval_arg (), 
	 *  but, because there are a few different places
	 *  where function definitions reside, then write
	 *  a function to return a return value, and also
	 *  a function that can create an expression for
	 *  an undefined function on the fly (and also
	 *  in eval_arg ()).
	 *
	 *  Then, add the macro cache to 
	 *  preprocess_template () in fn_tmpl.c.
	 */
	if (IS_DEFINED_FN (M_NAME (m_arg))) {
	  if ((return_class = fn_return_class (M_NAME (m_arg))) != NULL) {
	    if (!strcmp (return_class, "Integer") ||
		!strcmp (return_class, "Character")) {
	      c_int_to_obj_call (rcvr_obj, method, arg_object);
	    } else {
	      if (!strcmp (return_class, "Float")) {
		c_double_to_obj_call (rcvr_obj, method, arg_object);
	      } else {
		if (!strcmp (return_class, "String")) {
		  c_char_ptr_to_obj_call (rcvr_obj, method, arg_object);
		} else {
		  if (!strcmp (return_class, "LongInteger")) {
		    c_longlong_to_obj_call (rcvr_obj, method, arg_object);
		  } else {
		    warning (m_arg, 
			     "generate_c_to_obj_call: Unimplemented class %s.",
			     return_class);
		    DELETE_MESSAGES(c_messages,c_message_ptr,N_MESSAGES)
		      return ERROR;
		  }
		}
	      }
	    }
	  }
	  goto ctoobj_done;
	} else {
 	    warning (m_arg, "generate_c_to_obj_call: Undefined function %s.",
 		     m_arg -> name);
	    DELETE_MESSAGES(c_messages,c_message_ptr,N_MESSAGES)
 	    return ERROR;
	}
	break;
      }
  }
 ctoobj_done:
  DELETE_MESSAGES(c_messages,c_message_ptr,N_MESSAGES)
  return SUCCESS;
}

char *fmt_c_to_obj_call (OBJECT *rcvr_obj, METHOD *method,
			    OBJECT *arg_object) {

  int i,
    start_idx,
    end_idx;

  CFUNC *fn;
  MESSAGE *m_arg;
  char *return_class;
  static char buf[MAXMSG];

  start_idx = get_stack_top (c_messages);
  end_idx = tokenize (c_message_push, arg_object -> __o_name);

  for (i = start_idx, fn = NULL; i > end_idx; i--) {

    m_arg = c_messages[i];

    if ((m_arg -> tokentype == WHITESPACE) ||
	(m_arg -> tokentype == NEWLINE))
      continue;

    switch (m_arg -> tokentype)
      {
      case LABEL:
	/*
	 *  Use the same IS_FN() macro as in eval_arg (), 
	 *  but, because there are a few different places
	 *  where function definitions reside, then write
	 *  a function to return a return value, and also
	 *  a function that can create an expression for
	 *  an undefined function on the fly (and also
	 *  in eval_arg ()).
	 *
	 *  Then, add the macro cache to 
	 *  preprocess_template () in fn_tmpl.c.
	 */
	if (IS_DEFINED_FN (M_NAME (m_arg))) {
	  if ((return_class = fn_return_class (M_NAME (m_arg))) != NULL) {
	    if (!strcmp (return_class, "Integer") ||
		!strcmp (return_class, "Character")) {
	      sprintf (buf, "__ctalkCIntToObj (%s)", arg_object -> __o_name);
	    } else {
	      if (!strcmp (return_class, "Float")) {
		sprintf (buf, "__ctalkCDoubleToObj (%s)", 
			 arg_object -> __o_name);
	      } else {
		if (!strcmp (return_class, "String")) {
		  sprintf (buf, "__ctalkCCharPtrToObj (%s)", 
			   arg_object -> __o_name);
		} else {
		  if (!strcmp (return_class, "LongInteger")) {
		    sprintf (buf, "__ctalkCLongLongToObj (%s)",
			     arg_object->__o_name);
		  } else {
		    warning (m_arg, 
			     "generate_c_to_obj_call: Unimplemented class %s.",
			     return_class);
		    DELETE_MESSAGES(c_messages,c_message_ptr,N_MESSAGES)
		      return NULL;
		  }
		}
	      }
	    }
	  }
	  goto ctoobj_done;
	} else {
 	    warning (m_arg, "generate_c_to_obj_call: Undefined function %s.",
 		     m_arg -> name);
	    DELETE_MESSAGES(c_messages,c_message_ptr,N_MESSAGES)
 	    return NULL;
	}
	break;
      }
  }

 ctoobj_done:
  DELETE_MESSAGES(c_messages,c_message_ptr,N_MESSAGES)
  return buf;
}


int c_int_to_obj_call (OBJECT *rcvr_object, METHOD *method, 
		       OBJECT *arg_object) {
  static char buf[MAXMSG];

  sprintf (buf, "__ctalk_arg (\"%s\", \"%s\", __ctalkCIntToObj (%s));\n",
	   rcvr_object->__o_name, method->name, arg_object -> __o_name);

   if (is_global_frame ())
     fn_init (buf, FALSE);
   else
     fileout (buf, 0, 0);

  return SUCCESS;
}

int c_longlong_to_obj_call (OBJECT *rcvr_object, METHOD *method, 
			    OBJECT *arg_object) {
  static char buf[MAXMSG];

  sprintf (buf, "__ctalk_arg (\"%s\", \"%s\", __ctalkCLongLongToObj (%s));\n",
	   rcvr_object->__o_name, method->name, arg_object -> __o_name);

   if (is_global_frame ())
     fn_init (buf, FALSE);
   else
     fileout (buf, 0, 0);

  return SUCCESS;
}

int c_double_to_obj_call (OBJECT *rcvr_object, METHOD *method, 
		       OBJECT *arg_object) {
  static char buf[MAXMSG];

  sprintf (buf, "__ctalk_arg (\"%s\", \"%s\", __ctalkCDoubleToObj (%s));\n",
	   rcvr_object->__o_name, method->name, arg_object -> __o_name);

   if (is_global_frame ())
     fn_init (buf, FALSE);
   else
     fileout (buf, 0, 0);

  return SUCCESS;
}

int c_char_ptr_to_obj_call (OBJECT *rcvr_object, METHOD *method, 
		       OBJECT *arg_object) {
  static char buf[MAXMSG];

  sprintf (buf, "__ctalk_arg (\"%s\", \"%s\", __ctalkCCharPtrToObj (%s));\n",
	   rcvr_object->__o_name, method->name, arg_object -> __o_name);

   if (is_global_frame ())
     fn_init (buf, FALSE);
   else
     fileout (buf, 0, 0);

  return SUCCESS;
}

char *fn_return_class (char *fn_name) {
  CFUNC *fn_cfunc;
  char *fn_libname;
  static char default_type[MAXLABEL], buf[MAXLABEL];

  if ((fn_cfunc = get_function (fn_name)) != NULL) {
    strcpy (buf, cfunc_return_class (fn_cfunc));
    return buf;
  }

  if ((fn_libname = c99_name (fn_name, FALSE)) == NULL) {
    _warning ("fn_return_class (): Libc function %s has no prototype, return class defaulting to %s.\n", fn_libname, DEFAULT_LIBC_RETURNCLASS);
    strcpy (default_type, DEFAULT_LIBC_RETURNCLASS);
    return default_type;
  }
  if ((fn_libname = ctalk_lib_fn_name (fn_name, FALSE)) == NULL) {
    _warning ("fn_return_class (): Libctalk function %s has no prototype, return class defaulting to %s.\n", fn_libname, DEFAULT_LIBC_RETURNCLASS);
    strcpy (default_type, DEFAULT_LIBC_RETURNCLASS);
    return default_type;
  }

  return NULL;
}

char *cfunc_return_class (CFUNC *fn_cfunc) {

  static char buf[MAXLABEL];

  if (!strcmp (fn_cfunc -> return_type, "char")) {
    switch (fn_cfunc -> return_derefs)
      {
      case 2:
	strcpy (buf, "Array");
	break;
      case 1:
	strcpy (buf, "String");
	break;
      case 0:
	strcpy (buf, "Character");
	break;
      default:
	break;
      }
  } else {
    if (!strcmp (fn_cfunc -> return_type, "int") ||
 	!strcmp (fn_cfunc -> return_type, "short") ||
 	!strcmp (fn_cfunc -> return_type, "long")) {
      if ((!strcmp (fn_cfunc -> return_type, "long") && 
 	   !strcmp (fn_cfunc -> qualifier_type, "long")) ||
 	  (!strcmp (fn_cfunc -> qualifier_type, "long") &&
 	   !strcmp (fn_cfunc -> qualifier2_type, "long")))
 	strcpy (buf, "LongInteger");
      else
 	strcpy (buf, "Integer");
    } else {
      if (!strcmp (fn_cfunc -> return_type, "float") ||
 	  !strcmp (fn_cfunc -> return_type, "double")) {
 	strcpy (buf, "Float");
      } else {
	CVAR *__typedef_cvar;
	if ((__typedef_cvar = (CVAR *)_hash_get
	     (declared_typedefs, (void *)fn_cfunc -> return_type)) != NULL) {
	  strcpy (buf, basic_class_from_cvar (__typedef_cvar, 0));
	}
      }
    }
  }
  return buf;
}

