/* Copyright (c) 1993 Stephen F. White */

#include "cool.h"
#include "proto.h"
#include "servers.h"
#include "netio.h"
#include "execute.h"

void op_shutdown (void)
{
  if (!is_wizard (frame.this)) {
    raise (E_PERM);
  } else {
    server_running = 0;
    pushn (0);
  }
}

void op_sync(void)
{
  if (!is_wizard (frame.this)) {
    raise (E_PERM);
  } else {
    cache_sync ();
    pushn (0);
  }
}

void op_writelog (void)
{
  Var arg;

  arg = pop ();
  if (frame.this.id != SYS_OBJ && !is_wizard (frame.this)) {
    raise (E_PERM);
  } else if (arg.type != STR) {
    raise (E_ARGTYPE);
  } else {
    writelog ();
    fprintf (stderr, "%s\n", arg.v.str->str);
    pushn (0);
  }
  var_free (arg);
}

void op_disconnect(void)
{
    Var		arg;

    switch (nargs) {
      case 0:
	disconnect(frame.this.id);
	pushn(0);
	break;
      case 1:
        arg = pop();
        if (arg.type != NUM) {
	    var_free(arg);
	    raise(E_TYPE);
	} else if (!is_wizard(frame.this)) {
	    raise(E_PERM);
	} else {
	    disconnect_fd(arg.v.num);
  pushn (0);
}
        break;
    }
}

void op_raise (void)
{
  Var e;
  List *raise_args;

  e = pop ();
  if (e.type != ERR) {
    var_free (e);
    raise (E_ARGTYPE);
    (void) pop ();
  } else {
    ex_state = RAISED;
    raise_args = make_raise_args (e.v.err);
    raise_args->el[1].v.str = add_traceback_header (raise_args->el[1].v.str,
      e.v.err);
    send_raise (raise_args);
  }
}

void op_pass (void)
{
  Objid parent;
  Var args;

  parent.server = 0;
  parent.id = frame.m->code[frame.pc++];
  args = pop_args(count_args());
  send_message_and_block (frame.this, frame.this,
    string_dup (sym_get (frame.on, frame.m->name)), args.v.list, parent);
}

void op_hasparent (void)
{
  Var parent;

  parent = pop ();

  if (parent.type != OBJ) {
    var_free (parent);
    raise (E_ARGTYPE);
  } else {
    pushn (hasparent (this, parent.v.obj));
  }
}

void op_objsize (void)
{
  pushn (size_object (this));
}

void op_spew_method (void)
{
  Var name, ret;
  Method *m;

  name = pop ();
  if (name.type != STR) {
    var_free (name);
    raise (E_ARGTYPE);
  } else {
    if ((m = find_method (this, name.v.str->str))) {
      ret.type = STR;
      ret.v.str = decode_method (this, m, 2);
      push (ret);
    } else {
      raise (E_METHODNF);
    }
  }
  var_free (name);
}

void op_list_method (void)
{
  Var name, lineno, brackets, indent, ret;
  Method *m;

  lineno.type = brackets.type = indent.type = NUM;
  lineno.v.num = 1;
  brackets.v.num = 0;
  indent.v.num = 2;

  switch (nargs) {
  case 4:
    indent = pop ();
  case 3:
    brackets = pop ();
  case 2:
    lineno = pop ();
  case 1:
    name = pop ();
  }

  if (name.type != STR || indent.type != NUM || brackets.type != NUM
    || lineno.type != NUM) {
    var_free (name);
    var_free (indent);
    var_free (brackets);
    var_free (lineno);
    raise (E_ARGTYPE);
  } else {
    if ((m = find_method (this, name.v.str->str))) {
      ret.type = STR;
      ret.v.str = string_new (0);
      ret.v.str = decompile_method (ret.v.str, this, m, lineno.v.num,
        brackets.v.num, indent.v.num, 0, 0, 0);
      push (ret);
    } else {
      raise (E_METHODNF);
    }
  }
  var_free (name);
}

void op_decompile (void)
{
  Var ret;

  ret.type = STR;
  ret.v.str = decompile_object (this);
  push (ret);
}

void op_find_method (void)
{
  Var arg, ret;
  Object *where;

  arg = pop ();
  if (arg.type != STR) {
    raise (E_ARGTYPE);
  } else {
    (void) find_method_recursive (this, arg.v.str->str, &where);
    ret.type = OBJ;
    if (where) {
      ret.v.obj = where->id;
    } else {
      ret.v.obj.server = 0;
      ret.v.obj.id = NOTHING;
    }
    push (ret);
  }                             /* if */
  var_free (arg);
}
void op_this (void)
{
  Var v;

  v.type = OBJ;
  v.v.obj = frame.this;
  push (v);
}

