/* $Id: complexmethd.c,v 1.2 2009/01/04 15:49:30 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.
*/

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

/*
 *  Can handle statements like the following:
 *
 *  1. <object> <method|instancevar> <method|instancevar>* <args>...
 *  2. <self> <method|instancevar|classvar> <method|instancevar>* <args>...
 *  3. <class> <classvar|method>* <args>...
 *
 *  complex_method_statement and complex_self_method_statement are
 *  called by routines in method.c and rexpr.c.
 */

int complex_method_statement (OBJECT *receiver_object, MESSAGE_STACK messages, 
			      int method_idx) {

  if (complex_var_or_method_message (receiver_object, messages, method_idx))
    return TRUE;
  else
    return FALSE;
}

/*
 *  "Self" normally stays unevaled until run time.  Set the receiver object
 *  temporarily if necessary.
 */
int complex_self_method_statement (OBJECT *receiver_object, MESSAGE_STACK messages, 
			      int method_idx) {
  int r;

  if (messages[method_idx]->receiver_obj == NULL) {
    messages[method_idx]->receiver_obj = receiver_object;
    r = complex_var_or_method_message (receiver_object, messages, method_idx);
    messages[method_idx]->receiver_obj = NULL;
    return ((r) ? TRUE : FALSE);
  } else {
    r = complex_var_or_method_message (receiver_object, messages, method_idx);
    return ((r) ? TRUE : FALSE);
  }

  return FALSE;
}

int complex_var_or_method_message (OBJECT *receiver_object, MESSAGE_STACK messages,
				   int idx) {

  int next_message_idx;
  MESSAGE_CLASS next_message_class;

  if (messages[idx] -> receiver_obj == NULL) {
    /*
     *  For now - Ths should not be the case on the first call
     *  to this function.  However, we should add the ability
     *  to backtrack if the backtracking in message_class 
     *  is not sufficient.
     */
    return FALSE;
  } else {
    if ((next_message_idx = nextlangmsg (messages, idx)) == ERROR)
      return FALSE;
    if (METHOD_ARG_TERM_MSG_TYPE(messages[next_message_idx]))
      return FALSE;
    next_message_class = message_class (messages, next_message_idx);
    if (next_message_class == null_message) return FALSE;
    return TRUE;
  }

  return FALSE;
}

MESSAGE_CLASS message_class (MESSAGE_STACK messages, int idx) {

  int prev_idx;
  OBJECT *var_message_object = NULL,
    *rcvr_value_obj = NULL;
  OBJECT *__instvar, *__classvar;
  MESSAGE_CLASS prev_message_class;

  if (messages[idx] -> receiver_obj) {
    if ((var_message_object = 
	 get_class_variable (M_NAME(messages[idx]),
	     (!strcmp (messages[idx]->receiver_obj->__o_classname, "Class") ?
	      messages[idx]->receiver_obj->__o_name : 
	      messages[idx]->receiver_obj-> __o_classname),
			     FALSE)) != NULL) {
      return class_var_message;
    } else {
      for (__instvar = messages[idx]->receiver_obj->instancevars;
 	   __instvar; __instvar = __instvar -> next) {
 	if (!strcmp (M_NAME(messages[idx]), __instvar->__o_name)) {
 	  return instance_var_message;
	}
      }
      if (messages[idx]->receiver_obj->instancevars) {
	rcvr_value_obj = messages[idx]->receiver_obj->instancevars;
      } else {
	rcvr_value_obj = messages[idx]->receiver_obj;
      }
      if (get_instance_method (messages[idx],
			       rcvr_value_obj, M_NAME(messages[idx]), FALSE))
	return instance_method_message;
      if (get_class_method (messages[idx],
			    rcvr_value_obj, M_NAME(messages[idx]), FALSE))
	return class_method_message;
    }
  } else {
    /*
     *  Backtrack until we find a receiver.
     */
    if ((prev_idx = prevlangmsg (messages, idx)) == ERROR)
      return null_message;

    prev_message_class = message_class (messages, prev_idx);

    switch (prev_message_class)
      {
      case instance_method_message:
	break;
      case class_method_message:
	break;
      case instance_var_message:
	for (__instvar = messages[prev_idx]->receiver_obj->instancevars;
	     __instvar; __instvar = __instvar -> next) {
	  if (!strcmp (M_NAME(messages[prev_idx]), __instvar->__o_name)) {
	    messages[idx]->receiver_obj = __instvar;
	    return message_class (messages, idx);
	  }
	}
	break;
      case class_var_message:
	if ((__classvar = 
	     get_class_variable (M_NAME(messages[prev_idx]),
		 (!strcmp (messages[prev_idx]->receiver_obj->__o_classname, "Class") ?
		  messages[prev_idx]->receiver_obj->__o_name : 
		  messages[prev_idx]->receiver_obj-> __o_classname),
				 FALSE)) != NULL) {
	  messages[idx]->receiver_obj = ((__classvar->instancevars) ?
					 __classvar->instancevars : 
					 __classvar);
	  return message_class (messages, idx);
	} else {
	  warning (messages[prev_idx], "Class variable %s not found.", 
		   M_NAME(messages[prev_idx]));
	  return null_message;
	}
	break;
      case null_message:
	return null_message;
	break;
      }
  }

  return null_message;
}

/*
 *  Must be called after one of the functions, above.
 *  Returns the class of the final receiver in the 
 *  statement.
 */
char *complex_expr_class (MESSAGE_STACK messages, int rcvr_msg_idx) {

  int i, stack_end_idx;
  static char buf[MAXLABEL];

  stack_end_idx = get_stack_top (messages);

  for (i = rcvr_msg_idx-1; i >= stack_end_idx; i--) {

    if (M_ISSPACE(messages[i])) continue;

    if (messages[i] -> receiver_obj) {
      if (!strcmp (messages[i]->receiver_obj->__o_classname, "Class")) {
	strcpy (buf, messages[i]->receiver_obj->__o_name);
      } else {
	if (messages[i]->receiver_obj->instancevars) {
	  strcpy (buf, messages[i]->receiver_obj->instancevars->__o_classname);
	} else {
	  strcpy (buf, messages[i]->receiver_obj->__o_classname);
	}
      }
    } else {
      if (messages[i] -> receiver_obj == NULL)
	return buf;
    }
  }

  return buf;
}
