/* 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 {
    frame.pc++;                 /* ignore nargs */
    server_running = 0;
    pushn (0);
  }
}

void op_dump (void)
{
  frame.pc++;                   /* ignore nargs */
  if (!is_wizard (frame.this)) {
    raise (E_PERM);
  } else {
    cache_sync ();
    pushn (0);
  }
}

void op_writelog (void)
{
  Var arg;

  arg = pop ();
  frame.pc++;                   /* ignore nargs */
  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_boot (void)
{
  frame.pc++;                   /* ignore nargs */
  boot (frame.this.id);
  pushn (0);
}

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

  frame.pc++;                   /* ignore nargs */
  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)
{
  int nargs;
  Objid parent;
  Var args;

  nargs = frame.m->code[frame.pc++];
  parent.server = 0;
  parent.id = frame.m->code[frame.pc++];
  args = pop_args (nargs);
  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;
  frame.pc++;                   /* skip nargs */

  parent = pop ();

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

void op_objsize (void)
{
  frame.pc++;                   /* skip nargs */

  pushn (size_object (this));
}

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

  frame.pc++;                   /* skip nargs */
  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)
{
  int nargs = frame.m->code[frame.pc++];
  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;

  frame.pc++;                   /* skip nargs */
  ret.type = STR;
  ret.v.str = decompile_object (this);
  push (ret);
}

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

  frame.pc++;                   /* skip nargs */
  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);
}

static void do_programming (Playerid id, int what1, void *what2)
{
  void **progwhat = MALLOC (void *, 2);

  progwhat[0] = (void *) what1;
  progwhat[1] = what2;
  if (start_programming (id, progwhat)) {
    tell (id, "Couldn't enter programming mode -- can't open temp file.");
  } else {
    tell (id, "Entering programming mode.  Use \".\" to end input.");
  }
}

void op_program (void)
{
  Var obj, method;

  switch (frame.m->code[frame.pc++]) {
  case 0:                      /* program some objects */
    do_programming (frame.this.id, -1, 0);
    pushn (0);
    break;
  case 1:                      /* invalid */
    method = pop ();
    var_free (method);
    raise (E_RANGE);
    break;
  case 2:                      /* program a single method */
    method = pop ();
    obj = pop ();
    if (obj.type != OBJ || method.type != STR) {
      raise (E_ARGTYPE);
    } else if (!valid (obj.v.obj)) {
      raise (E_OBJNF);
    } else if (!can_program (frame.this, 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 {
      do_programming (frame.this.id, obj.v.obj.id,
        (void *) string_dup (method.v.str));
      pushn (0);
    }
    var_free (obj);
    var_free (method);
    break;
  }
}

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);
}

void op_compile (void)
{
  Var obj, method, pcode;
  int nargs = frame.m->code[frame.pc++];
  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.this, 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;

  frame.pc++;                   /* ignore nargs */
  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;

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

void op_rmmethod (void)
{
  Var method;

  frame.pc++;
  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;

  frame.pc++;
  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)
{
  int nargs = frame.m->code[frame.pc++];
  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)
{
  int nargs = frame.m->code[frame.pc++];
  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_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 ();
  frame.pc++;                   /* skip nargs */
  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 ();
  frame.pc++;                   /* skip nargs */
  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_strsub (void)
{
  int nargs;
  Var source, what, with, caseflag, ret;

  caseflag.type = NUM;
  caseflag.v.num = 0;
  nargs = frame.m->code[frame.pc++];
  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 ();
  frame.pc++;                   /* skip nargs */
  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);

  frame.pc++;                   /* skip nargs */
  push (servlist);
}

void op_ps (void)
{
  Var v;

  frame.pc++;                   /* skip nargs */
  v.type = LIST;
  v.v.list = ps ();
  push (v);
}                               /* op_ps() */

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

  frame.pc++;                   /* skip nargs */

  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() */