/* $Id: rtinfo.c,v 1.3 2008/11/02 18:47:41 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 <stdlib.h>
#include <time.h>
#include "object.h"
#include "message.h"
#include "ctalk.h"
#include "rtinfo.h"
#include "parser.h"

extern char *tzname[2];
long int timezone;
extern int daylight;

RT_INFO rtinfo;

RT_INFO *__call_stack[MAXARGS+1];
int __call_stack_ptr = MAXARGS;

I_PASS interpreter_pass;

static char argv_name[FILENAME_MAX];

static char classlib_name[FILENAME_MAX];

PARSER *parsers[MAXARGS+1];
int current_parser_ptr;

HASHTAB typedefhash;

void __argvName (char *s) {
  strcpy (argv_name, s);
}

char *__argvFileName (void) {
  return argv_name;
}

void __classLibName (char *s) {
  strcpy (classlib_name, s);
}

char *__classLibFileName (void) {
  return classlib_name;
}

void __setClassLibRead (Boolean b) {
  rtinfo.classlib_read = b;
}

Boolean __getClassLibRead (void) {
  return rtinfo.classlib_read;
}

int __ctalkRtSaveSourceFileName (char *s) {
  strcpy (rtinfo.source_file, s);
  return 0;
}

char *__source_filename (void) {
  return rtinfo.source_file;
}

void __ctalkRtReceiver (OBJECT *o) {
  rtinfo.rcvr_obj = o;
}

OBJECT *__ctalkRtReceiverObject (void) {
  return rtinfo.rcvr_obj;
}

void __ctalk_rtReceiverClass (OBJECT *o) {
  rtinfo.rcvr_class_obj = o;
}

OBJECT *__ctalk_rtReceiverClassObject (void) {
  return rtinfo.rcvr_class_obj;
}

void __ctalkRtMethodClass (OBJECT *o) {
  rtinfo.method_class_obj = o;
}

OBJECT *__ctalkRtMethodClassObject (void) {
  return rtinfo.method_class_obj;
}

void __ctalk_rtMethodFn (OBJECT *(*fn)()) {
  rtinfo.method_fn = fn;
}

void *__ctalkRtGetMethodFn (void) {
  return rtinfo.method_fn;
}

/*
 *  Only saved by __save_rt_info (). 
 */
METHOD *__ctalkRtGetMethod (void) {
  return __call_stack[__call_stack_ptr+1]->method;
}

RT_FN *get_fn (void) { 
  if (__call_stack_ptr >= MAXARGS) return NULL;
  return (__call_stack[__call_stack_ptr+1]->_rt_fn ? 
	  __call_stack[__call_stack_ptr+1]->_rt_fn :
	  NULL); 
}

RT_FN *get_calling_fn (void) { 
  if ((__call_stack_ptr + 1) >= MAXARGS) return NULL;
  return (__call_stack[__call_stack_ptr+2]->_rt_fn ? 
	  __call_stack[__call_stack_ptr+2]->_rt_fn :
	  NULL); 
}

int __ctalk_initFn (char *name) {

  RT_FN *r;
  RT_INFO *r_info;

  if ((r_info = (RT_INFO *)calloc (1, sizeof (RT_INFO))) == NULL)
    _error ("__save_rt_info: allocation error.\n");

  r = new_rt_fn ();

  strcpy (r -> name, name);

  r_info -> _rt_fn = r;
  r_info -> _arg_frame_top = __ctalkGetArgPtr () + 1;
  strcpy (r_info -> source_file, rtinfo.source_file);
  __call_stack[__call_stack_ptr--] = r_info;

  return 0;
}

