/* $Header: /belch_a/users/rearl/tinymuck/src/RCS/property.c,v 1.5 90/09/16 04:42:47 rearl Exp $ */

/*
 * $Log:	property.c,v $
 * Revision 1.5  90/09/16  04:42:47  rearl
 * Preparation code added for disk-based MUCK.
 * 
 * Revision 1.4  90/09/15  22:27:45  rearl
 * Fixed dangerous COMPRESS-related bug.
 * 
 * Revision 1.3  90/09/10  02:19:49  rearl
 * Introduced string compression of properties, for the
 * COMPRESS compiler option.
 * 
 * Revision 1.2  90/08/02  18:50:00  rearl
 * Fixed NULL pointer problem in get_property_class().
 * 
 * Revision 1.1  90/07/19  23:04:03  casie
 * Initial revision
 * 
 *
 */
 
#include "copyright.h"
#include "config.h"
#include "params.h"
#include "db.h"
#include "externs.h"

/* property.c
   A whole new lachesis mod.
   Adds property manipulation routines to TinyMUCK.   */

/* IN ALL CASES, the argument "value" is referred to only when "class" is
   a NULL string.                                                       */

/* modify a property list */
void insert_prop(struct plist *start, const char *type, const char *class)
{
  struct plist *curr, *prev;
  int diff;

  for(curr = start; curr; curr = curr -> next)
    {
      diff = string_compare(type, curr -> type);
      if(diff < 0)
	{
	  prev -> next = new_prop();
	  prev = prev -> next;
	  prev -> type = alloc_string(type);
	  prev -> class = alloc_compressed(class);
	  prev -> next = curr;
	  return;
	}
      if(diff == 0)
	{
	  if(curr -> class)
	    free((void *) curr -> class);
	  curr -> class = alloc_compressed(class);
	  return;
	}
      prev = curr;
    }
  prev -> next = new_prop();
  prev = prev -> next;
  prev -> type = alloc_string(type);
  prev -> class = alloc_compressed(class);
}

/* adds a new property to an object */
void
add_property(dbref player, const char *type, const char *class)
{
  struct plist *p, *new;

  if (!type || !*type || index(type, PROP_DELIMITER)) return;

  /* stick properties at the end because most frequently used ones
     will be put in front.                                      */
  if(*type == PROP_WIZARD)
    p = DBFETCH(player) -> attributes;
  else
    p = DBFETCH(player) -> properties;

  if(p) {
    if(string_compare(type, p -> type) >= 0) {    
      insert_prop(p, type, class);
      return;
    }
  }

  if(*type == PROP_WIZARD) {
    DBSTORE(player, attributes, new_prop());
    new = DBFETCH(player)->attributes;
  }
  else {
    DBSTORE(player, properties, new_prop());
    new = DBFETCH(player)->properties;
  }

  new -> type = alloc_string(type);
  new -> class = alloc_compressed(class);
  new -> next = p;
}

void add_attr(dbref player, const char *type, const char *class)
{
  char *attribute = (char *) calloc(strlen(type) + 2, sizeof(char));
  *attribute = PROP_WIZARD;
  strcat(attribute, type);

  if(class && *class)
    add_property(player, attribute, class);

  free((void *) attribute);
}

void delete_prop(struct plist *start, const char *type)
{
  struct plist *curr, *prev;
  int diff = 0;

  prev = start;

  for(curr = prev -> next; curr && (diff >= 0); curr = curr -> next)
    {
      diff = string_compare(type, curr -> type);
      if(diff == 0)
	{
	  prev -> next = curr -> next;
	  free_prop(curr);
	  return;
	}
      prev = curr;
    }
}

/* removes property list --- if it's not there then ignore */
void
remove_property(dbref player, const char *type)
{
  struct plist *p;

  if(*type == PROP_WIZARD)
    p = DBFETCH(player) -> attributes;
  else
    p = DBFETCH(player) -> properties;

  if(p) {
    if(string_compare(type, p -> type)) {
      delete_prop(p, type);
    }
    else {
      if(*type == PROP_WIZARD) {
	DBSTORE(player, attributes, p -> next);
      }
      else {
	DBSTORE(player, properties, p -> next);
      }
      free_prop(p);
    }
  }
}

/* checks if object has property, returning 1 if it or any of it's contents has
   the property stated                                                      */
int
has_property(dbref player, const char *type, const char *class)
{
  dbref things;

  if (has_property_strict(player, type, class))
    return 1;

  for (things = DBFETCH(player)->contents;
       things != NOTHING;
       things = DBFETCH(things)->next)
    if (has_property_strict(things, type, class))
      return 1;

  return 0;
}

struct plist * find_prop(struct plist *start, const char *type)
{
  struct plist *p;
  int diff = 0;

  for(p = start; p && (diff >= 0); p = p -> next)
    {
      diff = string_compare(type, p -> type);
      if(diff == 0)
	return p;
    }
return (struct plist *) 0;
}

/* checks if object has property, returning 1 if it has the property asked for */
int
has_property_strict(dbref player, const char *type, const char *class)
{
  struct plist *p;
  const char *str;

  str = get_compress(DoNull(class));

  if(*type == PROP_WIZARD)
    p = find_prop(DBFETCH(player)->attributes, type);
  else
    p = find_prop(DBFETCH(player)->properties, type);

  if(p)
    return (!string_compare(str, p -> class));

  return 0;
}


/* return class of property */
char *
get_property_class(dbref player, const char *type)
{
  struct plist *p;

  if(*type == PROP_WIZARD)
    p = find_prop(DBFETCH(player)->attributes, type);
  else
    p = find_prop(DBFETCH(player)->properties, type);

  if(p)
    return((char *) get_uncompress(p -> class));

  return((char *) 0);
}

char *
get_attr(dbref player, const char *type)
{
  char *result;
  char *attribute = (char *) calloc(strlen(type) + 2, sizeof(char));
  *attribute = PROP_WIZARD;
  strcat(attribute, type);

  result = (char *) get_property_class(player, attribute);
  free((void *) attribute);
  return result;
}

/* copies properties */
struct plist *
copy_prop(dbref old)
{
  struct plist *p;
  struct plist *new = 0, *first = 0;

  for (p = DBFETCH(old)->properties; p; p = p -> next)
    {
      if (!first)	first = new = new_prop();
      else              new -> next = new_prop();

      if (new -> next)
	new = new -> next;
      new -> type = alloc_string(p -> type);
      new -> class = alloc_compressed(p -> class);
    }
  return first;
}

/* copies attributes */
struct plist *
copy_attr(dbref old)
{
  struct plist *p;
  struct plist *new = 0, *first = 0;

  for (p = DBFETCH(old)->attributes; p; p = p -> next)
    {
      if (!first)       first = new = new_prop();
      else              new -> next = new_prop();

      if (new -> next)
        new = new -> next;
      new -> type = alloc_string(p -> type);
      new -> class = alloc_compressed(p -> class);
    }
  return first;
}

/* return old gender values for pronoun substitution code */
int
genderof(dbref player)
{
  char gender[5];
  sprintf(gender, "%c%s", PROP_WIZARD, "sex");

  if (has_property_strict(player, gender, "male"))
    return GENDER_MALE;
  else if (has_property_strict(player, gender, "female"))
    return GENDER_FEMALE;
  else if (has_property_strict(player, gender, "neuter"))
    return GENDER_NEUTER;
  else
    return GENDER_UNASSIGNED;
}