void op_player (void)
{
  Var v;

  v.type = OBJ;
  v.v.obj = frame.player;
  push (v);
}

void op_caller (void)
{
  Var v;

  v.type = OBJ;
  v.v.obj = frame.caller;
  push (v);
}

void op_args (void)
{
  Var v;

  v.type = LIST;
  v.v.list = list_dup (frame.args);
  push (v);
}

void op_setplayer (void)
{
  Var newplayer;

  newplayer = pop ();
  if (newplayer.type != OBJ) {
    raise (E_ARGTYPE);
  } else {
    frame.player = newplayer.v.obj;
  }
  var_free (newplayer);
}

void op_program(void)
{
    while (nargs--) {
	var_free(pop());
  }
    pushn (0);
}

static Playerid progr;
static const char *mem_code;
static int code_pos, mem_eof;
static int mem_getc (void);
static void mem_ungetc (int c);
static void mem_perror (const char *s);

static int mem_getc (void)
{
  if (mem_code[code_pos]) {
    return mem_code[code_pos++];
  } else {
    mem_eof = 1;
    return EOF;
  }
}

static void mem_ungetc (int c)
{
  if (code_pos > 0 && !mem_eof) {
    code_pos--;
  }
}

static void mem_perror (const char *s)
{
    tell(progr, s, 1 );
}

void op_compile (void)
{
  Var obj, method, pcode;
  Object *o;

  pcode = pop ();
  if (pcode.type != STR) {
    var_free (pcode);
    while (--nargs) {
      method = pop ();
      var_free (method);
    }
    raise (E_ARGTYPE);
    return;
  }
  mem_code = pcode.v.str->str;
  code_pos = 0;
  mem_eof = 0;
  progr = frame.this.id;
  switch (nargs) {
  case 1:                      /* program some objects */
    pushn (compile (progr, mem_getc, mem_ungetc, mem_perror, 0, 0, 0, 0, 0));
    break;
  case 2:                      /* invalid */
    method = pop ();
    var_free (method);
    raise (E_RANGE);
    break;
  case 3:                      /* program a single method */
    method = pop ();
    obj = pop ();
    if (obj.type != OBJ || method.type != STR) {
      raise (E_ARGTYPE);
    } else if (!(o = retrieve (obj.v.obj))) {
      raise (E_OBJNF);
	} else if (!can_program(frame.player, obj.v.obj)) {
      raise (E_PERM);
    } else if (!valid_ident (method.v.str->str)) {
      raise (E_METHODNF);       /* not quite the right error, but hey */
    } else {
      pushn (compile (progr, mem_getc, mem_ungetc, mem_perror, 1, o,
          method.v.str, 0, 0));

    }
    var_free (obj);
    var_free (method);
    this = retrieve (frame.this);       /* in case we recompiled "this" */
    break;
  }
  var_free (pcode);
}

void op_verb (void)
{
  Var verb, method, prep;
  int verbno, methodno, prepno;

  method = pop ();
  prep = pop ();
  verb = pop ();
  if (verb.type != STR || prep.type != STR || method.type != STR) {
    var_free (verb);
    var_free (prep);
    var_free (method);
    raise (E_ARGTYPE);
  } else {
    verbno = sym_add (this, verb.v.str);
    if (prep.v.str->str[0]) {
      prepno = sym_add (this, prep.v.str);
    } else {
      string_free (prep.v.str);
      prepno = -1;
    }
    methodno = sym_add (this, method.v.str);
    verb_add (this, verbno, prepno, methodno);
    cache_put (this, frame.this.id);
    pushn (0);
  }
}

void op_rmverb (void)
{
  Var verb;

  verb = pop ();
  if (verb.type != STR) {
    raise (E_ARGTYPE);
  } else {
  if (verb_rm(this, verb.v.str->str)) {
    pushn(0);
  } else {
    raise(E_VERBNF);
  }
    cache_put (this, frame.this.id);
  }
  var_free (verb);
}

void op_rmmethod (void)
{
  Var method;

  method = pop ();
  if (method.type != STR) {
    raise (E_ARGTYPE);
  } else {
    raise (rm_method (this, method.v.str->str));
    cache_put (this, frame.this.id);
  }
  var_free (method);
}

void op_rmvar (void)
{
  Var varname;

  varname = pop ();
  if (varname.type != STR) {
    raise (E_ARGTYPE);
  } else {
    raise (var_rm_global (this, varname.v.str->str));
    cache_put (this, frame.this.id);
  }
  var_free (varname);
}

