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

package key;

import java.util.Enumeration;
import java.util.StringTokenizer;
import java.util.Hashtable;
import java.util.Vector;
import java.io.*;

import java.lang.reflect.*;

/**
  *  Material
  *
  *  The base class for most user-manipulatable objects
 */
public abstract class Material extends Atom implements Thing
{
	private static final long serialVersionUID = -3366054986544050407L;
	
	public Material()
	{
			//  objects typically start out
			//  as publically moveable.
		getPermissionList().allow( Atom.moveAction );
	}
	
	public Reference build( Player p )
	{
		return( getThis() );
	}
	
	public String[] wearLocations( Player p )
	{
		return( new String[0] );
	}
	
//---  default implementations  --------------------------------
	public void use( Player p, StringTokenizer args, InteractiveConnection ic, Flags flags, Atom item )
	{
		ic.send( "You cannot use the " + getFullPortrait( p ) + "." );
	}
	
	public void wear( Player p, StringTokenizer args, InteractiveConnection ic, Flags flags, Atom item )
	{
			//  the default implementation is to allow the player to
			//  wear it
		defaultWear( p, args, ic, flags, item );
	}
	
	protected final void defaultWear( Player p, StringTokenizer args, InteractiveConnection ic, Flags flags, Atom item )
	{
		String[] wl = wearLocations( p );
		
		if( wl == null || wl.length == 0 )
		{
			ic.send( "You may not wear the " + getFullPortrait( p ) + "." );
			return;
		}
		
		Inventory i = p.getInventory();
		
			//  check all locations are free
		for( int j = 0; j < wl.length; j++ )
		{
			Thing o = (Thing) i.getProperty( wl[j] );
			
			if( o != null )
			{
				ic.sendFailure( "You are already wearing the " + getFullPortrait( p ) + " on your " + wl[j] + "." );
				return;
			}
		}
		
			//  place it in all locations
		for( int j = 0; j < wl.length; j++ )
		{
			i.setProperty( wl[j], this );
		}
		
		ic.sendFeedback( "You are now wearing the " + getFullPortrait( p ) + "." );
	}
	
	protected final void defaultRemove( Player p, StringTokenizer args, InteractiveConnection ic, Flags flags, Atom item )
	{
		String[] wl = wearLocations( p );
		
		if( wl == null || wl.length == 0 )
		{
			ic.send( "You may not wear the " + getFullPortrait( p ) + "." );
			return;
		}
		
		Inventory i = p.getInventory();
		boolean didRem = false;
		
			//  check all locations are free
		for( int j = 0; j < wl.length; j++ )
		{
			Thing o = (Thing) i.getProperty( wl[j] );
			
			if( o == item )
			{
				i.setProperty( wl[j], null );
				didRem = true;
			}
		}
		
		if( didRem )
			ic.sendFeedback( "You are no longer wearing the " + getFullPortrait( p ) + "." );
		else
			ic.sendFeedback( "But you aren't wearing a " + getFullPortrait( p ) + "." );
	}
	
	public void remove( Player p, StringTokenizer args, InteractiveConnection ic, Flags flags, Atom item )
	{
		defaultRemove( p, args, ic, flags, item );
	}
	
	public void wield( Player p, StringTokenizer args, InteractiveConnection ic, Flags flags, Atom item )
	{
		ic.send( "You cannot wield the " + getFullPortrait( p ) + "." );
	}
	
	public void read( Player p, StringTokenizer args, InteractiveConnection ic, Flags flags, Atom item )
	{
		ic.send( "You cannot read the " + getFullPortrait( p ) + "." );
	}
	
	public void look( Player p, StringTokenizer args, InteractiveConnection ic, Flags flags, Atom item )
	{
		ic.send( "You don't see anything of note" );
	}
	
	/**
	  *  It is not real friendly to override this method in order
	  *  to hide the inspect information - it's very useful stuff.
	 */
	public void inspect( Player p, StringTokenizer args, InteractiveConnection ic, Flags flags, Atom item )
	{
		//ic.send( "You do not descry anything of note about the " + getFullPortrait( p ) + "." );
		defaultInspect( p, args, ic, flags, item );
	}
	
