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

int s_add_verb(struct object *caller, struct object *obj, struct object
               *player, struct var_stack **rts) {
  struct var tmp,tmp2;
  struct verb *new_verb;
  struct fns *tmp_fns;

  if (pop(&tmp,rts,obj)) return 1;
  if (tmp.type!=NUM_ARGS) {
    clear_var(&tmp);
    return 1;
  }
  if (tmp.value.num!=2) {
    return 1;
  }
  if (pop(&tmp,rts,obj)) {
    return 1;
  }
  if (tmp.type!=STRING) {
    clear_var(&tmp);
    return 1;
  }
  if (pop(&tmp2,rts,obj)) {
    clear_var(&tmp);
    return 1;
  }
  if (tmp2.type==INTEGER && tmp2.value.integer==0) {
    tmp2.type=STRING;
    tmp2.value.string=copy_string("");
  }
  if (tmp2.type!=STRING) {
    clear_var(&tmp);
    clear_var(&tmp2);
    return 1;
  }
  if ((strlen(tmp2.value.string)+1)>MAX_STR_LEN) {
    clear_var(&tmp);
    clear_var(&tmp2);
    tmp.type=INTEGER;
    tmp.value.integer=1;
    push(&tmp,rts);
    return 0;
  }
  tmp_fns=find_fns(tmp.value.string,obj);
  if (!tmp_fns) {
    clear_var(&tmp);
    clear_var(&tmp2);
    tmp.type=INTEGER;
    tmp.value.num=1;
    push(&tmp,rts);
    return 0;
  }
  remove_verb(obj,tmp2.value.string);
  new_verb=(struct verb *) MALLOC(sizeof(struct verb));
  new_verb->verb_name=tmp2.value.string;
  new_verb->is_xverb=0;
  new_verb->function=tmp.value.string;
  new_verb->next=obj->verb_list;
  obj->verb_list=new_verb;
  tmp.type=INTEGER;
  tmp.value.num=0;
  push(&tmp,rts);
  return 0;
}

int s_add_xverb(struct object *caller, struct object *obj, struct object
               *player, struct var_stack **rts) {
  struct var tmp,tmp2;
  struct verb *new_verb;
  struct fns *tmp_fns;

  if (pop(&tmp,rts,obj)) return 1;
  if (tmp.type!=NUM_ARGS) {
    clear_var(&tmp);
    return 1;
  }
  if (tmp.value.num!=2) return 1;
  if (pop(&tmp,rts,obj)) return 1;
  if (tmp.type!=STRING) {
    clear_var(&tmp);
    return 1;
  }
  if (pop(&tmp2,rts,obj)) {
    clear_var(&tmp);
    return 1;
  }
  if (tmp2.type==INTEGER && tmp2.value.integer==0) {
    tmp2.type=STRING;
    tmp2.value.string=copy_string("");
  }
  if (tmp2.type!=STRING) {
    clear_var(&tmp);
    clear_var(&tmp2);
    return 1;
  }
  if ((strlen(tmp2.value.string)+1)>MAX_STR_LEN) {
    clear_var(&tmp);
    clear_var(&tmp2);
    tmp.type=INTEGER;
    tmp.value.integer=1;
    push(&tmp,rts);
    return 0;
   }
  tmp_fns=find_fns(tmp.value.string,obj);
  if (!tmp_fns) {
    clear_var(&tmp);
    clear_var(&tmp2);
    tmp.type=INTEGER;
    tmp.value.num=1;
    push(&tmp,rts);
    return 0;
  }
  remove_verb(obj,tmp2.value.string);
  new_verb=(struct verb *) MALLOC(sizeof(struct verb));
  new_verb->verb_name=tmp2.value.string;
  new_verb->is_xverb=1;
  new_verb->function=tmp.value.string;
  new_verb->next=obj->verb_list;
  obj->verb_list=new_verb;
  tmp.type=INTEGER;
  tmp.value.num=0;
  push(&tmp,rts);
  return 0;
}

