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.commands;

import key.*;
import java.io.*;
import java.util.StringTokenizer;

/**
  *  This is a wonderful command that can be instantiated and used
  *  for all sorts of 'constraints' when allowing users to set
  *  things.
  *
  *  Basically, if one of the required properties is null, the
  *  user is prompted for it.  Here are the properties (in the
  *  order the user needs to type them, for your usage displays)
  *
  *  <UL>
  *   <LI> on - if not set, the user must put the name of the
  *             atom to be used here.
  *   <LI> set - the property to be set on that atom - must be
  *              put here, if not set in the command.
  *  </UL>
  *
  *  Followed by the string value to set, if the maximum length
  *  (set on the command) isn't 0.  If the length is zero, the
  *  property will simply be blanked.  (set to "")
 */
public class ConstrainedSet extends Set
{
	private static final long serialVersionUID = 3128665239895446113L;
	
	public static final AtomicElement[] ELEMENTS =
	{
		AtomicElement.construct( ConstrainedSet.class, String.class, "blankFeedback",
			AtomicElement.PUBLIC_FIELD,
			"the message sent when the command is executed without arguments" ),
		AtomicElement.construct( ConstrainedSet.class, String.class, "filledFeedback",
			AtomicElement.PUBLIC_FIELD,
			"the message sent after the command is executed with arguments" ),
		AtomicElement.construct( ConstrainedSet.class, String.class, "setProperty",
			AtomicElement.PUBLIC_FIELD,
			"the constraint; the property to be set" ),
		AtomicElement.construct( ConstrainedSet.class, String.class, "displayProperty",
			AtomicElement.PUBLIC_FIELD,
			"the property used to display the value, if it is different to the setProperty" ),
		AtomicElement.construct( ConstrainedSet.class, Integer.TYPE, "maxLength",
			AtomicElement.PUBLIC_FIELD,
			"another constraint; the maximum length of the property" ),
		AtomicElement.construct( ConstrainedSet.class, Atom.class, "in",
			AtomicElement.PUBLIC_FIELD,
			"another constraint; the container the atom is in" ),
		AtomicElement.construct( ConstrainedSet.class, String.class, "on",
			AtomicElement.PUBLIC_FIELD,
			"another constraint; the atom to set the property on" ),
		AtomicElement.construct( ConstrainedSet.class, String.class, "mustContain",
			AtomicElement.PUBLIC_FIELD,
			"the set value must contain this string" ),
		AtomicElement.construct( ConstrainedSet.class, String.class, "subVerify",
			AtomicElement.PUBLIC_FIELD,
			"if true, prevents carats from appearing before the percent substitute code" )
	};
	
	public static final AtomicStructure STRUCTURE = new AtomicStructure( Set.STRUCTURE, ELEMENTS );
	
	public static final char valueCode = 'v';
	public static final char targetCode = 't';
	
	public static final int DEFAULT_MAX_LENGTH = 60;
	
	public String blankFeedback = "The current value for this room is '%v'";
	public String filledFeedback = "The value now reads: %v";
	public String setProperty = "portrait";
	public String mustContain = "";
	public String displayProperty = "fullPortrait";
	public int maxLength = DEFAULT_MAX_LENGTH;
	public boolean subVerify = false;
	public Reference in = Key.shortcuts().getThis();
	
	/**
	  *  use a string here because we can't have a relative
	  *  reference to a transient atom - which is precisely what
	  *  we want to achieve.
	 */
	public String on = "here";
	
	public ConstrainedSet()
	{
		setKey( "consset" );	//  a default
		usage = "<new message>";
	}
	
	public AtomicStructure getDeclaredStructure()
	{
		return( STRUCTURE );
	}
	
	public void run( Player p, StringTokenizer args, String fullLine, CategoryCommand caller, InteractiveConnection ic, Flags flags ) throws IOException
	{
		Container cin = null;
		
		try
		{
			cin = (Container) in.get();
		}
		catch( OutOfDateReferenceException e )
		{
			in = Reference.EMPTY;
			Log.error( "somebody set " + getId() + ".in wrong (disabled)", e );
			disable();
		}
		catch( ClassCastException e )
		{
			in = Reference.EMPTY;
			Log.error( "somebody set " + getId() + ".in wrong (disabled)", e );
			disable();
		}
		
		Atom r;
		
		if( cin == null )
			cin = Key.instance();
		
		String on = this.on;
		
			//  read the symbol from the arguments
		if( on == null )
		{
			if( args.hasMoreTokens() )
				on = args.nextToken();
			else
			{
					//  assuming they've set up the usage properly ;)
				usage( ic );
				return;
			}
		}
		
		r = (Atom) new Search( on, cin ).result;
		
		if( r == null )
		{
			ic.sendFailure( "Could not find '" + on + "' in " + cin.getId() );
			return;
		}
		
		p.putCode( targetCode, r.getName() );
		
		String pToSet = setProperty;
		
		if( pToSet == null )
		{
			if( args.hasMoreTokens() )
			{
				pToSet = args.nextToken();
			}
			else
			{
				usage( ic );
				return;
			}
		}
		
		if( maxLength > 0 )
		{
			if( !args.hasMoreTokens() )
			{
				usage( ic );
				{
					Object o = r.getProperty( displayProperty );
					if( o == null )
						o = "";
					
					p.putCode( valueCode, o.toString() );
				}
				ic.sendFeedback( Grammar.substitute( blankFeedback, p.getCodes() ) );
				return;
			}
			
			String np = args.nextToken( "" );
			
			if( maxLength < np.length() )
			{
				ic.sendError( "That is too long.  Please use at most " + maxLength + " character" + ((maxLength!=1) ? "s." : "." ) );
				return;
			}
			
			if( mustContain != null && mustContain.length() > 0 )
			{
				if( np.indexOf( mustContain ) == -1 )
				{
					ic.sendError( "The string must contain, at least '" + mustContain + "'." );
					return;
				}
			}

			if( subVerify )
			{
				if( np.indexOf( "^%" ) != -1 )
				{
					ic.sendError( "You may not place a carat (^^) before the substitute code (%)" );
					return;
				}
			}
			
			String fl = "." + pToSet + " " + np;
			
			Atom o = p.getContext();
			p.setContext( r );
			super.run( p, new StringTokenizer( fl ), fl, caller, ic, flags );
			p.setContext( o );
			
			{
				Object ob = r.getProperty( displayProperty );
				if( ob == null )
					ob = "";
				
				p.putCode( valueCode, ob.toString() );
			}
			
			ic.send( Grammar.substitute( filledFeedback, p.getCodes() ) );
		}
		else
		{
				//  as the length is 0 (or less, if they're stupid,
				//  we're just a blank command, not a set at all.
			r.setProperty( pToSet, "" );
			
			ic.send( Grammar.substitute( filledFeedback, p.getCodes() ) );
		}
	}
	
	protected void sendOkay( InteractiveConnection ic )
	{
	}
}