# A generic room in the moo. A room is any object that can contain other
# objects, including exits.

package Room;
use strict;
use vars qw(@ISA);
use Container;
use Utils;
use Error;
use UNIVERSAL qw(isa);
@ISA=qw{Container};

sub new {
	my $proto = shift;
	my $class = ref($proto) || $proto;
	my $this  = Container::new($class,@_);
	bless ($this, $class);
	return $this;
}

# This is only called after all the exits are tested, so it's safe to
# fail.
sub verb_go {
	my $this=shift;
	my $verbcall=shift;

	return "You can't go that way (".$verbcall->word('direct_object').").";
}

# Add a new thing to the room.
sub contents_add {
	my $this=shift;
	my $thing=shift;

	# Keep a seperate list of exits.
	if (isa($thing,"Exit")) {
		my @exits=@{$this->exits};
		push @exits,$thing;
		$this->exits(\@exits);
		$thing->location($this);
	}
	else {
		Container::contents_add($this,$thing);
	}
}

# Remove something from the room.
sub contents_remove {
	my $this=shift;
	my $thing=shift;
	
	if (isa($thing,"Exit")) {
		my @exits=();
		foreach (@{$this->exits}) {
			if ($_ ne $thing) {
				push @exits,$_;
			}
		}
		$this->exits(\@exits);
	}
	else {
		Container::contents_remove($this,$thing);
	}
}

# Rooms are responsible for all types of looking. In some cases, they just
# dispatch the look to other objects. I did it this way for a variety of
# reasons:
# * In a dark room, we don't want the user to be able to look at anything,
#   so it makes sense to centralize looking into the room.
# * The way verb definitions are set up forces us to handle container
#   looks here.
sub verb_look {
	my $this=shift;
	my $verbcall=shift;

	if ($verbcall->indirect_object) {
		if (isa($verbcall->indirect_object, "Container")) {
			# Looking inside a container. Pass this off to the container.
			return $verbcall->indirect_object->verb_look_inside($verbcall);
		}
		else {
			return $verbcall->indirect_object->name." is not a container.";
		}
	}
	elsif ($verbcall->word('indirect_object') ne undef) {
		# Failed look in non-existant container.
		return "There is no \"".$verbcall->word('indirect_object')."\" here.";
	}
	elsif ($verbcall->direct_object == $this) {
		# Looking at this room.
		return $this->look;
	}
	elsif ($verbcall->direct_object) {
		# Looking directly at some object, so pass it off to the object.
		return $verbcall->direct_object->verb_look($verbcall);
	}
	elsif ($verbcall->word('direct_object') eq undef) {
		# Just looking shows the room.
		return $this->look;
	}
	else {
		# Failed look.
		return "There is no \"".$verbcall->word('direct_object')."\" here.";
	}
}

# Override to show the room name as well as it's desc.
sub look {
	my $this=shift;
	return ($this->name,$this->description,$this->listobjects(ActiveUser::getactive));
}

# The room's help lets a user ask for help w/o specifiying an object.
sub verb_help {
	my $this=shift;
	my $verbcall=shift;
	
	if ($verbcall->direct_object) {
		# We were asked for object-specific help.
		return Container::verb_help($this,$verbcall);	
	}
	
	my $topic=$verbcall->word('direct_object');
	if ($topic eq undef) {
		$topic="index"; # special case.
	}
	# Query a variety of stuff for help, and build up a list of all
	# matches.
	my %help = $verbcall->caller->gethelp($topic);
	%help = (%help, $this->gethelp($topic));
	my $thing;
	foreach $thing (@{$this->exits}, @{$this->contents}) {
		if ($thing) {
			%help = (%help, $thing->gethelp($topic));
		}
	}
	# See if there's a generic help object and query it.
	my $generics=ThingList::FindByType('Generics');
	if ($generics) {
		my $genhelp=$generics->findgeneric('helpobject');
		if ($genhelp) {
			%help = (%help, $genhelp->gethelp($topic));
		}	
	}
	
	if (keys(%help) == 0) {
		return "Sorry, no help is available on that.";
	}
	elsif (keys(%help) == 1) {
		return values %help;
	}
	else {
		# Multiple possible matches.
		# Or are there? Check to see one of the matches is exactly
		# what the user asked for. If so, we can discard the partial
		# matches.
		foreach (keys %help) {
			if ($_ eq $topic) {
				return $help{$_};
			}
		}
		
		# TODO: use question_callback.
		return "Several help topics match your query:",
			map("\t $_", keys %help);
	}
}

sub verb_exits {
	my $this=shift;
	my $verbcall=shift;

	my $ret="";
	my $exit;
	
	if (Error::iserror($this->exits)) {
		return "You don't have permission to see the exits in this room.";
	}
	
	if ($#{$this->exits} == -1) {
		return "This room has no exits.";
	}
	
	foreach $exit (@{$this->exits}) {
		if ($exit) {
			if (isa($exit,"Exit") && isa($exit->destination,"Thing")) {
				$ret.=Text::subst($this->exit_msg, {
						name => $exit->name,
						roomname => $exit->destination->name,
					});
			}
		}
		else {
			Utils::Log("notice","oops in room exit list!!!");
		}
	}
	$ret=~s/\n$//;
	return $ret;
}

sub verb_say {
	my $this=shift;
	my $verbcall=shift;

	my $text=$verbcall->command;
	$text=~s/^\s*\w+\s+//; # remove command.

	$verbcall->caller->tell("You say, \"$text\"");
	$this->announce($verbcall->caller, $verbcall->caller->name." says, \"$text\"");
	return "";
}

sub verb_emote {
	my $this=shift;
	my $verbcall=shift;

	my $text=$verbcall->command;
	$text=~s/^\s*\w+\s+//; # remove command.
	
	if ($text=~m/^:(.*)/) {
		# Handle emote w/o space.
		$text=$verbcall->caller->name.$1;
	}
	else {
		$text=$verbcall->caller->name." ".$text;
	}	

	chomp $text;
	$this->announce(undef, $text);
	return "";
}

# cleans up after a room has been deleted
sub remove {
	my $this = shift;

	# we need to remove any exits leading out of/into this room...
	my $exit;
	foreach $exit (ThingList::FindByType("Exits")) {
		if (($exit->location == $this) || ($exit->destination == $this)) {
			$exit->remove;
	        }
	}
	
	return Container::remove($this);
}

# Say a string to all things in room except the one passed.
sub announce {
	my $this=shift;
	my $notyou=shift;
	my $text=shift;
	
	my $thing;
	
	foreach $thing (@{$this->contents}) {
		if ($thing && $thing ne $notyou) {
			$thing->tell($text);
		}
	}
}

1;