int s_call_other(struct object *caller, struct object *obj, struct object
                 *player, struct var_stack **rts) {
  struct var tmp1,tmp2,tmp3;
  struct var_stack *arg_stack;
  struct fns *tmp_fns;
  struct var *old_locals;
  unsigned int old_num_locals;

  if (pop(&tmp1,rts,obj)) return 1;
  if (tmp1.type!=NUM_ARGS) {
    clear_var(&tmp1);
    return 1;
  }
  if (tmp1.value.num<2) return 1;
  tmp1.value.num-=2;
  push(&tmp1,rts);
  if (!(arg_stack=gen_stack(rts,obj))) return 1;
  if (pop(&tmp2,rts,obj)) {
    free_stack(&arg_stack);
    return 1;
  }
  if (pop(&tmp1,rts,obj)) {
    clear_var(&tmp2);
    free_stack(&arg_stack);
    return 1;
  }
  if (tmp1.type!=OBJECT || tmp2.type!=STRING) {
    clear_var(&tmp1);
    clear_var(&tmp2);
    free_stack(&arg_stack);
    return 1;
  }
  tmp_fns=find_fns(tmp2.value.string,tmp1.value.objptr);
  clear_var(&tmp2);
  if (!tmp_fns) {
    free_stack(&arg_stack);
    tmp1.type=INTEGER;
    tmp1.value.integer=0;
    push(&tmp1,rts);
    return 0;
  }
  if (tmp_fns->is_static) {
    free_stack(&arg_stack);
    tmp1.type=INTEGER;
    tmp1.value.integer=0;
    push(&tmp1,rts);
    return 0;
  }
  old_locals=locals;
  old_num_locals=num_locals;
  if (interp(obj,tmp1.value.objptr,player,&arg_stack,tmp_fns)) {
    locals=old_locals;
    num_locals=old_num_locals;
    free_stack(&arg_stack);
    tmp1.type=INTEGER;
    tmp1.value.integer=0;
    push(&tmp1,rts);
    return 0;
  }
  locals=old_locals;
  num_locals=old_num_locals;
  if (pop(&tmp1,&arg_stack,obj)) {
    tmp1.type=INTEGER;
    tmp1.value.integer=0;
  }
  free_stack(&arg_stack);
  push(&tmp1,rts);
  return 0;
}

int s_alarm(struct object *caller, struct object *obj, struct object
            *player, struct var_stack **rts) {
  struct var tmp1,tmp2;

  if (pop(&tmp1,rts,obj)) return 1;
  if (tmp1.type!=NUM_ARGS) {
    clear_var(&tmp1);
    return 1;
  }
  if (tmp1.value.num!=2) return 1;
  if (pop(&tmp2,rts,obj)) return 1;
  if (pop(&tmp1,rts,obj)) {
    clear_var(&tmp2);
    return 1;
  }
  if (tmp1.type!=INTEGER || tmp2.type!=STRING) {
    clear_var(&tmp1);
    clear_var(&tmp2);
    return 1;
  }
  if (!find_fns(tmp2.value.string,obj) || tmp1.value.integer<0) {
    clear_var(&tmp2);
    tmp1.value.integer=1;
    push(&tmp1,rts);
    return 0;
  }
  queue_for_alarm(obj,tmp1.value.integer,tmp2.value.string);
  clear_var(&tmp2);
  tmp1.value.integer=0;
  push(&tmp1,rts);
  return 0;
}

int s_remove_alarm(struct object *caller, struct object *obj, struct object
                   *player, struct var_stack **rts) {
  struct var tmp;
  signed long tmpint;

  if (pop(&tmp,rts,obj)) return 1;
  if (tmp.type!=NUM_ARGS) {
    clear_var(&tmp);
    return 1;
  }
  if (tmp.value.num==0) {
    tmp.type=INTEGER;
    tmp.value.integer=remove_alarm(obj,NULL);
    push(&tmp,rts);
    return 0;
  } else
    if (tmp.value.num==1) {
      if (pop(&tmp,rts,obj)) return 1;
      if (tmp.type!=STRING) {
        clear_var(&tmp);
        return 1;
      }
      tmpint=remove_alarm(obj,tmp.value.string);
      clear_var(&tmp);
      tmp.type=INTEGER;
      tmp.value.integer=tmpint;
      push(&tmp,rts);
      return 0;
    }
  return 1;
}

int s_caller_object(struct object *caller, struct object *obj, struct object
                    *player, struct var_stack **rts) {
  struct var tmp;

  if (pop(&tmp,rts,obj)) return 1;
  if (tmp.type!=NUM_ARGS) {
    clear_var(&tmp);
    return 1;
  }
  if (tmp.value.num!=0) return 1;
  tmp.type=OBJECT;
  tmp.value.objptr=caller;
  if (!caller) {
    tmp.type=INTEGER;
    tmp.value.integer=0;
  }
  push(&tmp,rts);
  return 0;
}

