#include "copyright.h"

/* commands which set parameters */
#include <stdio.h>
#include <ctype.h>

#include "db.h"
#include "config.h"
#include "match.h"
#include "interface.h"
#include "externs.h"

#ifdef COMPRESS
#define alloc_compressed(x) alloc_string(compress(x))
#else /* COMPRESS */
#define alloc_compressed(x) alloc_string(x)
#endif /* COMPRESS */

#define FREE_STRING(X) X = ((X) ? (free ((void *) (X)), NULL) : NULL)

static dbref match_controlled(dbref player, const char *name)
{
    dbref match;

    init_match(player, name, NOTYPE);
    match_everything();

    match = noisy_match_result();
    if(match != NOTHING && !controls(player, match)) {
	notify(player, "Permission denied.");
	return NOTHING;
    } else {
	return match;
    }
}

void do_name(dbref player, const char *name, char *newname)
{
    dbref thing;
    char *password;

    if((thing = match_controlled(player, name)) != NOTHING) {
	/* check for bad name */
	if(*newname == '\0') {
	    notify(player, "Give it what new name?");
	    return;
	}

	/* check for renaming a player */
	if(Typeof(thing) == TYPE_PLAYER) {
	    /* split off password */
	    for(password = newname;
		*password && !isspace(*password);
		password++);
	    /* eat whitespace */
	    if(*password) {
		*password++ = '\0'; /* terminate name */
		while(*password && isspace(*password)) password++;
	    }

	    /* check for null password */
	    if(!*password) {
		notify(player,
		       "You must specify a password to change a player name.");
		notify(player, "E.g.: name player = newname password");
		return;
	    } else if(strcmp (db[thing].name, "guest") == 0) {
		notify(player,
		       "You are only a guest here...no name changing.");
		return;
	    } else if(!db[thing].password) {
		/* If original has no password, set one */
		db[thing].password = alloc_string(password);
	    } else if(strcmp(password, db[thing].password)) {
		notify(player, "Incorrect password.");
		return;
	    } else if(string_compare(newname, db[thing].name) &&
		      !ok_player_name(newname)) {
		notify(player, "You can't give a player that name.");
		return;
	    }
	    /* everything ok, notify */
	    writelog("NAME CHANGE: %s(#%d) to %s\n",
		    db[thing].name, thing, newname);
#ifdef PLAYER_LIST
	    delete_player(thing);
	    free((void *) db[thing].name);
	    db[thing].name = alloc_string(newname);
	    add_player(thing);
	    notify(player, "Name set.");
	    return;
#endif PLAYER_LIST	    
	} else {
	    if(!ok_name(newname)) {
		notify(player, "That is not a reasonable name.");
		return;
	    }
	}

	/* everything ok, change the name */
	if(db[thing].name) {
	    free((void *) db[thing].name);
	}
	db[thing].name = alloc_string(newname);
	notify(player, "Name set.");
    }
}

void do_describe(dbref player, const char *name, const char *description)
{
    dbref thing;

    if((thing = match_controlled(player, name)) != NOTHING) {
	if(db[thing].description) {
	    free((void *) db[thing].description);
	}
	db[thing].description = alloc_compressed(description);
	notify(player, "Description set.");
    }
}

void do_fail(dbref player, const char *name, const char *message)
{
    dbref thing;

    if((thing = match_controlled(player, name)) != NOTHING) {
	if(db[thing].fail_message) {
	    free((void *) db[thing].fail_message);
	}
	db[thing].fail_message = alloc_compressed(message);
	notify(player, "Message set.");
    }
}

void do_success(dbref player, const char *name, const char *message)
{
    dbref thing;

    if((thing = match_controlled(player, name)) != NOTHING) {
	if(db[thing].succ_message) {
	    free((void *) db[thing].succ_message);
	}
	db[thing].succ_message = alloc_compressed(message);
	notify(player, "Message set.");
    }
}

void do_osuccess(dbref player, const char *name, const char *message)
{
    dbref thing;

    if((thing = match_controlled(player, name)) != NOTHING) {
	if(db[thing].osuccess) {
	    free((void *) db[thing].osuccess);
	}
	db[thing].osuccess = alloc_compressed(message);
	notify(player, "Message set.");
    }
}

void do_ofail(dbref player, const char *name, const char *message)
{
    dbref thing;

    if((thing = match_controlled(player, name)) != NOTHING) {
	if(db[thing].ofail) {
	    free((void *) db[thing].ofail);
	}
	db[thing].ofail = alloc_compressed(message);
	notify(player, "Message set.");
    }
}

