package relational.email;

import relational.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.dnd.*;
import java.awt.datatransfer.*;
import java.lang.*;
import java.lang.reflect.*;
import java.io.*;
import java.util.*;
import javax.swing.*;
import javax.swing.event.*;

/**
 * The user-interface element that captures single keystrokes to enable
 * the user to quickly navigate a panel of commands. Inspired by the
 * fact that I never pull-down menus when I use Pine.
 * @classConcise true
 */
public class CommandPanel
	extends JPanel
	implements ActionListener, ChangeListener {

	//////////////////////////////////////////////////////////////
	// State machine definition
	//////////////////////////////////////////////////////////////

	abstract class CommandState extends State {
		public void displayError(String s) {
			error(s);	// pass error out to CommandPanel
		}

		public void freeTextEntered(String text) {
			error("Free text in weird state.");
			requestFocus();	// focus shouldn't be on that field right now!
		}
	}

	class IdleState extends CommandState {
		public IdleState() {}
			// need public constructors because we're instantiated through
			// java.lang.reflect.Constructor.
		public void enterState() {
			// ask for the focus, to snatch it away from the free text field
			requestFocus();
			// reset temporary state
			newConnective = null;
			newQueryColumn = null;
		}
		public void keyTyped(KeyEvent e) {
			// only way to get out of idle state is the special
			// escape key ';'
		}
	}
	class BooleanState extends CommandState {
		public BooleanState() {}
		public void keyTyped(KeyEvent e) {
			if (Character.toUpperCase(e.getKeyChar())=='N') {
				newConnective = new WhereAnd();
				goToState(ColumnState.class);
			} else if (Character.toUpperCase(e.getKeyChar())=='B') {
				newConnective = new WhereOr();
				goToState(ColumnState.class);
			} else if (Character.toUpperCase(e.getKeyChar())=='A') {
				setQuery(Where.always());
				goToState(IdleState.class);
			} else if (Character.toUpperCase(e.getKeyChar())=='F') {
				if (currentQuery instanceof WhereNot) {
					// strip off the top Not
					setQuery((Where) currentQuery.getChild(0));
					goToState(IdleState.class);
				} else {
					setQuery(new WhereNot(currentQuery));
					goToState(IdleState.class);
				}
			} else {
				errorNotDefined(e);
			}
		}
	}
	class ColumnState extends CommandState {
		public ColumnState() {}
		public void keyTyped(KeyEvent e) {
			if (Character.toUpperCase(e.getKeyChar())=='S') {
				newQueryColumn = "Subject";
				goToState(ValueState.class);
			} else if (Character.toUpperCase(e.getKeyChar())=='F') {
				newQueryColumn = "From";
				goToState(ValueState.class);
			} else if (Character.toUpperCase(e.getKeyChar())=='A') {
				newQueryColumn = "Body";
				goToState(ValueState.class);
			} else {
				errorNotDefined(e);
			}
		}
	}
	class ValueState extends CommandState {
		public ValueState() {}
		public void enterState() {
			freeTextField.requestFocus();
		}
		public void freeTextEntered(String value) {
			// construct the query and send it
			Where newQuery = new WhereIn(
				Message.f_primaryKey,
				new Select(Header.f_msg,
					Where.and(
						new WhereEquals(Header.f_name, newQueryColumn),
						new WhereLike(Header.f_value, value)
					)
				)
			);
			if (newConnective == null) {
				// this is replacing a WhereAlways
				setQuery(newQuery);
			} else {
				newConnective.setChild(0, currentQuery);
				newConnective.setChild(1, newQuery);
				setQuery(newConnective);
			}
			goToState(IdleState.class);
		}
		public void keyTyped(KeyEvent e) {
			errorNotDefined(e);
		}
	}

	///////////////////////////////////////////////////////////////////////
	///////////////////////////////////////////////////////////////////////

	// when we have the focus, pass keystrokes to the current state object.
	class MyKeyListener
		extends KeyAdapter {
		public void keyTyped(KeyEvent e) {
			currentState.keyTyped(e);
		}
	}

	///////////////////////////////////////////////////////////////////////
	///////////////////////////////////////////////////////////////////////

	Hashtable stateCache;
	Where currentQuery;
	Where newConnective;	// new query connective being constructed
	String newQueryColumn;
	CommandState currentState;

	protected CommandState getState(Class clazz) {
		if (stateCache.get(clazz)==null) {
			try {
				Constructor ctor =
					clazz.getConstructor(new Class[] { CommandPanel.class });
				Object ob = ctor.newInstance(new Object[] { this });
				stateCache.put(clazz, ob);
			} catch (Exception ex) {
				error(ex.toString());
			}
		}
		return (CommandState) stateCache.get(clazz);
	}
	protected void goToState(Class clazz) {
		currentState = getState(clazz);
		System.err.println("currentState: "+currentState);
		currentState.enterState();
			// give state a chance to do any upon-entry setup
	}
	protected void focusLost() {
		currentQuery = null;
		goToState(IdleState.class);
	}
	String whoHasFocus() {
		Component c = this;
		while (!(c instanceof Window)) {
			c = c.getParent();
		}
		System.err.println("isVisible: "+isVisible());
		System.err.println("isFocusTraversable: "+isFocusTraversable());
		System.err.println("isEnabled: "+isEnabled());
		return
			c+" says "+
			(c.hasFocus()
				? ((Window)c).getFocusOwner().toString()
				: "(window doesn't have focus) ");
	}
	public boolean isFocusTraversable() {
		return true;
	}
	protected void escapeKey() {
		currentQuery = wherePanel.getQuery();
		requestFocus();
		if (currentQuery instanceof WhereAlways) {
			goToState(ColumnState.class);
		} else {
			goToState(BooleanState.class);
		}
	}
	protected void setQuery(Where query) {
		wherePanel.setQuery(query);
	}
	protected void error(String s) {
		System.err.println(s);
	}
	protected void errorNotDefined(KeyEvent e) {
		error("not defined: "+e);
	}

	ChangeListener clistener;

	JTextField freeTextField;
	WherePanel wherePanel;
	MyKeyListener keyListener;

	public CommandPanel(WherePanel wp) {
		setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));

		freeTextField = new JTextField(40);
		freeTextField.addActionListener(this);
		add(freeTextField);

		this.wherePanel = wp;
		wherePanel.addChangeListener(this);

		stateCache = new Hashtable();

		keyListener = new MyKeyListener();
		addKeyListener(keyListener);
	}

	public void actionPerformed(ActionEvent e) {
		if (e.getActionCommand().equals("select")) {
			escapeKey();
		}
		if (e.getSource() == freeTextField) {
			currentState.freeTextEntered(e.getActionCommand());
		}
	}

	public void stateChanged(ChangeEvent e) {
		// System.err.println("stateChanged: "+e);
	}
}