int s_clone_object(struct object *caller, struct object *obj, struct object
                   *player, struct var_stack **rts) {
  struct var tmp,tmp2,tmp3;
  struct object *tmpobj;
  struct code *newcode;
  unsigned int line;
  struct var_stack *arg_stack;
  struct fns *tmpfns;
  struct var *old_locals;
  struct proto *tmp_proto;
  unsigned int old_num_locals;

  if (pop(&tmp,rts,obj)) return 1;
  if (tmp.type!=NUM_ARGS) {
    clear_var(&tmp);
    return 1;
  }
  if (tmp.value.num!=1) return 1;
  if (pop(&tmp,rts,obj)) return 1;
  if (tmp.type!=STRING && tmp.type!=OBJECT) {
    clear_var(&tmp);
    return 1;
  }
  if (tmp.type==STRING) {
    if (!(tmpobj=find_proto(tmp.value.string))) {
      line=parse_code(tmp.value.string,obj,&newcode);
      if (line==((unsigned int) -1)) {
        clear_var(&tmp);
        tmp.type=INTEGER;
        tmp.value.integer=0;
        push(&tmp,rts);
        return 0;
      }
      if (line) {
        compile_error(player,tmp.value.string,line);
        clear_var(&tmp);
        tmp.type=INTEGER;
        tmp.value.integer=0;
        push(&tmp,rts);
        return 0;
      }
      tmpobj=newobj();
      tmp_proto=(struct proto *) MALLOC(sizeof(struct proto));
      tmpobj->flags|=PROTOTYPE;
      tmpobj->parent=tmp_proto;
      tmpobj->parent->funcs=newcode;
      tmpobj->parent->next_proto=ref_to_obj(0)->parent->next_proto;
      ref_to_obj(0)->parent->next_proto=tmpobj->parent;
      tmpobj->parent->pathname=tmp.value.string;
      tmpobj->parent->proto_obj=tmpobj;
      if (newcode->num_globals) {
        tmpobj->globals=(struct var *) MALLOC(sizeof(struct var)*newcode->
                                              num_globals);
        line=0;
        while (line<newcode->num_globals) {
          tmpobj->globals[line].type=INTEGER;
          tmpobj->globals[line].value.integer=0;
          line++;
        }
      }
      add_loaded(tmpobj);
      tmpobj->obj_state=DIRTY;
      tmpfns=find_fns("init",tmpobj);
      if (tmpfns) {
        tmp2.type=NUM_ARGS;
        tmp2.value.num=0;
        arg_stack=NULL;
        push(&tmp2,&arg_stack);
        old_locals=locals;
        old_num_locals=num_locals;
        interp(obj,tmpobj,player,&arg_stack,tmpfns);
        locals=old_locals;
        num_locals=old_num_locals;
        free_stack(&arg_stack);
      }
    } else
      clear_var(&tmp);
    tmp.type=OBJECT;
    tmp.value.objptr=tmpobj;
  }
  tmpobj=newobj();
  tmpobj->parent=tmp.value.objptr->parent;
  tmpobj->next_child=tmpobj->parent->proto_obj->next_child;
  tmpobj->parent->proto_obj->next_child=tmpobj;
  if (tmpobj->parent->funcs->num_globals) {
    tmpobj->globals=(struct var *) MALLOC(sizeof(struct var)*tmpobj->parent->
                                          funcs->num_globals);
    line=0;
    while (line<tmpobj->parent->funcs->num_globals) {
     tmpobj->globals[line].type=INTEGER;
     tmpobj->globals[line].value.integer=0;
     line++;
    }
  }
  add_loaded(tmpobj);
  tmpobj->obj_state=DIRTY;
  tmpfns=find_fns("init",tmpobj);
  if (tmpfns) {
    arg_stack=NULL;
    old_locals=locals;
    old_num_locals=num_locals;
    tmp3.type=NUM_ARGS;
    tmp3.value.num=0;
    push(&tmp3,&arg_stack);
    interp(obj,tmpobj,player,&arg_stack,tmpfns);
    locals=old_locals;
    num_locals=old_num_locals;
    free_stack(&arg_stack);
  }
  tmp.value.objptr=tmpobj;
  push(&tmp,rts);
  return 0;
}

