key0-96/
key0-96/doc/key/
key0-96/doc/key/credits/
key0-96/doc/key/developers/
key0-96/doc/key/developers/resources/
key0-96/setup/caves/
key0-96/setup/help/
key0-96/setup/ruins/
key0-96/src/
key0-96/src/commands/
key0-96/src/events/
key0-96/src/hack/
key0-96/src/sql/
key0-96/src/swing/
key0-96/src/talker/forest/
key0-96/src/talker/objects/
key0-96/src/terminals/
/*
**               j###t  ########## ####   ####
**              j###t   ########## ####   ####
**             j###T               "###L J###"
**          ######P'    ##########  #########
**          ######k,    ##########   T######T
**          ####~###L   ####
**          #### q###L  ##########   .#####
**          ####  \###L ##########   #####"
**
**  $Id$
**
**  Class History
**
**  Date        Name         Description
**  ---------|------------|-----------------------------------------------
**  29Oct98     subtle       created
**
*/

package key.commands;

import key.*;
import java.io.IOException;
import java.util.StringTokenizer;
import java.util.Enumeration;
import java.lang.reflect.Method;
import java.lang.reflect.InvocationTargetException;

public class Verb extends Command
{
	private static final long serialVersionUID = 7601571514137070656L;
	
	public static final AtomicElement[] ELEMENTS =
	{
		AtomicElement.construct( Verb.class, Boolean.TYPE, "checkInventory",
			AtomicElement.PUBLIC_FIELD,
			"true iff the players inventory should be checked for the object" ),
		AtomicElement.construct( Verb.class, Boolean.TYPE, "checkRoom",
			AtomicElement.PUBLIC_FIELD,
			"true iff the players current room should be checked for the object" ),
		AtomicElement.construct( Verb.class, String.class, "verb",
			AtomicElement.PUBLIC_ACCESSORS,
			"the name of the method to call on the matched object" )
	};
	
	public static final AtomicStructure STRUCTURE = new AtomicStructure( Command.STRUCTURE, ELEMENTS );
	
	public boolean checkInventory;
	public boolean checkRoom;
	protected String verb = Material.getDefaultVerb();
	protected transient Method method;
	
	public AtomicStructure getDeclaredStructure()
	{
		return( STRUCTURE );
	}
	
	public Verb()
	{
		setKey( "use" );
		usage = "<object> [<option args>]";
		method = Material.getMethod( verb );
	}
	
	/**
	  * @return true iff this was a keyword and we've done everything we need to
	 */
	protected boolean handleKeyword( Player p, StringTokenizer args, String fullLine, CategoryCommand caller, InteractiveConnection ic, Flags flags, String keyword ) throws IOException
	{
		if( keyword.equalsIgnoreCase( "all" ) )
		{
			Room r = p.getLocation();
			Inventory i = p.getInventory();
			
			int count = 0;
			
			if( checkInventory )
			{
				for( Enumeration e = i.elements(); e.hasMoreElements(); )
				{
					Thing t = (Thing) e.nextElement();
					
					doIt( p, args, fullLine, ic, flags, i, r, t );
					count++;
				}
			}
			
			if( checkRoom )
			{
				for( Enumeration e = r.elements(); e.hasMoreElements(); )
				{
					Thing t = (Thing) e.nextElement();
					
					doIt( p, args, fullLine, ic, flags, i, r, t );
					count++;
				}
			}
			
			if( count == 0 )
			{
				if( checkRoom && checkInventory )
					ic.sendFailure( "There aren't any objects in your inventory or this room." );
				else if( checkRoom )
					ic.sendFailure( "There aren't any objects in this room." );
				else if( checkInventory )
					ic.sendFailure( "There aren't any objects in your inventory." );
				else
					ic.sendFailure( "This command will never match any object, it is improperly set up." );
			}
			
			return true;
		}
		else
			return( false );
	}
	
	protected void handleNoArgs( Player p, StringTokenizer args, String fullLine, CategoryCommand caller, InteractiveConnection ic, Flags flags ) throws IOException
	{
		usage( ic );
	}
	
		//  We can't run with scissors, but we can run with items.
	protected final void runWithItem( Player p, StringTokenizer args, String fullLine, CategoryCommand caller, InteractiveConnection ic, Flags flags, String nt ) throws IOException
	{
		if( !handleKeyword( p, args, fullLine, caller, ic, flags, nt ) )
		{
			Room r = p.getLocation();
			Inventory i = p.getInventory();
			
			Thing t = null;
			
			if( checkInventory )
				t = getObjectIn( p, i, ic, nt, null );
			
				//  if it wasn't in the inventory, get it from the room
				//  (ideally, a joint object collection would be best,
				//   but it probably isn't worth it.  another way would
				//   be to just get the strength of the match from
				//   both, but it's added complexity that may not be necessary)
			if( t == null && checkRoom )
				t = getObjectIn( p, r, ic, nt, null );
			
			if( t != null )
				doIt( p, args, fullLine, ic, flags, i, r, t );
			else if( checkRoom && checkInventory )
				ic.sendFailure( "This object is not in your inventory or this room." );
			else if( checkRoom )
				ic.sendFailure( "This object is not in this room." );
			else if( checkInventory )
				ic.sendFailure( "This object is not in your inventory." );
			else
				ic.sendFailure( "This command will never match an object, it is improperly set up." );
		}
	}
	
	public void run( Player p, StringTokenizer args, String fullLine, CategoryCommand caller, InteractiveConnection ic, Flags flags ) throws IOException
	{
		if( !args.hasMoreTokens() )
		{
			handleNoArgs( p, args, fullLine, caller, ic, flags );
			return;
		}
		
		String nt = args.nextToken();
		runWithItem( p, args, fullLine, caller, ic, flags, nt );
	}
	
	protected void doIt( Player p, StringTokenizer args, String fullLine, InteractiveConnection ic, Flags flags, Inventory i, Room r, Thing t )
	{
		if( verb != null && verb.length() != 0 )
		{
			if( method == null )
				method = Material.getMethod( verb );
			
			if( method != null )
			{
				Object[] params = new Object[ Material.verbParameters.length ];
				
				params[0] = p;
				params[1] = args;
				params[2] = ic;
				params[3] = flags;
				params[4] = (Atom) t;
				
				try
				{
					method.invoke( t, params );
				}
				catch( InvocationTargetException e )
				{
					Throwable x = e.getTargetException();
					
					Log.error( "while invoking verb " + method.toString(), x );
					if( p.isExpert() )
					{
						ic.sendFailure( x.toString() + "\nduring ^h" + method.toString() + "^-" );
						ic.printStackTrace( x );
					}
					else
						ic.sendFailure( "You may not do this." );
				}
				catch( Exception e )
				{
					Log.error( "while invoking verb " + method.toString(), e );
					if( p.isExpert() )
					{
						ic.sendFailure( e.toString() + "\nduring ^h" + method.toString() + "^-" );
						ic.printStackTrace( e );
					}
					else
						ic.sendFailure( "You may not do this." );
				}
			}
			else
				ic.sendFailure( "Could not find method '" + verb + "'." );
		}
		else
			ic.sendFailure( "This command is incorrectly set up" );
	}
	
	public String getVerb()
	{
		return( verb );
	}
	
	public void setVerb( String v )
	{
		Method m = Material.getMethod( v );
		
		if( m != null )
		{
			verb = v;
			method = m;
		}
		else
			throw new InvalidSearchException( "Unknown verb '" + v + "'.  Try one of: " + Material.getAvailableVerbs() );
	}
}