/* Copyright 1989, 1990 by James Aspnes, David Applegate, and Bennet Yee */
/* See the file COPYING for distribution information */
#include <ctype.h>

#include "db.h"
#include "globals.h"
#include "externs.h"

extern set get_contents (datum);

datum lookup (datum obj, datum var)
{
  struct object *o;
  datum value;

  /* handle special cases */
  if (var < FIXED_STRINGS) {
    if ((o = object (obj)) == 0) {
      return NOTHING;
    } else if (isflagname (var)) {
      return flag_set (obj, name2flag (var));
    } else
      switch (var) {
      case OWNER_NAME:
        return o->owner;
      case PARENT_NAME:
        return o->parent;
      default:
        /* fall through, not special */
        break;
      }
  }

  while ((o = object (obj)) != 0) {
    if (assoc (o->vars, var, &value))
      return value;
    else
      obj = o->parent;
  }
  return NOTHING;
}

datum set_variable (datum obj, datum var, datum value)
{
  struct object *o;
  datum flag;
  set loc_contents;

  if (!controls (me, obj)
    || (o = object (obj)) == 0)
    return 0;

  /* handle special cases */
  if (var < FIXED_STRINGS) {
    if (isflagname (var)) {
      flag = name2flag (var);
      switch (flag) {
      case F_ADMIN:
        return 0;               /* can't change admin status */
      case F_WIZARD:
      case F_CONNECTED:
        if (!flag_set (me, F_ADMIN))
          return 0;
        break;
      case F_PLAYER:
      case F_BUILDER:
      case F_PROGRAMMER:
        if (!flag_set (me, F_WIZARD))
          return 0;
        break;
      default:
        break;
      }

      /* permission ok, do it */
      if (value) {
        o->flags |= flag;
      } else {
        o->flags &= ~flag;
      }
      return 1;
    } else
      switch (var) {
      case OWNER_NAME:
        if (!flag_set (me, F_WIZARD)) {
          return 0;
        } else {
          o->owner = value;
          return 1;
        }
      case PARENT_NAME:
        if (is_ancestor (value, obj)) {
          /* we'd create a loop */
          return 0;
        } else {
          o->parent = value;
          return 1;
        }
      case ALIASES_NAME:
        /* nuke the name list on our location's contents */
        /* other lists will just have to lose until the next GC pass */
        if ((loc_contents = get_contents (o->location)) != 0) {
          set_clear_name_list (loc_contents);
        }
        /* fall through to normal variable set */
        break;
      default:
        /* nothing special, fall through */
        break;
      }
  }

  gc_mark_string (var);         /* for incremental gc */
  o->vars = set_assoc (o->vars, var, value);
  return 1;
}

datum unset (datum obj, datum var)
{
  struct object *o;

  if (var < FIXED_STRINGS) {
    /* let set_variable deal with it */
    return set_variable (obj, var, NOTHING);
  } else if (!controls (me, obj) || (o = object (obj)) == 0) {
    return 0;                   /* no such object as far as I'm concerned */
  } else {
    /* it's real, nuke it */
    o->vars = del_assocs (o->vars, var);
    return 1;
  }
}

datum lookup_action (datum obj, datum verb)
{
  struct object *o;
  datum value;

  while ((o = object (obj)) != 0) {
    if (assoc (o->actions, verb, &value))
      return value;
    else
      obj = o->parent;
  }

  return NOTHING;
}

datum set_string (datum obj, datum var, datum value)
{
  gc_mark_string (value);
  return set_variable (obj, var, value);
}

datum set_action (datum obj, datum verb, datum value)
{
  struct object *o;

  if (controls (me, obj)
    && (o = object (obj)) != 0) {
    gc_mark_string (verb);
    gc_mark_string (value);
    o->actions = set_assoc (o->actions, verb, value);
    return 1;
  } else {
    return 0;
  }
}

datum unset_action (datum obj, datum verb)
{
  struct object *o;

  if (controls (me, obj)
    && (o = object (obj)) != 0) {
    o->actions = del_assocs (o->actions, verb);
    return 1;
  } else {
    return 0;
  }
}

static set lookup_setvar_internal (struct object *o, datum var)
{
  datum value;

  if (assoc (o->sets, var, &value)) {
    return ((set) value);
  } else {
    return empty_set ();
  }
}

set lookup_setvar (datum obj, datum var)
{
  struct object *o;

  if ((o = object (obj)) != 0) {
    return lookup_setvar_internal (o, var);
  } else {
    return empty_set ();
  }
}

/* no permissions checks */
datum add_to_internal (datum obj, datum var, datum value)
{
  struct object *o;

  if ((o = object (obj)) != 0) {
    o->sets =
      set_assoc (o->sets, var,
      (datum) add_member (lookup_setvar_internal (o, var), value));
    gc_mark_string (var);
    return 1;
  } else {
    return 0;
  }
}

datum add_to (datum obj, datum var, datum value)
{
  if (controls (me, obj) && var != CONTENTS_NAME) {
    return add_to_internal (obj, var, value);
  } else {
    return 0;
  }
}

/* no permissions checks */
datum take_from_internal (datum obj, datum var, datum value)
{
  struct object *o;

  if ((o = object (obj)) != 0) {
    o->sets =
      set_assoc (o->sets, var,
      (datum) del_member (lookup_setvar_internal (o, var), value));
    return 1;
  }
  return 0;
}

datum take_from (datum obj, datum var, datum value)
{
  if (controls (me, obj) && var != CONTENTS_NAME) {
    return take_from_internal (obj, var, value);
  } else {
    return 0;
  }
}

datum clear_set_var (datum obj, datum var)
{
  datum value;
  struct object *o;

  if (controls (me, obj)
    && (o = object (obj)) != 0) {
    if (assoc (o->sets, var, &value)) {
      free_set ((set) value);
      o->sets = del_assocs (o->sets, var);
    }
    return 1;
  }
  return 0;
}

datum contains (datum obj, datum setvar, datum thing)
{
  return member (lookup_setvar (obj, setvar), thing);
}

datum count_set_var (datum obj, datum setvar)
{
  set s;

  s = lookup_setvar (obj, setvar);
  return set_empty (s) ? 0 : set_count (s);
}

set get_contents (datum obj)
{
  return lookup_setvar (obj, CONTENTS_NAME);
}