package talk;

import java.awt.*;
import java.lang.String;

class ChatFrame extends Frame {
	int rowsShown = 10, colsShown = 60;
	TextArea outputText;
	TextField inputField;
	final int outputBufferHigh = 4096 + 2048;
	final int outputBufferLow = 4096;
	Client owner;
	ChatFrame(Client ownerArg) {
		super("Chat");
		owner = ownerArg;
		int size = 10;
		Font font = new Font("Times Roman", Font.PLAIN, size);
		GridBagLayout gridbag = new GridBagLayout();
		setLayout(gridbag);
		GridBagConstraints c = new GridBagConstraints();
		c.fill = GridBagConstraints.BOTH;
		c.gridwidth = GridBagConstraints.REMAINDER;
		c.gridheight = 1;
		c.weightx = 1.0;
		c.weighty = 2.0;
		c.gridx = 0;
		c.gridy = 0;
		outputText = new TextArea(rowsShown, colsShown);
		outputText.setEditable(false);
		outputText.setFont(font);
		gridbag.setConstraints(outputText, c);
		add(outputText);
		c.fill = GridBagConstraints.HORIZONTAL;
		c.gridx = 0;
		c.gridy = 1;
		c.gridheight = 1;
		c.weightx = 1.0;
		c.weighty = 0.0;
		inputField = new TextField();
		inputField.setEditable(true);
		gridbag.setConstraints(inputField, c);
		add(inputField);		
		inputField.requestFocus();
		outputText.setText("");
		inputField.setText("");
		pack();
		show();
	}

	// handleEvent is an important method; all events in
	// this frame come to this method first. We want to
	// catch WINDOW_DESTROY events, which occur when the
	// user tries to close the frame. For all other events,
	// it is very important to call the original version
	// of handleEvent. Otherwise, action() and other event
	// handling methods will never be called! 

	public boolean handleEvent(Event evt) {
		if (evt.id == Event.WINDOW_DESTROY)
		{
			// Tell the applet to get rid of us
			// and hang up.
			owner.disconnect("Not Connected.");
			// Return true; we have completely 
			// processed this event.
			return true;
		}
		// We didn't handle this event here, so call the
		// version of handleEvent in the parent class.
		// In Java, the syntax for this is "super.methodName".
		return super.handleEvent(evt);
	}
	public boolean action(Event evt, Object arg) {
		// Text fields send an action event when
		// the user presses ENTER. In that case, the
		// 'arg' argument is a String object containing
		// the text of the field. Handy, isn't it?

		if (evt.target == (Object) inputField) {
			inputLine((String) arg);
			return true;
		}
		return false;
	}
	void inputLine(String arg)
	{
		// The user has entered a line of text. 
		// Pass it to the owner.
		owner.inputLine(arg);
		// Now clear the text entry field.
		if (inputField != null) {
			inputField.setText("");
		}
	}
	void outputLine(String l) {	
		// Our task here is to break the line of text
		// into separate lines (word wrapping), since
		// the TextArea component cannot word wrap for us.
		// Otherwise users would have to scroll horizontally,
		// which is very unpleasant to do often.

		// Find the length of the line
		int len = l.length();

		// The number of columns we want per line
		int w = colsShown;

		// While the string is too long for one line...
		while (len > w) {
			int sp;
			// Consider the string up to the maximum # of columns
			String sub = l.substring(0, w);
			// And look for the last space in that string
			sp = sub.lastIndexOf(' ');
			if (sp == -1) {
				// If there are no spaces at all,
				// then snap the line in half right
				// at the max # of columns.

				// Insert a line break...
				outputText.appendText("\n");
				// Insert the new line...
				outputText.appendText(sub);
				// And break it off.
				l = l.substring(w);
				// Subtract from the length.
				len -= w;
			} else {
				// There is a space, so break politely
				// at the space.

				// Insert a line break...
				outputText.appendText("\n");
				// Insert the new line...
				String untilSpace = sub.substring(0, sp);
				outputText.appendText(untilSpace);
				// And break it off, *after* the space
				// (thus the + 1).
				l = l.substring(sp + 1);
				// Subtract the length of the new line
				// from the total length...
				len -= sp;
				// And subtract one more for the space,
				// which has been discarded.
				len --;
			}
		}
		// Now that the line is short enough, if there's
		// any left, we can just add it right in.
		if (len > 0) {
			outputText.appendText("\n");
			outputText.appendText(l);
		}
		// Delete any excess scrollback.
		deleteOldScrollback();
		// And scroll to the end.
		scrollOutputToEnd();
	}
	void deleteOldScrollback()
	{
		// Our task here is to remove the oldest
		// text in the output window if there's more
		// than a certain amount of data there.

		// Get the length of the output window
		int len = outputText.getText().length();
		int remove = 0;

		// Get the text in the output window
		String text = outputText.getText();

		// If we have more than outputBufferHigh characters
		// of text, then we want to throw out text until
		// we get below outputBufferLow characters. 
		// This strategy avoids the need to throw out
		// text on every single call.
		
		// If we are above the high-water mark...
		if (len > outputBufferHigh) {
			// While we are above the low-water mark...
			while (len > outputBufferLow) {
				// We want to split cleanly at a line break.
				// So keep removing complete lines of text
				// on each pass, until we are under the
				// low water mark.	
	
				// Find a carriage return...
				int cr = text.indexOf('\n');

				// If there are no carriage returns, which
				// isn't very likely, then just hack off
				// exactly as many bytes as needed to
				// reach the low-water mark. Add the
				// number of bytes removed to 'remove',
				// subtract it from the length, and
				// break out of the loop.
				if (cr == -1) {
					remove += len - outputBufferLow;
					len -= remove;
					break;
				}
				// If there is a carriage return, which there
				// will be, then break off the text after
				// the carriage return. Add the
				// number of characters removed to 'remove',
				// and subtract it from the length.
				remove += (cr + 1);		
				text = text.substring(0, cr + 1);
				len -= (cr + 1);	
			}
		}
		// Now, this is the clever part: instead of actually
		// setting new text for the entire TextArea, we will
		// just replace the first 'remove' bytes in the
		// existing text with an empty string. We do this
		// because it isn't "jumpy" and doesn't annoy the user.
		if (remove > 0) {
			outputText.replaceText("", 0, remove);
		}
	}
	void scrollOutputToEnd() 
	{
		// Our task here is to scroll to the end
		// of the TextArea containing the output 
		// from the server. A little-known fact
		// about java text components: the caret
		// is always at the end of the selection,
		// so by selecting zero characters at the 
		// very end, we scroll to the end.
		int len = outputText.getText().length();		
		outputText.select(len, len);		
	}
}

