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
**  ---------|------------|-----------------------------------------------
**  01Jun96     adamb        added all the commands in.
**                           the loop for some of them isn't
**                           available yet, but will be.
**
**  02Jun96     paulm        fixed a bug which meant blank
**                           lines were added to the end of
**                           the buffer during the edit
**
**  03Jun96     adamb        merged Paul's update with the 
**                           editor.
**
**  05Jun96     adamb        fixed the stringBuffer to StringTokenizer
**                           code so that blank lines are niether
**                           added nor deleted.
**
**  05Jun96     adamb        added the looping for some commands and
**                           also the elimination of the .g command.
**                           ie: .5 to goto line #5.
**                           Added .s(tats) command.
*/

package key;

import key.util.LinkedList;

import java.util.*;
import java.lang.*;

/**
  *  Kind of hacked to edit paragraphs - doesn't do them
  *  properly, but then, what does?
 */
public class Editor
{
	public static final MultiParagraph intro;
	
	static
	{
		MultiParagraph.Generator i = new MultiParagraph.Generator();
		i.append( LineParagraph.LINE );
		i.append( new TextParagraph( TextParagraph.LEFT, "Entering the forest editor <footnote 10>:", 0, 0, 0, 0 ) );
		i.append( new TextParagraph( TextParagraph.LEFT, "If you're not sure how to use the editor, enter '.quit' immediately (without the quotes), and read 'help editor'", 2, 0, 0, 0 ) );
		i.append( BlankLineParagraph.BLANKLINE );
		intro = i.getParagraph();
	}
	
