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 java.net.Socket;
import java.net.InetAddress;
import java.io.*;
import java.util.Hashtable;

/**
  *  all output from the program is classified
  *  into one of the 'types' of data that is
  *  being fed back.  This not only allows a
  *  great flexibility in the output method
  *  used on a telnet connection, but it allows
  *  for the easy expansion of connections to
  *  support applet clients or other frontends
  *  that require code support.
  *
  *  This class supports several 'codes' within the
  *  strings you pass to output to the program.
  *
  *  <ul>
  *  <li><i>\n</i>:    To mean 'start a new line'
  *  </ul>
 */
public abstract class InteractiveConnection
extends AnimatedAtom
{
	private transient Player player;
	private transient Interactive interacting = null;
	
	/**
	  *  The constructor must take a socket as its sole
	  *  argument
	  *
	  * @param s The socket that the connection is on
	 */
	public InteractiveConnection()
	{
		setKey( "IC" );
	}
	
	public void interactWith( Interactive i )
	{
		interacting = i;
	}

	public void run()
	{
		try
		{
			while( interacting != null && isAlive() && isConnected() )
			{
				interacting.run( this );
			}
		}
		catch( Throwable e )
		{
			if( isConnected() )
			{
				try
				{
					send( "Sorry, an error occurred.  Closing connection.\n" );
				}
				catch( NetworkException ne )
				{
				}
				
				Log.error( e );
			}
		}
		finally
		{
			close();
		}
	}
	
	private void readObject( ObjectInputStream ois ) throws IOException
	{
		throw new UnexpectedResult( "attempting to de-serialise InteractiveConnection" );
	}
	
	private void writeObject( ObjectOutputStream ois ) throws IOException
	{
		throw new UnexpectedResult( "attempting to serialise InteractiveConnection" );
	}
	
	public String getName()
	{
		if( player != null )
			return( super.getName() + " for " + player.getName() );
		else
			return( super.getName() );
	}
	
	public final void startUnIdling( Player p )
	{
		player = p;
		p.unIdle();
	}
	
	void stopBeingTemporary()
	{
		//  do nothing, we always want to be temporary
	}
	
	public abstract boolean isConnected();
	public abstract boolean newbiesAllowed();
	public abstract void printStackTrace( Throwable t );
	public abstract String getFullSiteName();
	public abstract String getSiteName();
	
	public void flush()
	{
	}
	
	/**
	  *  The intention is to provide a method that only calls flush()
	  *  on the stream if the socket is waiting for input.
	 */
	public void flushIfWaiting()
	{
		flush();
	}
	
	/**
	  *  Throw away all incoming input.
	 */
	public abstract void discard() throws IOException;
	
	public final void stopUnIdling()
	{
		player = null;
	}
	
	public Player getPlayer()
	{
		return( player );
	}
	
	/**
	  *  Specifies that this ic is in 'verbose'
	  *  mode - that is, it will recieve newbie
	  *  help instrctions and so on.
	 */
	public boolean verbose()
	{
		return( true );
	}
	
	/**
	  *  The sub-classes should call this routine everytime
	  *  the player puts some input into the program
	 */
	protected final void unIdle()
	{
		if( player != null )
			player.unIdle();
	}
	
	public synchronized void close()
	{
		interactWith( null );
	}
	
	/**
	  *  draw a line across the screen
	 */
	public abstract void sendLine();
	
	/**
	  *  beeps the terminal, if supported.
	 */
	public void beep()
	{
	}
	
	/**
	  * This function should block until it can
	  * return a complete string of the player's
	  * input.  It can then be used in sequential
	  * code without complications with loops.
	  *
	  * @param prompt The prompt that the player sees (eg "-> ")
	  * @return the string that the player entered.  This string is not \n terminated
	 */
	public abstract String input( String prompt );
	
	/**
	  * This function is very similar to
	  * input(), above, except that it should be
	  * used to *not* echo text as it's entered,
	  * mainly for passwords
	 */
	public abstract String hiddenInput( String prompt );
	
	/**
	  *  leave some blank space (usually the height of the characters
	 */
	public abstract void blankLine();
	
	public abstract void sendSubliminal( String message, int duration, int frequency );
	
	public abstract void sendRaw( String message );
	public abstract void send( String message );
	public abstract void send( char qualifier, String message );
	public abstract void send( Paragraph para );
	public abstract void send( Paragraph para, boolean okayToPage );
	
	public final void sendSystem( String message )
	{
		send( message );
	}
	
	public void blankLines( int c )
	{
		while( c-- > 0 )
			blankLine();
	}
	
	/**
	  * This function is used to output direct,
	  * *successful* feedback of a players command.
	  * It is important that this feedback is normal
	  * and expected by the player, since it generally
	  * won't be hilited in any way.  Please try
	  * to restrict this output to a single line.
	  * <p>
	  * this command does start a new line
	  * <p>
	  * Examples:
	  * <p>
	  * You tell name 'hi' <br>
	  *
	  * @param message the text to be displayed
	 */
	public void sendFeedback( String message )
	{
		send( message );
	}
	
	/**
	  * This function is used when the user makes some
	  * sort of error in typing - *not* if a command
	  * can't be executed because of a failure.  So,
	  * Command not found would go here, but 'Not enough
	  * privs to nuke snapper' wouldn't.
	  * <p>
	  * this command does start a new line
	 */
	public void sendError( String message )
	{
		send( message );
	}
	
	/** META: remove */
	public void sendFailure( String message )
	{
		send( message );
	}
	
	public void send( Screen s )
	{
		send( s.text );
	}
	
		/**  returns true if we need to output a 'paging' prompt */
	public abstract boolean isPaging();
}