/* interp.c */

#include "config.h"
#include "object.h"
#include "construct.h"
#include "interp.h"
#include "instr.h"
#include "operproto.h"
#include "globals.h"
#include "cache.h"

int (*oper_array[NUM_OPERS+NUM_SCALLS])(struct object *caller, struct object
                                        *obj, struct object *player, struct
                                        var_stack **rts)={
  comma_oper,eq_oper,pleq_oper,mieq_oper,mueq_oper,dieq_oper,moeq_oper,
  aneq_oper,exeq_oper,oreq_oper,lseq_oper,rseq_oper,cond_oper,or_oper,
  and_oper,bitor_oper,exor_oper,bitand_oper,condeq_oper,noteq_oper,less_oper,
  lesseq_oper,great_oper,greateq_oper,ls_oper,rs_oper,add_oper,min_oper,
  mul_oper,div_oper,mod_oper,not_oper,bitnot_oper,postadd_oper,preadd_oper,
  postmin_oper,premin_oper,umin_oper,s_add_verb,s_add_xverb,s_call_other,
  s_alarm,s_remove_alarm,s_caller_object,s_clone_object,s_destruct,
  s_contents,s_next_object,s_location,s_next_child,s_parent,s_next_proto,
  s_move_object,s_this_object,s_this_player,s_set_interactive,
  s_interactive,s_set_priv,s_priv,s_in_editor,s_connected,s_get_devconn,
  s_send_device,s_reconnect_device,s_disconnect_device,s_random,s_time,
  s_mktime,s_typeof,s_command,s_compile_object,s_edit,s_cat,s_ls,s_rm,s_cp,
  s_mv,s_mkdir,s_rmdir,s_hide,s_unhide,s_chown,s_syslog,s_sscanf,s_sprintf,
  s_midstr,s_strlen,s_leftstr,s_rightstr,s_subst,s_instr,s_otoa,s_itoa,
  s_atoi,s_atoo,s_upcase,s_downcase,s_is_legal,s_otoi,s_itoo,s_chmod,
  s_fread,s_fwrite,s_remove_verb,s_ferase,s_chr,s_asc,s_sysctl,
  s_prototype,s_iterate,s_next_who };

void interp_error(char *msg, struct object *player, struct object *obj,
                  struct fns *func, unsigned long line) {
  char *buf;

  buf=MALLOC(strlen(obj->parent->pathname)+strlen(msg)+(2*ITOA_BUFSIZ)+20);
  sprintf(buf," interp: %s#%ld line #%ld: %s",obj->parent->pathname,
          (long) obj->refno,(long) line,msg);
  log_sysmsg(buf);
  if (player) {
    send_device(player,buf+1);
    send_device(player,"\n");
  }
  FREE(buf);
}

void clear_locals() {
  int loop;

  if (!num_locals) return;
  loop=0;
  while (loop<num_locals) {
    clear_var(&(locals[loop]));
    loop++;
  }
  FREE(locals);
}

struct fns *find_fns(char *name, struct object *obj) {
  struct fns *next;

  next=obj->parent->funcs->func_list;
  while (next) {
    if (!strcmp(next->funcname,name))
      return next;
    next=next->next;
  }
  return NULL;
}