static void __delete_fn_local_objs (RT_FN *r) {
  VARENTRY *v_ptr, *v_ptr_prev;

  for (v_ptr = r -> local_objects.vars; v_ptr && v_ptr -> next;
       v_ptr = v_ptr -> next)
    ;
  if (v_ptr == r -> local_objects.vars) {
    if (v_ptr && v_ptr -> var_object && 
	IS_OBJECT(v_ptr -> var_object)) {
      __ctalkDeleteObject (v_ptr -> var_object);
      delete_varentry (v_ptr);
    }
    r -> local_objects.vars = NULL;
  } else {
    while (v_ptr != r -> local_objects.vars) {
      v_ptr_prev = v_ptr -> prev;
      if (v_ptr && v_ptr -> var_object && 
	  IS_OBJECT(v_ptr -> var_object)) {
	__ctalkDeleteObject (v_ptr -> var_object);
      }
      delete_varentry (v_ptr);
      v_ptr = v_ptr_prev;
    }
    if (v_ptr && v_ptr -> var_object && 
	IS_OBJECT(v_ptr -> var_object)) {
      __ctalkDeleteObject (v_ptr -> var_object);
    }
    delete_varentry (v_ptr);
    r -> local_objects.vars = NULL;
  }
}

int __ctalk_exitFn (int app_exit) {

  RT_FN *r;
  RT_INFO *r_info;

  r_info = __call_stack[++__call_stack_ptr];
  __call_stack[__call_stack_ptr] = NULL;

  r = r_info -> _rt_fn;
  free (r_info);

  if (r) {
    if (r -> local_objects.vars != NULL) {
      __delete_fn_local_objs (r);
    }
    free (r);
  }

  if (app_exit)
    __ctalkClassDictionaryCleanup ();

  return 0;
}

RT_FN *new_rt_fn (void) {

  RT_FN *r;

  if ((r = (RT_FN *)__xalloc (sizeof (RT_FN))) == NULL)
    _error ("new_rt_fn: allocation error.\n");

  return r;
}

void __init_time (void) {
  time_t t;
  struct tm *tm_ptr;
  t = time (NULL);
  tm_ptr = localtime (&t);
}

int __save_rt_info (OBJECT *__rcvr_obj, OBJECT *__rcvr_class_obj, 
		    OBJECT *__method_class_obj, METHOD *method,
		    OBJECT *(*fn) (), Boolean inline_call) {
  RT_INFO *r;
  if ((r = (RT_INFO *)calloc (1, sizeof (RT_INFO))) == NULL)
    _error ("__save_rt_info: allocation error.\n");

  r -> inline_call = inline_call;
  r -> rcvr_obj = rtinfo.rcvr_obj = __rcvr_obj;
  r -> rcvr_class_obj = rtinfo.rcvr_class_obj = __rcvr_class_obj;
  r -> method_class_obj = rtinfo.method_class_obj = __method_class_obj;
  r -> method = method;
  r -> method_fn = rtinfo.method_fn = fn;
  r -> _arg_frame_top = __ctalkGetArgPtr () + 1;
  strcpy (r -> source_file, rtinfo.source_file);
  __call_stack[__call_stack_ptr--] = r;

  return SUCCESS;
}

int __restore_rt_info (void) {

  RT_INFO *r;
  r = __call_stack[++__call_stack_ptr];
  __call_stack[__call_stack_ptr] = NULL;

  if (__call_stack_ptr >= MAXARGS) {
    rtinfo.rcvr_obj = rtinfo.rcvr_class_obj = rtinfo.method_class_obj = NULL;
    rtinfo.method_fn = NULL;
  } else {
    rtinfo.rcvr_obj = __call_stack[__call_stack_ptr+1] -> rcvr_obj;
    rtinfo.rcvr_class_obj = __call_stack[__call_stack_ptr+1] -> rcvr_class_obj;
    rtinfo.method_class_obj = 
      __call_stack[__call_stack_ptr+1] -> method_class_obj;
    rtinfo.method_fn = __call_stack[__call_stack_ptr+1] -> method_fn;
    rtinfo.method = __call_stack[__call_stack_ptr+1] -> method;
  }
  free (r);

  return SUCCESS;
}

int __get_call_stack_ptr (void) {
  return __call_stack_ptr;
}

Boolean __ctalkIsInlineCall (void) {
  return rtinfo.inline_call;
}

Boolean __ctalkCallerIsInlineCall (void) {
  return __call_stack[__call_stack_ptr + 1]->inline_call;
}