int s_destruct(struct object *caller, struct object *obj, struct object
               *player, struct var_stack **rts) {
  struct var tmp;
  struct object *curr,*prev;

  if (pop(&tmp,rts,obj)) return 1;
  if (tmp.type!=NUM_ARGS) {
    clear_var(&tmp);
    return 1;
  }
  if (tmp.value.num!=1) return 1;
  if (pop(&tmp,rts,obj)) return 1;
  if (tmp.type!=OBJECT) {
    clear_var(&tmp);
    return 1;
  }
  if (tmp.value.objptr!=obj && (!(obj->flags & PRIV))) {
    tmp.type=INTEGER;
    tmp.value.integer=1;
    push(&tmp,rts);
    return 0;
  }
  if (tmp.value.objptr==ref_to_obj(0)) {
    tmp.type=INTEGER;
    tmp.value.integer=1;
    push(&tmp,rts);
    return 0;
  }
  queue_for_destruct(tmp.value.objptr);
  tmp.type=INTEGER;
  tmp.value.integer=0;
  push(&tmp,rts);
  return 0;
}

int s_contents(struct object *caller, struct object *obj, struct object
               *player, struct var_stack **rts) {
  struct var tmp;

  if (pop(&tmp,rts,obj)) return 1;
  if (tmp.type!=NUM_ARGS) {
    clear_var(&tmp);
    return 1;
  }
  if (tmp.value.num!=1) return 1;
  if (pop(&tmp,rts,obj)) return 1;
  if (tmp.type!=OBJECT) {
    clear_var(&tmp);
    return 1;
  }
  if (tmp.value.objptr->contents) {
    tmp.value.objptr=tmp.value.objptr->contents;
  } else {
    tmp.type=INTEGER;
    tmp.value.integer=0;
  }
  push(&tmp,rts);
  return 0;
}

int s_next_object(struct object *caller, struct object *obj, struct object
               *player, struct var_stack **rts) {
  struct var tmp;

  if (pop(&tmp,rts,obj)) return 1;
  if (tmp.type!=NUM_ARGS) {
    clear_var(&tmp);
    return 1;
  }
  if (tmp.value.num!=1) return 1;
  if (pop(&tmp,rts,obj)) return 1;
  if (tmp.type!=OBJECT) {
    clear_var(&tmp);
    return 1;
  }
  if (tmp.value.objptr->next_object) {
    tmp.value.objptr=tmp.value.objptr->next_object;
  } else {
    tmp.type=INTEGER;
    tmp.value.integer=0;
  }
  push(&tmp,rts);
  return 0;
}

int s_location(struct object *caller, struct object *obj, struct object
               *player, struct var_stack **rts) {
  struct var tmp;

  if (pop(&tmp,rts,obj)) return 1;
  if (tmp.type!=NUM_ARGS) {
    clear_var(&tmp);
    return 1;
  }
  if (tmp.value.num!=1) return 1;
  if (pop(&tmp,rts,obj)) return 1;
  if (tmp.type!=OBJECT) {
    clear_var(&tmp);
    return 1;
  }
  if (tmp.value.objptr->location) {
    tmp.value.objptr=tmp.value.objptr->location;
  } else {
    tmp.type=INTEGER;
    tmp.value.integer=0;
  }
  push(&tmp,rts);
  return 0;
}

int s_next_child(struct object *caller, struct object *obj, struct object
               *player, struct var_stack **rts) {
  struct var tmp;

  if (pop(&tmp,rts,obj)) return 1;
  if (tmp.type!=NUM_ARGS) {
    clear_var(&tmp);
    return 1;
  }
  if (tmp.value.num!=1) return 1;
  if (pop(&tmp,rts,obj)) return 1;
  if (tmp.type!=OBJECT) {
    clear_var(&tmp);
    return 1;
  }
  if (tmp.value.objptr->next_child) {
    tmp.value.objptr=tmp.value.objptr->next_child;
  } else {
    tmp.type=INTEGER;
    tmp.value.integer=0;
  }
  push(&tmp,rts);
  return 0;
}

int s_parent(struct object *caller, struct object *obj, struct object
               *player, struct var_stack **rts) {
  struct var tmp;

  if (pop(&tmp,rts,obj)) return 1;
  if (tmp.type!=NUM_ARGS) {
    clear_var(&tmp);
    return 1;
  }
  if (tmp.value.num!=1) return 1;
  if (pop(&tmp,rts,obj)) return 1;
  if (tmp.type!=OBJECT) {
    clear_var(&tmp);
    return 1;
  }
  if (tmp.value.objptr->parent) {
    tmp.value.objptr=tmp.value.objptr->parent->proto_obj;
  } else {
    tmp.type=INTEGER;
    tmp.value.integer=0;
  }
  push(&tmp,rts);
  return 0;
}

