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
**  ---------|------------|-----------------------------------------------
**  19Aug98     subtle       start of recorded history
**
*/

package key;

import key.util.LinkedList;

import java.util.Enumeration;
import java.util.StringTokenizer;
import java.io.Writer;
import java.io.IOException;

/**
  *  This class defines the basic abstract implementation of
  *  a terminal type.  It's specific to a TelnetConnection,
  *  since the other (custom) terminals will need changes at
  *  a much higher level.
 */
public class Terminal
{
	/**
	  *  The maximum expected colours on a line.
	  * <br>
	  *  If this limit is exceeded, then the ^-
	  *  operator won't work correctly - for
	  *  efficiency reasons we're simply going
	  *  to write over the top part of the stack.
	  * <br>
	  *  If this becomes a problem in the future,
	  *  (all those F&$#*(&#$ gits who set their
	  *  emails "all pretty colours" for instance,
	  *  just increase this stack size.
	 */
	public static final int STACK_SIZE = 15;
	
	/**
	  *  The position that was marked.  If no-where
	  *  has been marked, this will be -1.  If you
	  *  mark, you can't remark something until
	  *  the terminal has been reset (like what
	  *  happens when there's a new paragraph)
	  * <br>
	  *  Its meant to be used in, for example, the
	  *  'say' command.  You don't want the users
	  *  stuffed up colour codes wrecking your
	  *  colouring for the end quote, so right after
	  *  the open quote you mark, then restore mark
	  *  just before the end quote.  This means
	  *  no matter *what* happens, you can always
	  *  get back to where you marked.
	  * <br>
	  *  Don't mark when you're > the stack size.
	  * <br>
	  *  Again - if you run out of stack, bad things *will
	  *  happen*. ;)
	 */
	protected int marked;
	
	/**
	  *  The actual stack variable, holds all the
	  *  previous colour codes.
	 */
	protected char[] stack;
	
	/**
	  *  This always points at the next place to put
	  *  an increasing colour code.  (ie, one past)
	 */
	protected int stackValue; 
	
	/**
	  *  If this boolean is true, then the stream is
	  *  in an uncoloured state (the last code sent
	  *  down reset it completely).
	 */
	protected boolean normal;

	/**
	  *  It is the responsibility of the subclass to
	  *  set this array.
	 */
	protected String names[] = { "none", "dumb" };

	protected Terminal()
	{
		marked = -1;
		stackValue = 1;
		stack = new char[ STACK_SIZE ];
		normal = true;
		
			//  see, here's the thing - the first entry
			//  on the stack is *always* normal, since
			//  without it, we've got no way to restore.
		stack[0] = 'N';
	}
	
	/**
	  *  Should be called at the end of every section
	  *  of input - will reset the stream to its
	  *  proper state
	 */
	public void reset( Writer outputStream ) throws IOException
	{
		stackValue = 1;
		marked = -1;

		if( !normal )
		{
			outputStream.write( stringForCode( 'N' ) );
			//Log.debug( this, "terminal reset" );
			normal = true;
		}
		//else
			//Log.debug( this, "terminal NOT reset (already normal)" );
	}

	public boolean nameMatches( String test )
	{
		for( int i = 0; i < names.length; i++ )
		{
			if( test.equalsIgnoreCase( names[i] ) )
				return true;
		}

		return false;
	}

	public String getName()
	{
		if( names != null && names.length > 0 )
			return( names[0] );
		else
			throw new UnexpectedResult( "names isn't valid in " + Type.typeOf( this ).getName() );
	}
	
	/**
	  *  Does all the necessary coding to support
	  *  the newly returned colour code for input
	  *  into the stream.
	 */
	public String stringForCode( char code )
	{
		String result="";
		
		switch( code )
		{
			case '@':  // set mark if its not already
				if( marked == -1 )
					marked = stackValue;
				
				return( "" );
			
			case '$':
				if( marked != -1 )
					stackValue = marked - 1;
				
					//  restore the previous colour
				return( stringForCode( stack[ stackValue ] ) );
			
			case '-':
				if( stackValue > 1 )
					stackValue-=2;

					//  technically speaking, the stackValue
					//  could here be invalid, but.. I mean..
					//  nothings going to go wrong, right?
					//  *arrogent smile*
				
				return( stringForCode( stack[ stackValue ] ) );
		}
		
		normal = false;
		
		result =  codeLookup( code );
		
			//  push this value onto the stack
		if( stackValue < STACK_SIZE )
			stack[ stackValue++ ] = code;
		else
			stack[ STACK_SIZE-1 ] = code;
		
		return( result );
	}
	
	public String codeLookup( char code )
	{
		switch( code )
		{
			case 'n':
			case 'N':
				normal = true;
		}
	
		return( "" );
	}

	public String clearScreen()
	{
		return( "" );
	}
	
	public String beep()
	{
		return( "" );
	}
	
	protected static LinkedList terminalTypes;

	public static Terminal createTerminal( String terminalName ) throws IllegalAccessException, InstantiationException
	{
		for( Enumeration e = terminalTypes.elements(); e.hasMoreElements(); )
		{
			Terminal t = (Terminal) e.nextElement();
			if( t.nameMatches( terminalName ) )
				return( (Terminal) t.getClass().newInstance() );
				//return( (Terminal) Type.typeOf( t ).newInstance() );
		}

		return null;
	}
	
	static
	{
		terminalTypes = new LinkedList();
		
		terminalTypes.append( new key.terminals.Dumb() );
		terminalTypes.append( new key.terminals.ISO6429() );
	}
}