/* $Id: rt_rcvr.c,v 1.4 2008/10/29 22:47:01 kiesling Exp $ */

/*
  This file is part of ctalk.
  Copyright © 2005-2008  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 library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2 of the License, or (at your option) any later version.

  This library 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
  Lesser 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 <string.h>
#include "object.h"
#include "message.h"
#include "ctalk.h"

extern OBJECT *__ctalk_receivers[MAXARGS+1];
extern int __ctalk_receiver_ptr;

extern OBJECT *__ctalk_classes;

extern OBJECT *__ctalk_dictionary;
extern OBJECT *__ctalk_last_object;

int __ctalk_receiver_push (OBJECT *__o) {
  if (!__o || !IS_OBJECT (__o))
    return ERROR;

  __ctalk_receivers[__ctalk_receiver_ptr--] = __o;
  return __ctalk_receiver_ptr + 1;

}

int __ctalk_receiver_push_ref (OBJECT *__o) {

  if (!__o || !IS_OBJECT (__o))
    return ERROR;

  __refObj (&(__ctalk_receivers[__ctalk_receiver_ptr--]), &__o);
  return __ctalk_receiver_ptr + 1;
}

OBJECT *__ctalk_receiver_pop (void) {

  OBJECT *rcvr;

  if (__ctalk_receiver_ptr == MAXARGS)
    return NULL;

  rcvr = __ctalk_receivers[++__ctalk_receiver_ptr];
  __ctalk_receivers[__ctalk_receiver_ptr] = NULL;
  return rcvr;
}

OBJECT *__ctalk_receiver_pop_deref (void) {
  OBJECT *rcvr;

  if (__ctalk_receiver_ptr == MAXARGS)
    return NULL;

  rcvr = __ctalk_receivers[++__ctalk_receiver_ptr];
  __ctalk_receivers[__ctalk_receiver_ptr] = NULL;
  __objRefCntDec (OBJREF (rcvr));
  return rcvr;
}

void __ctalk_class_initialize (void) {
   __ctalk_receiver_ptr = MAXARGS;
   __setClassLibRead (False);
   __init_time ();
}

OBJECT *__ctalk_receivers_at (int ptr) {
  return __ctalk_receivers[ptr];
}

int __ctalkGetReceiverPtr (void) {
  return __ctalk_receiver_ptr;
}

int is_receiver (OBJECT *__o) {
  int i;
  for (i = __ctalk_receiver_ptr + 1; i <= MAXARGS; i++)
    if (__o == __ctalk_receivers[i])
      return TRUE;
  return FALSE;
}

int is_current_receiver (OBJECT *__o) {
  if (__o == __ctalk_receivers[__ctalk_receiver_ptr + 1])
      return TRUE;
  return FALSE;
}


/*
 *  Called for now only from __ctalk_arg ().  It is sufficient
 *  sufficient for single-token constants, and at this point 
 *  is not much more than a convenience function.  
 * 
 *  However, if we want to use, "self," to refer to this receiver,
 *  __ctalkEvalExpr () must be able to figure out that the constant
 *  receiver's object was immediately pushed by __ctalk_arg ().
 *
 *  TO DO - 1. Test with complex expressions.  
 *          2. Figure out a way for __ctalkEvalExpr () to determine
 *          that, "self," refers to a receiver constant without
 *          having to set another state.
 */

extern Boolean is_constant_rcvr;

OBJECT *__constant_rcvr (char *s) {
  OBJECT *o;
  o = __ctalkEvalExpr (s);
  is_constant_rcvr = True;
  return o;
}

int need_defined_receiver (MESSAGE_STACK messages, int idx,
			   int stack_start, int stack_end,
			   OBJECT *rcvr_class) {
  int next_msg_idx;
  MESSAGE *m;

  m = messages[idx];

  /*
   *  "super" and function names can be special cases for now.
   */
  if (!strcmp (M_NAME(m), "super"))
    return FALSE;

  if (is_function_call (messages, idx, stack_start, stack_end))
    return FALSE;

  /*
   *  Typecast, etc.
   */
  if (is_c_data_type (m -> name) ||
      __get_rt_typedef (m -> name))
    return FALSE;

  if ((next_msg_idx = 
       __ctalkNextLangMsg (messages, idx, stack_end)) != ERROR) {

    if (METHOD_ARG_TERM_MSG_TYPE(messages[next_msg_idx]))
      return FALSE;

    if (!__ctalk_isMethod 
	(M_NAME(messages[next_msg_idx]), 
	 messages, next_msg_idx, 
	 stack_start, stack_end) &&
	!__ctalkIsInstanceVariableOf (rcvr_class, M_NAME(m)) &&
	!__ctalkIsClassVariableOf (rcvr_class, M_NAME(m))) {
      return TRUE;
    }
  }
  return FALSE;
}