int s_next_proto(struct object *caller, struct object *obj, struct object
               *player, struct var_stack **rts) {
  struct var tmp;

  if (pop(&tmp,rts,obj)) return 1;
  if (tmp.type!=NUM_ARGS) {
    clear_var(&tmp);
    return 1;
  }
  if (tmp.value.num!=1) return 1;
  if (pop(&tmp,rts,obj)) return 1;
  if (tmp.type!=OBJECT) {
    clear_var(&tmp);
    return 1;
  }
  if (tmp.value.objptr->parent->next_proto) {
    tmp.value.objptr=tmp.value.objptr->parent->next_proto->proto_obj;
  } else {
    tmp.type=INTEGER;
    tmp.value.integer=0;
  }
  push(&tmp,rts);
  return 0;
}

int s_move_object(struct object *caller, struct object *obj, struct object
                  *player, struct var_stack **rts) {
  struct var tmp;
  struct object *item,*dest,*prev,*curr;

  if (pop(&tmp,rts,obj)) return 1;
  if (tmp.type!=NUM_ARGS) {
    clear_var(&tmp);
    return 1;
  }
  if (tmp.value.num!=2) return 1;
  if (pop(&tmp,rts,obj)) return 1;
  if (tmp.type!=OBJECT && (tmp.type!=INTEGER || tmp.value.integer!=0)) {
    clear_var(&tmp);
    return 1;
  }
  if (tmp.type==OBJECT)
    dest=tmp.value.objptr;
  else
    dest=NULL;
  if (pop(&tmp,rts,obj)) return 1;
  if (tmp.type!=OBJECT && (tmp.type!=INTEGER || tmp.value.integer!=0)) {
    clear_var(&tmp);
    return 1;
  }
  if (tmp.type==OBJECT)
    item=tmp.value.objptr;
  else {
    tmp.type=INTEGER;
    tmp.value.integer=1;
    push(&tmp,rts);
    return 0;
  }
  if (dest) {
    curr=dest;
    while (curr) {
      if (curr==item) {
        tmp.type=INTEGER;
        tmp.value.integer=1;
        push(&tmp,rts);
        return 0;
      }
      curr=curr->location;
    }
  }
  if (item->location) {
    curr=item->location->contents;
    if (curr==item)
      item->location->contents=item->next_object;
    else
      while (curr) {
        prev=curr;
        curr=curr->next_object;
        if (curr==item) {
          prev->next_object=curr->next_object;
          break;
        }
      }
  }
  item->next_object=NULL;
  item->location=dest;
  if (dest) {
    item->next_object=dest->contents;
    dest->contents=item;
  }
  tmp.type=INTEGER;
  tmp.value.integer=0;
  push(&tmp,rts);
  return 0;
}

int s_this_object(struct object *caller, struct object *obj, struct object
                    *player, struct var_stack **rts) {
  struct var tmp;

  if (pop(&tmp,rts,obj)) return 1;
  if (tmp.type!=NUM_ARGS) {
    clear_var(&tmp);
    return 1;
  }
  if (tmp.value.num!=0) return 1;
  tmp.type=OBJECT;
  tmp.value.objptr=obj;
  if (!obj) {
    tmp.type=INTEGER;
    tmp.value.integer=0;
  }
  push(&tmp,rts);
  return 0;
}

int s_this_player(struct object *caller, struct object *obj, struct object
                    *player, struct var_stack **rts) {
  struct var tmp;

  if (pop(&tmp,rts,obj)) return 1;
  if (tmp.type!=NUM_ARGS) {
    clear_var(&tmp);
    return 1;
  }
  if (tmp.value.num!=0) return 1;
  tmp.type=OBJECT;
  tmp.value.objptr=player;
  if (!player) {
    tmp.type=INTEGER;
    tmp.value.integer=0;
  }
  push(&tmp,rts);
  return 0;
}

int s_remove_verb(struct object *caller, struct object *obj, struct object
                  *player, struct var_stack **rts) {
  struct var tmp;

  if (pop(&tmp,rts,obj)) return 1;
  if (tmp.type!=NUM_ARGS) {
    clear_var(&tmp);
    return 1;
  }
  if (tmp.value.num!=1) return 1;
  if (pop(&tmp,rts,obj)) return 1;
  if (tmp.type==INTEGER && tmp.value.integer==0) {
    tmp.type=STRING;
    *(tmp.value.string=MALLOC(1))='\0';
  }
  if (tmp.type!=STRING) {
    clear_var(&tmp);
    return 1;
  }
  remove_verb(obj,tmp.value.string);
  clear_var(&tmp);
  tmp.type=INTEGER;
  tmp.value.integer=0;
  push(&tmp,rts);
  return 0;
}