	public final static void anyAtomInspect( Player p, StringTokenizer args, InteractiveConnection ic, Flags flags, Atom th )
	{
		Reference owner = th.getOwnerReference();
		StringBuffer sb = new StringBuffer( th.getId() );
		Type type = Type.typeOf( th );
		
		sb.append( " is" );
		
		if( type != null )
		{
			sb.append( ' ' );
			sb.append( Grammar.aAn( type.getName() ) );
		}
		
		if( owner.isValid() )
		{
			sb.append( " owned by " );
			sb.append( owner.getName() );
		}
		
		sb.append( '.' );
		
		ic.send( sb.toString() );
	}
	
	public final static void standardInspect( Player p, StringTokenizer args, InteractiveConnection ic, Flags flags, Thing th )
	{
		ic.sendLine();
		anyAtomInspect( p, args, ic, flags, (Atom) th );
		ic.sendLine();
		
		{
			Vector verbs = new Vector( 10, 10 );
			
				//  this is lots of fun.
				//  scan through the methods on this object comparing
				//  them to these defaults and see what has changed.
			Class ourClass = th.getClass();
			Method[] localMethods = ourClass.getMethods();
			
			for( int i = 0; i < localMethods.length; i++ )
			{
				Method m = localMethods[i];
				Class[] parameters = m.getParameterTypes();
				
				if( parametersEquivalent( parameters, verbParameters ) )
				{
					String mn = m.getName();
					
					if( th.isMethodSpecial( m ) )
					{
							//  this one is different
						verbs.addElement( mn );
					}
				}
			}
			
			if( verbs.size() == 0 )
				ic.send( "No active verbs" );
			else
				ic.send( "Verbs: ^h" + Grammar.enumerate( verbs.elements() ) + "^-" );
		}
		
		ic.sendLine();
	}
	
	protected final void defaultInspect( Player p, StringTokenizer args, InteractiveConnection ic, Flags flags, Atom item )
	{
		standardInspect( p, args, ic, flags, (Thing) item );
	}
	
	public boolean isMethodSpecial( Method m )
	{
		Class dc = m.getDeclaringClass();
		
		return( dc != Material.class &&
				!Modifier.isFinal( m.getModifiers() ) &&
				!Modifier.isAbstract( m.getModifiers() )
			  );
	}
	
	public void get( Player p, StringTokenizer args, InteractiveConnection ic, Flags flags, Atom item )
	{
		defaultGet( p, args, ic, flags, item );
	}
	
	protected final void defaultGet( Player p, StringTokenizer args, InteractiveConnection ic, Flags flags, Atom item )
	{
		Room r = p.getLocation();
		Inventory i = p.getInventory();
		
			//  ensure that we won't have to back out
		r.checkPermissionList( Container.removeFromAction );
		i.checkPermissionList( Container.addToAction );
		checkPermissionList( Atom.moveAction );
		
		if( !i.canAdd() )
		{
			ic.sendFailure( "You are carrying too much already." );
			return;
		}
		
		try
		{
			r.remove( this );
			i.add( this );
			ic.sendFeedback( "You pick up the " + getFullPortrait( p ) + "." );
		}
		catch( Exception e )
		{
			throw new UnexpectedResult( e );
		}
	}
	
	public void give( Player p, StringTokenizer args, InteractiveConnection ic, Flags flags, Atom item )
	{
		defaultGive( p, args, ic, flags, item );
	}
	
	protected final void defaultGive( Player p, StringTokenizer args, InteractiveConnection ic, Flags flags, Atom item )
	{
		if( !args.hasMoreTokens() )
		{
			ic.sendFailure( "Who do you want to give the " + getFullPortrait( p ) + " to?" );
			return;
		}
		
		String pname = args.nextToken();
		Player t = null;
		
		try
		{
			t = (Player) Command.getOnlinePlayer( p, ic, pname );
		}
		catch( ClassCastException e )
		{
			ic.sendFailure( "You may only give to single players" );
			return;
		}
		
		if( t == null )
		{
			ic.sendFailure( "No such player '" + pname + "'" );
			return;
		}
		
		Inventory i = p.getInventory();
		Inventory ti = t.getInventory();
		
			//  ensure that we won't have to back out
		i.checkPermissionList( Container.removeFromAction );
		ti.checkPermissionList( Container.addToAction );
		checkPermissionList( Atom.moveAction );
		
		if( !ti.canAdd() )
		{
			ic.sendFailure( p.HeShe() + " is carrying too much already." );
			return;
		}
		
		try
		{
			i.remove( this );
			ti.add( this );
			ic.sendFeedback( "You give the " + getFullPortrait( p ) + " to " + t.getName() + "." );
			if( t.connected() )
			{
				t.send( p.getName() + " gives you a " + getFullPortrait( p ) + "." );
			}
		}
		catch( Exception e )
		{
			throw new UnexpectedResult( e );
		}
	}
	