void do_lock(dbref player, const char *name, const char *keyname)
{
    dbref thing;
    struct boolexp *key;

    init_match(player, name, NOTYPE);
    match_everything();

    switch(thing = match_result()) {
      case NOTHING:
	notify(player, "I don't see what you want to lock!");
	return;
      case AMBIGUOUS:
	notify(player, "I don't know which one you want to lock!");
	return;
      default:
	if(!controls(player, thing)) {
	    notify(player, "You can't lock that!");
	    return;
	}
	break;
    }

    key = parse_boolexp(player, keyname);
    if(key == TRUE_BOOLEXP) {
	notify(player, "I don't understand that key.");
    } else {
	/* everything ok, do it */
	free_boolexp(db[thing].key);
	db[thing].key = key;
	notify(player, "Locked.");
    }
}

void do_unlock(dbref player, const char *name)
{
    dbref thing;

    if((thing = match_controlled(player, name)) != NOTHING) {
	free_boolexp(db[thing].key);
	db[thing].key = TRUE_BOOLEXP;
	notify(player, "Unlocked.");
    }
}

void do_unlink(dbref player, const char *name)
{
    dbref exit;

    init_match(player, name, TYPE_EXIT);
    match_exit();
    match_here();
    if(Wizard(player)) {
	match_absolute();
    }

    switch(exit = match_result()) {
      case NOTHING:
	notify(player, "Unlink what?");
	break;
      case AMBIGUOUS:
	notify(player, "I don't know which one you mean!");
	break;
      default:
	if(!controls(player, exit)) {
	    notify(player, "Permission denied.");
	} else {
	    switch(Typeof(exit)) {
	      case TYPE_EXIT:
		db[exit].location = NOTHING;
		notify(player, "Unlinked.");
		break;
	      case TYPE_ROOM:
		db[exit].location = NOTHING;
		notify(player, "Dropto removed.");
		break;
	      default:
		notify(player, "You can't unlink that!");
		break;
	    }
	}
    }
}

void do_chown(dbref player, const char *name, const char *newobj)
{
    dbref thing;
    dbref owner;
    
    init_match(player, name, NOTYPE);
    match_everything();

    if((thing = noisy_match_result()) == NOTHING) {
	return;
    } else if((owner = lookup_player(newobj)) == NOTHING &&
	      string_compare(newobj,"me")) {
	notify(player, "I couldn't find that player.");
    } else if(Typeof(thing) == TYPE_PLAYER) {
	notify(player, "Players always own themselves.");
    } else if (!Wizard(player) &&
	       ((string_compare(newobj,"me") &&
		 owner != player) || !(db[thing].flags & UNWANTED))) {
	notify(player, "Permission denied.");
    } else if (!Wizard(player) && !could_doit(player,thing)) {
	notify(player, "That item is locked.");
    } else {
	if (!string_compare(newobj,"me"))
	    owner = player;
	db[thing].owner = owner;
	notify(player, "Owner changed.");
    }
}