	static public Paragraph edit( Player p, Paragraph buffer, InteractiveConnection ic, int maxLines, int maxBytes )
	{
		String startBuffer;
		int alignment = TextParagraph.LEFT;
		LinkedList lines = new LinkedList();
		int currentBytes=0;
		
		if( buffer instanceof TextParagraph )
			startBuffer = ((TextParagraph)buffer).getText();
		else
		{
			//ic.sendError( "This editor can only edit single text paragraphs at the moment - previous data cleared" );
			//startBuffer = "";
			ic.sendError( "This editor can only edit single text paragraphs." );
			return( buffer );
		}
		
		//
		// The StringTokenizer when delim return is set to true, returns in 1
		// entry the line and the next contains the delim.
		// If the line is (null) then the line is NOT returned, only the delim.
		// Therefore, if you use the line, you then have to remove the delim.
		// so if you get a delim, it isn't treated as a delim, it's treated as
		// a blank line.
		//
		
		StringTokenizer st = new StringTokenizer( startBuffer, "\n", true );
		if( !st.hasMoreTokens() )
			lines.append( "" );
		while( st.hasMoreTokens() ) 
		{
			String strToken=st.nextToken();
			if(!strToken.startsWith("\n"))
			{
				lines.append(strToken);
				if(st.hasMoreTokens()) 
				{
					strToken=st.nextToken();
				}
			} 
			else 
			{
				lines.append("");				// a CR in the text means a blank line
			}
		}
		
		ic.send( intro, false );
		ic.sendFeedback( "Limitations:  " + maxLines + " lines, " + maxBytes + " characters." );
		ic.sendLine();
		
		String entered=null;
		LinkedList.Iterator cline=lines.iterator();
		cline.last();
		
		// if there isn't a CR at the end, place one there.
		if(!((String)cline.element()).equals("")) 
		{
			cline.insertAfter("");
			cline.last();
		}
		
		display( ic, lines, alignment );
		ic.sendLine();
		
		currentBytes = countBytes( lines );
		
		while( true )
		{
			try
			{
				entered = ic.input( "+ " );
			}
			catch( NetworkException e ) 
			{ 
				System.out.println( "disconnect from editor" ); 
				return( buffer );
			} 
			
			if( entered.startsWith( "." ) ) 
			{
				//
				// first all the singlar commands not affected by numerical repeating 
				//

				if(entered.equals(".quit")) 
				{ 
					//
					// Quit - Abort and Exit 
					//
					ic.sendFeedback( "Aborting edit..." ); 
					return( buffer );
				}
				else if( entered.equals( ".end" ) )
				{
					//
					// End - Save and Exit
					//
					ic.sendFeedback("Saving editor buffer...");
					StringBuffer r = new StringBuffer( "" );
					for( Enumeration e = lines.elements(); e.hasMoreElements(); )
					{
						r.append( (String) e.nextElement() );
						if( e.hasMoreElements() )
							r.append( '\n' );
					}
					
					return( new TextParagraph( alignment, r.toString() ) );
				}
				else if( entered.equalsIgnoreCase( ".centrealign" ) || entered.equalsIgnoreCase( ".centeralign" ) )
				{
					alignment = TextParagraph.CENTERALIGNED;
					ic.sendFeedback( "This paragraph will now be centre aligned" );
				}
				else if( entered.equalsIgnoreCase( ".leftalign" ) )
				{
					alignment = TextParagraph.LEFT;
					ic.sendFeedback( "This paragraph will now be left aligned" );
				}
				else if(entered.startsWith(".l")) 
				{
					//
					// Look - view the current line
					//
					ic.sendRaw((String)cline.element());
				}
				else if( entered.startsWith( ".v" ) )
				{
					//
					// View - view the entire buffer
					//
					display( ic, lines, alignment );
				}
				else if(entered.startsWith(".w")) 
				{
					//
					// Wipe - Erase the entire buffer
					//
					cline.first();
					lines.clear();
					cline.first();
					lines.append( "" );
					cline.last();
					alignment = TextParagraph.LEFT;
					ic.sendFeedback( "Editing Buffer Cleared." );
					currentBytes = 0;
				}
				else if(entered.startsWith(".t")) 
				{
					//
					// Top - Go to the top of the buffer
					//
					cline.first();
					ic.sendFeedback( "At Top of Buffer." );
				}
				else if(entered.startsWith(".b")) 
				{
					//
					// Bottom - Go to the end of the buffer
					//
					cline.last();
					ic.sendFeedback( "At Bottom of Buffer." );
				}
				else if(entered.startsWith(".s"))
				{
					//
					// Stats - Some statistic about your edit buffer
					//
					ic.sendFeedback( "Total Number of lines: " + lines.count() +" (max " + maxLines + "), total bytes: " + currentBytes + " (max " + maxBytes + ")" );
				}
				else if(entered.startsWith(".g")) 
				{
					//
					// Go - Go to a given line No.
					//
					if(entered.length()>2) 
					{
						if(Character.isDigit(entered.charAt(2))) 
						{
							Integer lineNo;
							try 
							{
								lineNo=Integer.valueOf(entered.substring(2));
							}
							catch(java.lang.NumberFormatException e) 
							{ 
								ic.sendError("Invalid Line No: " + e.getMessage()); 
								continue;
							} 
								// Integer lineNo=Integer.valueOf(entered.substring(2));
							if(lines.count()>=lineNo.intValue()) 
							{
								cline.first();
								for(int i=0;i<lineNo.intValue()-1;i++) 
								{
									cline.next();
								}
								ic.sendRaw((String)cline.element());
							} 
							else 
							{
								ic.sendError("Line Number " + lineNo.intValue() + " out of range.  Total Lines in buffer: " + lines.count());
							}
						} 
						else 
						{
							ic.sendError("Usage: .g<n>  ... where <n> is the line no. to go to.");
						}
					} 
					else 
					{
						ic.sendError("Usage: .g<n>  ... where <n> is the line no. to go to.");
					}
				}
				else
				{
					//
					// Now for the numerical looping commands, ie .5d
					//
					int loop;
					String numberString=extractNumber(entered);
					if(numberString.length() > 0)
					{
						Integer intLoop=Integer.valueOf(numberString);
						loop=intLoop.intValue();
						if(numberString.length()+1==entered.length())
						{
							if(lines.count()>=loop) 
							{
								cline.first();
								for(int i=0;i<loop-1;i++) 
								{
									cline.next();
								}
								ic.sendRaw((String)cline.element());
							} 
							else 
							{
								ic.sendError("Line Number " + loop + " out of range.  Total Lines in buffer: " + lines.count());
							}
							loop=0;
						}
					}
					else
					{
						loop=1;
					}
					for(int i=0;i<loop;i++)
					{
						if( entered.endsWith( "+" ) )
						{
							//
							// Go forward 1 line
							//
							if( cline.isLast() )
							{
								ic.sendError( "Already at the end of the buffer - can't go forward" );
							}
							else
							{
								cline.next();
								ic.sendFeedback( (String) cline.element() );
							}
						}
						else if( entered.endsWith( "-" ) )
						{
							//
							// Go back one line
							//
							if( cline.isFirst() )
							{
								ic.sendError( "Already at the start of the buffer - can't go back" );
							}
							else
							{
								cline.previous();
								ic.sendFeedback( (String) cline.element() );
							}
						}
						else if( entered.endsWith( "d" ) || entered.endsWith( "del" ) )
						{
							//
							// delete - Delete the current line
							//
							if( cline.isLast() )
							{
								ic.sendError( "At end of buffer - cannot delete" );
							}
							else
							{
								cline.remove();
								ic.sendFeedback( "Line deleted" );
							}
						}
						else
						{
							//
							// Okay ... command not found/
							//
							ic.sendError("Command: " + entered + " not found.");
							break;
						}
					}
					
					currentBytes = countBytes( lines );
				}
			}
			else
			{
				if( lines.count() > maxLines )
				{
					ic.sendError( "Too many lines, line NOT added." );
				}
				else if( currentBytes + entered.length() > maxBytes )
				{
					ic.sendError( "Too many characters in buffer, line NOT added." );
				}
				else
				{
					cline.insertBefore( entered );
					ic.send( entered );
					currentBytes += entered.length();
				}
			}
		}
	}
	
	static private void display( InteractiveConnection ic, LinkedList lines, int alignment )
	{
		StringBuffer sb = new StringBuffer();
		
		for( Enumeration e = lines.elements(); e.hasMoreElements(); ) 
		{
			String s = (String)e.nextElement();
			if( e.hasMoreElements() ) 
			{
				sb.append( s );
				sb.append( '\n' );
			}
		}
		
		ic.send( new TextParagraph( alignment, sb.toString() ) );
	}
	
	static private int countBytes( LinkedList lines )
	{
		int b = 0;
		
		for( Enumeration e = lines.elements(); e.hasMoreElements(); )
		{
			String s = (String) e.nextElement();
			
			b += s.length();
		}
		
		return( b );
	}
	
	static private String extractNumber(String entered)
	{
		int i;
		
		for( i = 1; i < entered.length(); i++ )
		{
			if( !Character.isDigit(entered.charAt(i)) )
			{
				break;
			}
		}
		
		return( entered.substring( 1, i ) );
	}
}