int interp(struct object *caller, struct object *obj, struct object *player,
           struct var_stack **arg_stack, struct fns *func) {
  struct var_stack *rts,*stack1;
  struct var tmp,tmp2;
  struct fns *temp_fns;
  unsigned long arg_count,loop,line;
  unsigned int old_num_locals,error_occurred;
  struct var *old_locals;
  int retstatus;

  load_data(obj);
  rts=NULL;
  stack1=NULL;
  line=0;
  num_locals=func->num_locals;
  loop=0;
  if (num_locals)
    locals=(struct var *) MALLOC(sizeof(struct var)*num_locals);
  else
    locals=NULL;
  while (loop<num_locals) {
    locals[loop].type=INTEGER;
    locals[loop].value.integer=0;
    loop++;
  }
  if (pop(&tmp,arg_stack,caller)) {
    interp_error("malformed argument stack",player,obj,func,0);
    free_stack(arg_stack);
    return 1;
  }
  if (tmp.type!=NUM_ARGS) {
    interp_error("malformed argument stack",player,obj,func,0);
    free_stack(arg_stack);
    return 1;
  }
  if (tmp.value.num>num_locals) {
    interp_error("too many arguments",player,obj,func,0);
    return 1;
  }
  loop=tmp.value.num;
  while (loop>0) {
    if (pop(&tmp,arg_stack,caller)) {
      interp_error("malformed argument stack",player,obj,func,0);
      free_stack(arg_stack);
      clear_locals();
      return 1;
    }
    --loop;
    copy_var(&(locals[loop]),&tmp);
    clear_var(&tmp);
  }
  loop=0;
  while (1) {

#ifdef CYCLE_HARD_MAX
    if (hard_cycles++>CYCLE_HARD_MAX) {
      tmp.type=INTEGER;
      tmp.value.integer=0;
      interp_error("cycle hard maximum exceeded",player,obj,func,line);
      pushnocopy(&tmp,arg_stack);
      clear_locals();
      return 0;
    }
#endif /* CYCLE_HARD_MAX */

#ifdef CYCLE_SOFT_MAX
    if (soft_cycles++>CYCLE_SOFT_MAX) {
      tmp.type=INTEGER;
      tmp.value.integer=0;
      interp_error("cycle soft maximum exceeded",player,obj,func,line);
      pushnocopy(&tmp,arg_stack);
      clear_locals();
      return 0;
    }
#endif /* CYCLE_SOFT_MAX */

    switch (func->code[loop].type) {
      case INTEGER:
      case STRING:
      case OBJECT:
      case GLOBAL_L_VALUE:
      case LOCAL_L_VALUE:
      case NUM_ARGS:
      case ARRAY_SIZE:
        push(&(func->code[loop]),&rts);
        loop++;
        break;
      case LOCAL_REF:
      case GLOBAL_REF:
        if (popint(&tmp,&rts,obj)) {
          interp_error("failed array reference",player,obj,func,line);
          free_stack(&rts);
          clear_locals();
          return 1;
	}
        tmp2.value.l_value.size=tmp.value.integer;
        if (popint(&tmp,&rts,obj)) {
          interp_error("failed array reference",player,obj,func,line);
          free_stack(&rts);
          clear_locals();
          return 1;
	}
        tmp2.value.l_value.ref=tmp.value.integer;
        if (func->code[loop].type==LOCAL_REF) {
          if (tmp2.value.l_value.ref>=num_locals) {
            interp_error("array reference out of bound",player,obj,func,line);
            free_stack(&rts);
            clear_locals();
            return 1;
	  }
	} else
          if (tmp2.value.l_value.ref>=obj->parent->funcs->num_globals) {
            interp_error("array reference out of bound",player,obj,func,line);
            free_stack(&rts);
            clear_locals();
            return 1;
	  }
        if (tmp2.value.l_value.size!=1) {
          interp_error("illegal array reference",player,obj,func,line);
          free_stack(&rts);
          clear_locals();
          return 1;
	}
        if (func->code[loop].type==LOCAL_REF)
          tmp2.type=LOCAL_L_VALUE;
        else
          tmp2.type=GLOBAL_L_VALUE;
        push(&tmp2,&rts);
        loop++;
        break;
      case ASM_INSTR:
        if (func->code[loop].value.instruction<NUM_OPERS) {
          retstatus=((*oper_array[func->code[loop].value.instruction])
                     (caller,obj,player,&rts));
          if (retstatus) {
            interp_error("arithmetic operation failed",player,obj,func,line);
            free_stack(&rts);
            clear_locals();
            return 1;
          }
        } else {
          old_locals=locals;
          old_num_locals=num_locals;
          if (func->code[loop].value.instruction==S_SSCANF ||
              func->code[loop].value.instruction==S_SPRINTF ||
              func->code[loop].value.instruction==S_FREAD)
            stack1=gen_stack_noresolve(&rts,obj);
          else
            stack1=gen_stack(&rts,obj);
          retstatus=((*oper_array[func->code[loop].value.instruction])
                     (caller,obj,player,&stack1));
          locals=old_locals;
          num_locals=old_num_locals;
          if (retstatus) {
            interp_error("system call failed",player,obj,func,line);
            free_stack(&rts);
            free_stack(&stack1);
            clear_locals();
            return 1;
          }
          if (pop(&tmp,&stack1,obj)) {
            interp_error("system call returned malformed stack",player,obj,
                         func,line);
            free_stack(&rts);
            free_stack(&stack1);
            clear_locals();
            return 1;
          }
          pushnocopy(&tmp,&rts);
          free_stack(&stack1);
        }
        loop++;
        break;
      case FUNC_CALL:
        stack1=gen_stack(&rts,obj);
        old_locals=locals;
        old_num_locals=num_locals;
        if (interp(obj,obj,player,&stack1,func->code[loop].value.func_call)) {
          locals=old_locals;
          num_locals=old_num_locals;
          free_stack(&stack1);
          free_stack(&rts);
          clear_locals();
          return 1;
        }
        locals=old_locals;
        num_locals=old_num_locals;
        if (pop(&tmp,&stack1,obj)) {
          interp_error("function returned malformed stack",player,obj,func,
                       line);
          free_stack(&rts);
          free_stack(&stack1);
          clear_locals();
          return 1;
        }
        pushnocopy(&tmp,&rts);
        free_stack(&stack1);
        loop++;
        break;
      case FUNC_NAME:
        temp_fns=find_fns(func->code[loop].value.string,obj);
        if (!temp_fns) {
          interp_error("unknown function",player,obj,func,line);
          free_stack(&rts);
          clear_locals();
          return 1;
        }
        clear_var(&(func->code[loop]));
        func->code[loop].type=FUNC_CALL;
        func->code[loop].value.func_call=temp_fns;
        break;
      case JUMP:
        loop=func->code[loop].value.num;
        break;
      case BRANCH:
        if (pop(&tmp,&rts,obj)) {
          interp_error("failed branch instruction",player,obj,func,line);
          free_stack(&rts);
          clear_locals();
          return 1;
        }
        if (resolve_var(&tmp,obj)) {
          interp_error("failed variable resolution",player,obj,func,line);
          free_stack(&rts);
          clear_locals();
          return 1;
        }
        if (tmp.type==INTEGER && tmp.value.integer==0) {
          loop=func->code[loop].value.num;
        } else {
          clear_var(&tmp);
          loop++;
        }
        break;
      case NEW_LINE:
        free_stack(&rts);
        line=func->code[loop].value.num;
        loop++;
        break;
      case RETURN:
        if (pop(&tmp,&rts,obj)) {
          interp_error("stack malformed on return",player,obj,func,line);
          free_stack(&rts);
          clear_locals();
          return 1;
        }
        if (resolve_var(&tmp,obj)) {
          interp_error("stack malformed on return",player,obj,func,line);
          free_stack(&rts);
          clear_locals();
          return 1;
        }
        pushnocopy(&tmp,arg_stack);
        free_stack(&rts);
        clear_locals();
        return 0;
        break;
    }
  }
}