void do_set(dbref player, const char *name, const char *flag)
{
    dbref thing;
    const char *p;
    object_flag_type f;

    /* find thing */
    if((thing = match_controlled(player, name)) == NOTHING) return;

    /* move p past NOT_TOKEN if present */
    for(p = flag; *p && (*p == NOT_TOKEN || isspace(*p)); p++);

    /* identify flag */
    if(*p == '\0') {
	notify(player, "You must specify a flag to set.");
	return;
    } else if(string_prefix("LINK_OK", p)) {
	f = LINK_OK;
    } else if(string_prefix("DARK", p)) {
	f = DARK;
    } else if(string_prefix("STICKY", p)) {
	f = STICKY;
    } else if(string_prefix("WIZARD", p) ||
    	      string_prefix("TINKER", p)) {
	f = WIZARD;
    } else if(string_prefix("TEMPLE", p) ||
    	      string_prefix("JUNKPILE", p)) {
	f = TEMPLE;
    } else if(string_prefix("HAVEN", p)) {
	f = HAVEN;
    } else if(string_prefix("ABODE", p)) {
	f = ABODE;
    } else if(string_prefix("UNWANTED", p)) {
	f = UNWANTED;
#ifdef ROBOT_MODE
    } else if(string_prefix("ROBOT", p) ||
	      string_prefix("BOT", p)) {
	if (Typeof(thing) == TYPE_PLAYER) {
	    if (Wizard(player)) {
		writelog ("ROBOT: success %s(%d) %s %s(%d)\n",
			  db[player].name, player,
			  *flag == NOT_TOKEN ? "reset" : "set",
			  db[thing].name, thing);
	    } else {
		writelog ("ROBOT: failed %s(%d) %s %s(%d)\n",
			  db[player].name, player,
			  *flag == NOT_TOKEN ? "reset" : "set",
			  db[thing].name, thing);
		notify(player,
#ifndef TINKER
		       "Only a Wizard can designate a player robotic.");
#else TINKER
		       "Only a Tinker can designate a player robotic.");
#endif TINKER
		return;
	    }
	}
	f = ROBOT;
#endif ROBOT_MODE
    } else if(string_prefix("TABULAR_WHO", p)) {
	f = TABULAR_WHO;
    } else if(string_prefix("REVERSED_WHO", p)) {
	f = REVERSED_WHO;
#ifdef GENDER
    } else if(string_prefix("MALE", p) || string_prefix("FEMALE", p) ||
              string_prefix("NEUTER", p)   || string_prefix("UNASSIGNED", p)) {
        if (Typeof(thing) != TYPE_PLAYER) {
	    notify(player, "Sorry, only players have gender.");
	    return;
        }
        if (thing != player && !Wizard(player)) {
	    notify(player, "You can only give yourself a sex-change.");
	    return;
        }
        db[thing].flags &= ~GENDER_MASK;
	if(string_prefix("UNASSIGNED", p) || *flag == NOT_TOKEN) {
	    db[thing].flags |= (GENDER_UNASSIGNED << GENDER_SHIFT);
	    notify(player, "Gender set to unassigned.");
	} else if (string_prefix("MALE", p)) {
	    db[thing].flags |= (GENDER_MALE << GENDER_SHIFT);
	    notify(player, "Gender set to male.");
        } else if(string_prefix("FEMALE", p)) {
	    db[thing].flags |= (GENDER_FEMALE << GENDER_SHIFT);
	    notify(player, "Gender set to female.");
        } else if(string_prefix("NEUTER", p)) {
	    db[thing].flags |= (GENDER_NEUTER << GENDER_SHIFT);
	    notify(player, "Gender set to neuter.");
	}
        return;
#endif /* GENDER */
#ifdef RESTRICTED_BUILDING
    } else if(string_prefix("BUILDER", p) ||) {
    	      string_prefix("CONSTRUCTOR", p)
	f = BUILDER;
#endif				/* RESTRICTED_BUILDING */
    } else {
	notify(player, "I don't recognize that flag.");
	return;
    }

    /* check for restricted flag */
    if(!Wizard(player)
       && (f == TEMPLE
#ifdef RESTRICTED_BUILDING
	   || f == BUILDER
#endif /* RESTRICTED_BUILDING */
	   || f == DARK &&
	   (Typeof(thing) != TYPE_ROOM && Typeof(thing) != TYPE_THING))) {
	notify(player, "Permission denied.");
	return;
    }

#ifdef GOD_PRIV
    if (!God(player) && f == DARK && Typeof(thing) == TYPE_PLAYER) {
	notify(player, "Permission denied.");
	return;
    }
#endif GOD_PRIV

    if (!Wizard(player) && f == DARK && Typeof(thing) == TYPE_THING &&
    	db[thing].location != player) {
	notify(player, "You must be holding an object to set it DARK.");
	return;
    }

#ifdef GOD_PRIV
    if(!God(player) && (f == WIZARD)) {
#else
    if (!Wizard(player) && (f == WIZARD)) {
#endif GOD_PRIV
	writelog ("WIZARD: failed %s(%d) %s %s(%d)\n",
		  db[player].name, player,
		  *flag == NOT_TOKEN ? "reset" : "set",
		  db[thing].name, thing);

	notify(player, "Permission denied.");
	return;
    }

    if (f == WIZARD) {
	writelog ("WIZARD: success %s(%d) %s %s(%d)\n",
		  db[player].name, player,
		  *flag == NOT_TOKEN ? "reset" : "set",
		  db[thing].name, thing);
    }

#ifdef GOD_PRIV
    /* check for unGODding */
    if (f == WIZARD && God(thing)) {
	notify(player, "Gods can not me made mortal.");
	return;
    }
#else
    if (f == WIZARD && thing == player) {
	notify(player, "You cannot make yourself mortal!");
	return;
    }
#endif GOD_PRIV

    if ( f==HAVEN && !(Typeof(thing) == TYPE_ROOM ||
		       Typeof(thing) == TYPE_PLAYER)) {
	notify(player, "Only rooms or players can be HAVEN.");
	return;
    }

    if (f==UNWANTED && Typeof(thing)==TYPE_PLAYER) {
	notify(player, "You should always want yourself.");
	return;
    }

    if ((f==TABULAR_WHO || f==REVERSED_WHO) &&
	Typeof(thing) != TYPE_PLAYER) {
	notify(player, "Player preference flags can only be set on players.");
	return;
    }
    
    /* else everything is ok, do the set */
    if(*flag == NOT_TOKEN) {
	/* reset the flag */
	db[thing].flags &= ~f;
	notify(player, "Flag reset.");
    } else {
	/* set the flag */
	db[thing].flags |= f;
	notify(player, "Flag set.");
    }
}