	public void drop( Player p, StringTokenizer args, InteractiveConnection ic, Flags flags, Atom item )
	{
		defaultDrop( p, args, ic, flags, item );
	}
	
	protected final void defaultDrop( Player p, StringTokenizer args, InteractiveConnection ic, Flags flags, Atom item )
	{
		Room r = p.getLocation();
		Inventory i = p.getInventory();
		
			//  ensure we won't have to back out
		r.checkPermissionList( Container.addToAction );
		i.checkPermissionList( Container.removeFromAction );
		checkPermissionList( Atom.moveAction );
		
		if( !r.canAdd() )
		{
			ic.sendFailure( "There is too much in this room to drop any more." );
			return;
		}
		
		try
		{
			i.remove( this );
			r.add( this );
			ic.sendFeedback( "You put down " + getFullPortrait( p ) + "." );
		}
		catch( CannotDropWhileWearingException e )
		{
			ic.sendFailure( "You cannot drop " + getFullPortrait( p ) + " while you are wearing it." );
		}
		catch( Exception e )
		{
			throw new UnexpectedResult( e );
		}
	}
	
		//  META: eventually do a default method for this
	public void sit( Player p, StringTokenizer args, InteractiveConnection ic, Flags flags, Atom item )
	{
		ic.send( "You cannot sit on the " + getFullPortrait( p ) + "." );
	}
	
		//  META: eventually do a default method for this
	public void stand( Player p, StringTokenizer args, InteractiveConnection ic, Flags flags, Atom item )
	{
		ic.send( "You are not sitting on the " + getFullPortrait( p ) + "." );
	}
	
	public void drink( Player p, StringTokenizer args, InteractiveConnection ic, Flags flags, Atom item )
	{
		ic.send( "You cannot drink the " + getFullPortrait( p ) + "." );
	}
	
	public void eat( Player p, StringTokenizer args, InteractiveConnection ic, Flags flags, Atom item )
	{
		ic.send( "The " + getFullPortrait( p ) + " is not edible." );
	}
	
	public void open( Player p, StringTokenizer args, InteractiveConnection ic, Flags flags, Atom item )
	{
		ic.send( "You cannot open the " + getFullPortrait( p ) + "." );
	}
	
	public void close( Player p, StringTokenizer args, InteractiveConnection ic, Flags flags, Atom item )
	{
		ic.send( "You cannot close the " + getFullPortrait( p ) + "." );
	}
	
	public void fill( Player p, StringTokenizer args, InteractiveConnection ic, Flags flags, Atom item )
	{
		ic.send( "You cannot fill the " + getFullPortrait( p ) + "." );
	}
	
	public void lock( Player p, StringTokenizer args, InteractiveConnection ic, Flags flags, Atom item )
	{
		ic.send( "You cannot lock the " + getFullPortrait( p ) + "." );
	}
	
	public void unlock( Player p, StringTokenizer args, InteractiveConnection ic, Flags flags, Atom item )
	{
		ic.send( "You cannot unlock the " + getFullPortrait( p ) + "." );
	}
	
//---  proxy methods  --------------------------------
	public String getFullPortrait( Player p, Atom proxy )
		{	return( getFullPortrait( p ) );	}
	
