/* 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"

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;
	    }
	    break;
	  case PARENT_NAME:
	    if(is_ancestor(value, obj)) {
		/* we'd create a loop */
		return 0;
	    } else {
		o->parent = value;
		return 1;
	    }
	    break;
	  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);
}