#ifdef RECYCLE
/****************************************************************
 * do_recycle: Allow any player to give an item away...the item
 * is @chowned to the "Recycler" player, and if it's a thing,
 * it is @linked to the home of the "Recycler" as well.
 ****************************************************************/

void do_recycle(dbref player, const char *name)
{
    dbref thing, recip, i;
    char buf[BUFFER_LEN];

    if (!name || !*name) {
	notify(player, "You must specify an object to @recycle.");
	return;
    }

    if((thing = match_controlled(player, name)) == NOTHING) return;
    
    if(Typeof(thing) == TYPE_PLAYER) {
	notify(player, "You can't @recycle players.");
	return;
    }

    if((recip = lookup_player(RECYCLER)) == NOTHING) {
	notify(player, "There is no current Recycler.");
	return;
    }
    
    /* Okay - do it */
    db[thing].owner = recip;

    /* If its a thing, link it to the Recyclers home */
    if (Typeof(thing) == TYPE_THING) {
	db[thing].exits = db[recip].exits;
	moveto (thing, db[thing].exits);
    }

    /* Clear its strings */
    FREE_STRING (db[thing].name);
    FREE_STRING (db[thing].description);
    FREE_STRING (db[thing].fail_message);
    FREE_STRING (db[thing].ofail);
    FREE_STRING (db[thing].succ_message);
    FREE_STRING (db[thing].osuccess);
    sprintf (buf, "junk-%d", thing);
    db[thing].name = alloc_string(buf);

    /* unlock it */
    free_boolexp(db[thing].key);
    db[thing].key = TRUE_BOOLEXP;

    /* Make it worthless */
    db[thing].pennies = 1;

    /* Make it up for grabs */
    db[thing].flags |= UNWANTED;

    /* Tell player we did it */    
    notify(player, "Object recycled.");
}

/****************************************************************
 * do_count: count things...
 *	players:	Number of objects owned, carried
 *	rooms:		number of exits from & to, number contents
 ****************************************************************/

void do_count(dbref player, const char *name)
{
    dbref thing, i, exit;
    char buf[BUFFER_LEN];
    int owned=0, contents=0, from=0, to=0, objects=0, rooms=0, exits=0;
    int rowned=0, rplayers=0, robjects=0, rrooms=0, rexits=0;

    if (!name || !*name) {
	notify(player, "You must specify an object to @count.");
	return;
    }

    if((thing = match_controlled(player, name)) == NOTHING) return;

    if(Typeof(thing) != TYPE_PLAYER && Typeof(thing) != TYPE_ROOM) {
	notify(player, "You can only @count people or rooms.\n");
	return;
    }

    if(!payfor(player, FIND_COST)) {
	notify(player, "You don't have enough pennies.");
	return;
    }
    
    switch (Typeof(thing)) {
      case TYPE_PLAYER:
	for (i=0; i<db_top; i++) {
	    if (db[i].location == thing) contents++;
	    if (db[i].owner == thing) {
		switch (Typeof(i)) {
		  case TYPE_THING:	objects++; break;
		  case TYPE_EXIT:	exits++; break;
		  case TYPE_ROOM:	rooms++; break;
		}
		owned++;
	    }
	}
	sprintf(buf,
	     "%s owns %d objects (%d things, %d rooms, %d exits), %d carried.",
		unparse_object(player, thing),
	        owned, objects, rooms, exits, contents);
	notify(player, buf);
	break;

      case TYPE_ROOM:
	for (i=0; i<db_top; i++) {
	    if (db[i].location == thing) {
		if (Typeof(i) == TYPE_EXIT) {
		    to++;
		} else {
		    contents++;
		}
	    }
	}
	DOLIST(exit, db[thing].exits) {
	    from++;
	}
	sprintf(buf, "%s has %d entrances, %d exits, %d objects.",
		unparse_object(player, thing), to, from, contents);
	notify(player, buf);
	
	if (!Wizard (player)) break;

	for (i=0; i<db_top; i++) {
	    if (db[db[i].owner].location == thing) {
	    	switch (Typeof(i)) {
		  case TYPE_EXIT: rexits++; break;
  		  case TYPE_ROOM: rrooms++; break;
  		  case TYPE_THING: robjects++; break;
		  case TYPE_PLAYER: rplayers++; break;
		}
		rowned++;
	    }
	}

	if (rplayers == 0) {
	    sprintf (buf, "There are no players in %s.\n",
	    unparse_object (player, thing));
	} else {
	    sprintf(buf,
	    	"The %d player%s in %s own%s %d %s: %d %s, %d %s, %d %s\n.",
		rplayers, (rplayers == 1) ? "" : "s",
		unparse_object(player, thing), (rplayers == 1) ? "s" : "",
		rowned, "objects", robjects, "things", rrooms, "rooms",
		rexits, "exits");
	}

	notify(player, buf);

	break;
    }

}
#endif RECYCLE