	/*
	public final void use( Player p, StringTokenizer args, InteractiveConnection ic, Flags flags )
		{	use( p, args, ic, flags, this );	}
	public final void wear( Player p, StringTokenizer args, InteractiveConnection ic, Flags flags )
		{	wear( p, args, ic, flags, this );	}
	public final void remove( Player p, StringTokenizer args, InteractiveConnection ic, Flags flags )
		{	remove( p, args, ic, flags, this );	}
	public final void wield( Player p, StringTokenizer args, InteractiveConnection ic, Flags flags )
		{	wield( p, args, ic, flags, this );	}
	public final void read( Player p, StringTokenizer args, InteractiveConnection ic, Flags flags )
		{	read( p, args, ic, flags, this );	}
	public final void look( Player p, StringTokenizer args, InteractiveConnection ic, Flags flags )
		{	look( p, args, ic, flags, this );	}
	public final void get( Player p, StringTokenizer args, InteractiveConnection ic, Flags flags )
		{	get( p, args, ic, flags, this );	}
	public final void drop( Player p, StringTokenizer args, InteractiveConnection ic, Flags flags )
		{	drop( p, args, ic, flags, this );	}
	public final void sit( Player p, StringTokenizer args, InteractiveConnection ic, Flags flags )
		{	sit( p, args, ic, flags, this );	}
	public final void stand( Player p, StringTokenizer args, InteractiveConnection ic, Flags flags )
		{	stand( p, args, ic, flags, this );	}
	public final void drink( Player p, StringTokenizer args, InteractiveConnection ic, Flags flags )
		{	drink( p, args, ic, flags, this );	}
	public final void eat( Player p, StringTokenizer args, InteractiveConnection ic, Flags flags )
		{	eat( p, args, ic, flags, this );	}
	public final void open( Player p, StringTokenizer args, InteractiveConnection ic, Flags flags )
		{	open( p, args, ic, flags, this );	}
	public final void close( Player p, StringTokenizer args, InteractiveConnection ic, Flags flags )
		{	close( p, args, ic, flags, this );	}
	public final void fill( Player p, StringTokenizer args, InteractiveConnection ic, Flags flags )
		{	fill( p, args, ic, flags, this );	}
	public final void lock( Player p, StringTokenizer args, InteractiveConnection ic, Flags flags )
		{	lock( p, args, ic, flags, this );	}
	public final void unlock( Player p, StringTokenizer args, InteractiveConnection ic, Flags flags )
		{	unlock( p, args, ic, flags, this );	}
	public final void give( Player p, StringTokenizer args, InteractiveConnection ic, Flags flags )
		{ give( p, args, ic, flags, this ); }
	*/
	
	/**
	  *  This proxy method should be a fraction more expressive.
	  *  In particular, it should prefix the inspect screen by
	  *  some information detailing that this inspect is proxied,
	  *  and isn't completely representative of the object.
	  *
	  *  MaterialContainer, the primary user of the proxy interface,
	  *  by default outputs an inspect for the container, followed
	  *  by the proxy inspect, thus providing complete information
	  *  about the object.
	public void inspect( Player p, StringTokenizer args, InteractiveConnection ic, Flags flags )
		{	inspect( p, args, ic, flags, this );	}

	 */

//---  static section  -------------------------------------------------
//
//  contains some static constants that are useful for the manipulation
//  of objects.
//
	protected static transient Hashtable methods = new Hashtable();
	protected static transient String available_verbs;
	protected static transient String defaultVerb;
	protected static transient Method defaultMethod;
	
	public static final Class[] verbParameters =
	{
		Player.class,
		StringTokenizer.class,
		InteractiveConnection.class,
		Flags.class,
		Atom.class
	};
	
	public static boolean parametersEquivalent( Class[] a, Class[] b )
	{
		if( a.length != b.length )
			return( false );
		
		for( int j = 0; j < a.length; j++ )
		{
			if( a[j] != b[j] )
				return( false );
		}
		
		return( true );
	}
	
		//  place the methods from Thing into the hashtable
	static
	{
		Vector v = new Vector( 5, 5 );
		
		try
		{
			Method m[] = Thing.class.getDeclaredMethods();
			
			for( int i = 0; i < m.length; i++ )
			{
				Class[] parameters = m[i].getParameterTypes();

				if( parametersEquivalent( parameters, verbParameters ) )
				{
					String name = m[i].getName();
					Method meth = m[i];
					
					methods.put( name, meth );
					v.addElement( name );
					
					if( defaultMethod == null )
					{
						defaultMethod = meth;
						defaultVerb = name;
					}
				}
			}
			
			available_verbs = Grammar.commaSeperate( v.elements() );
		}
		catch( Exception e )
		{
			Log.debug( Material.class, "Could not initialise fields from Thing: " + e.toString() );
			e.printStackTrace();
		}
	}
	
	public static final String getDefaultVerb()
	{
		return( defaultVerb );
	}
	
	public static final Method getMethod( String verb )
	{
		return( (Method) methods.get( verb ) );
	}

	public static final String getAvailableVerbs()
	{
		return( available_verbs );
	}
}