void op_listinsert (void)
{
  Var list, value, pos;

  pos.type = NUM;
  pos.v.num = -1;
  if (nargs > 2) {
    pos = pop ();
  }
  value = pop ();
  list = pop ();
  if (list.type != LIST || pos.type != NUM) {
    var_free (pos);
    var_free (list);
    var_free (value);
    raise (E_ARGTYPE);
  } else if (pos.v.num > list.v.list->len) {
    raise (E_RANGE);
  } else {
    list.v.list = list_insert (list.v.list, value, pos.v.num);
    push (list);
  }
}

void op_listappend (void)
{
  Var list, value, pos;

  pos.type = NUM;
  pos.v.num = -1;
  if (nargs > 2) {
    pos = pop ();
  }
  value = pop ();
  list = pop ();
  if (list.type != LIST || pos.type != NUM) {
    var_free (pos);
    var_free (list);
    var_free (value);
    raise (E_ARGTYPE);
  } else if (pos.v.num > list.v.list->len) {
	var_free(pos);  var_free(list);  var_free(value);
    raise (E_RANGE);
  } else {
    list.v.list = list_append (list.v.list, value, pos.v.num);
    push (list);
  }
}

void op_listdelete (void)
{
  Var pos;                      /* position to delete */
  Var list;                     /* list to delete it from */

  pos = pop ();
  list = pop ();
  if (list.type != LIST || pos.type != NUM) {
    var_free (pos);
    var_free (list);
    raise (E_ARGTYPE);
  } else if (pos.v.num < 1 || pos.v.num > list.v.list->len) {
    var_free (list);
    raise (E_RANGE);
  } else {
    list.v.list = list_delete (list.v.list, pos.v.num);
    push (list);
  }
}

void op_listassign (void)
{
  Var pos;                      /* position to assign */
  Var value;                    /* value to assign it */
  Var list;                     /* list to modify */

  pos = pop ();
  value = pop ();
  list = pop ();
  if (list.type != LIST || pos.type != NUM) {
    var_free (pos);
    var_free (list);
    var_free (value);
    raise (E_ARGTYPE);
  } else if (pos.v.num < 1 || pos.v.num > list.v.list->len) {
    var_free (list);
    var_free (value);
    raise (E_RANGE);
  } else {
    list.v.list = list_assign (list.v.list, value, pos.v.num);
    push (list);
  }
}

void op_sort(void)
{
    int		reverseflag = 0;
    Var		list, reverse, ret;

    if (nargs > 1) {
	reverse = pop();
	reverseflag = ISTRUE(reverse);
	var_free(reverse);
    }
    list = pop();
    if (list.type != LIST) {
	var_free(list);
	raise(E_ARGTYPE);
    } else {
	ret.type = LIST;
	ret.v.list = list_sort(list.v.list, reverseflag);
	push(ret);
    }
}

void op_strsub(void)
{
  Var source, what, with, caseflag, ret;

  caseflag.type = NUM;  caseflag.v.num = 0;
  if (nargs > 3) {
    caseflag = pop ();
  }
  with = pop ();
  what = pop ();
  source = pop ();
  if (source.type != STR || what.type != STR || with.type != STR) {
    raise (E_ARGTYPE);
  } else {
    ret.type = STR;
    ret.v.str = strsub (source.v.str->str, what.v.str->str, with.v.str->str,
      ISTRUE (caseflag));
    push (ret);
  }
  var_free (source);
  var_free (what);
  var_free (with);
  var_free (caseflag);
}

void op_psub (void)
{
  Var source, ret;

  source = pop ();
  if (source.type != STR) {
    raise (E_ARGTYPE);
  } else {
    ret.type = STR;
    ret.v.str = psub (source.v.str->str);
    push (ret);
  }
  var_free (source);
}

void op_servers (void)
{
  Var servlist;

  servlist.type = LIST;
  servlist.v.list = list_dup (server_list);

  push (servlist);
}

void op_ps (void)
{
  Var v;

  v.type = LIST;  v.v.list = ps();
  push (v);
}                               /* op_ps() */

void op_kill (void)
{
  Var pid;
  int r;

  pid = pop ();
  if (pid.type != NUM) {
    var_free (pid);
    raise (E_ARGTYPE);
  } else {
    r = cmkill (pid.v.num, frame.caller);
    switch (r) {
    case 0:
    case -1:
      pushn (r);
      break;
    case -2:
      raise (E_PERM);
      break;
    }
  }                             /* if */
}                               /* op_kill() */

void op_set_parse(void)
{
    Var		id, oid;

    oid = pop();  id = pop();
    if (oid.type != OBJ || id.type != NUM) {
	var_free(oid);  var_free(id);
	raise(E_ARGTYPE);
    } else if (!is_wizard(frame.this)) {
	raise(E_PERM);
    } else {
	set_parse(id.v.num, oid.v.obj.id);
	pushn(0);
    }
}