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.border.*;
import javax.swing.event.*;
import javax.swing.text.*;

/**
 * A graphical menu that can be quickly navigated with keystrokes.
 */
public class MenuPanel
	extends JPanel
	implements ActionListener, ChangeListener {

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

	protected void escapeKey() {
		// fire up the Select menu
		menuSelect.doClick();
	}
	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;
	JLabel freeTextLabel;
	JMenu menuSelect;
	WherePanel wherePanel;
	Runnable pendingCommand;

	class SelectAllAction extends AbstractAction {
		public SelectAllAction() { super("All"); }
		public void actionPerformed(ActionEvent e) {
			setQuery(Where.always());
		}
	}

	class SelectFlipAction extends AbstractAction {
		public SelectFlipAction() { super("Flip"); }
		public void actionPerformed(ActionEvent e) {
			Where currentQuery = wherePanel.getQuery();
			if (currentQuery instanceof WhereNot) {
				// strip off existing Not
				setQuery((Where) currentQuery.getChild(0));
			} else {
				setQuery(new WhereNot(currentQuery));
			}
		}
	}

	class SelectColumnAction
		extends AbstractAction
		implements Runnable {
		Where joiner;
		public SelectColumnAction(String column, Where joiner) {
			super(column);
			this.joiner = joiner;
		}
		public void actionPerformed(ActionEvent e) {
			// pass a continuation to the free-text box to set the query
			// when the free-text is entered.
			requestFreeText((String) getValue(Action.NAME), this);
		}
		public void run() {
			Where currentQuery = wherePanel.getQuery();
			Where newQuery = new WhereIn(
				Message.f_primaryKey,
				new Select(Header.f_msg,
					Where.and(
						new WhereEquals(Header.f_name,
							(String) getValue(Action.NAME)),
						new WhereLike(Header.f_value,
							freeTextField.getText())
					)
				)
			);
			Where compositeQuery = null;
			if (joiner==null) {
				compositeQuery = newQuery;
			} else {
				try {
					compositeQuery = (Where) joiner.getClass().newInstance();
				} catch (IllegalAccessException ex) {
					throw new RuntimeException(ex.toString());
				} catch (InstantiationException ex) {
					throw new RuntimeException(ex.toString());
				}
				compositeQuery.setChild(0, currentQuery);
				compositeQuery.setChild(1, newQuery);
			}
			setQuery(compositeQuery);
		}
	}

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

		JMenuBar menuBar = new JMenuBar();
		menuBar.setMinimumSize(new Dimension(100,30));
		menuBar.setBorder(new BevelBorder(BevelBorder.RAISED));
		add(menuBar);

		menuSelect = new HandyJMenu("Select");
		menuSelect.add(new SelectAllAction());
		menuSelect.add(new SelectFlipAction());
		menuSelect.addSeparator();
		addSubmenus(menuSelect, null);
		menuSelect.addSeparator();

		JMenu menuBroaden = new JMenu("Broaden");
		addSubmenus(menuBroaden, new WhereOr());
		menuSelect.add(menuBroaden);

		JMenu menuNarrow = new JMenu("Narrow");
		addSubmenus(menuNarrow, new WhereAnd());
		menuSelect.add(menuNarrow);

		// menuSelect.setMnemonic(';');
		menuBar.add(menuSelect);

		JMenu menuAct = new HandyJMenu("Act");
		menuAct.add(new ComposeAction(false));
		menuAct.add(new ComposeAction(true));
		menuBar.add(menuAct);

		freeTextLabel = new JLabel();
		// freeTextLabel.setMinimumSize(new Dimension(100,0));
			// yeah, that *really* works as advertised! Not.
		add(freeTextLabel);
		freeTextField = new JTextField(40);
		freeTextField.addActionListener(this);
		add(freeTextField);

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

	static class HandyJMenu
		extends JMenu
		implements MenuKeyListener {
		// Java Gripe #642: when subclassing, you can inherit everything...
		// except the set of constructors. So convenience constructors
		// are convenient to use, but inconvenient to extend.
		public HandyJMenu(String name) {
			super(name);
			addMenuKeyListener(this);
			prefix = new String();
		}
		String prefix;
		public void menuKeyPressed(MenuKeyEvent e) {}
		public void menuKeyReleased(MenuKeyEvent e) {}
		public void menuKeyTyped(MenuKeyEvent e) {
			if (e.isConsumed()) {
				return;
			}
			char typed = e.getKeyChar();
			prefix = prefix + String.valueOf(typed);
			JMenu source = (JMenu) e.getSource();
			JMenuItem luckyWinner = null;
			Component[] theComps = source.getMenuComponents();
			for (int i=0; i<theComps.length; i++) {
				if (theComps[i] instanceof JMenuItem) {
					JMenuItem jmi = (JMenuItem) theComps[i];
					String label = jmi.getText();
					if (label.toLowerCase().startsWith(prefix.toLowerCase())) {
						e.consume();
							// we'll either use this event now, or store it
							// for a later match...
						if (luckyWinner == null) {
							luckyWinner = jmi;
						} else {
							// ambiguous match -- wait for more state
							return;
						}
					}
				}
			}
			prefix = new String();
				// if we had a match, we want to reset state; if we had no
				// matches, we still want to reset state, so user can try
				// again. I think.
			if (luckyWinner != null) {
				// Found an unambiguous match in current menu!
				MenuSelectionManager.defaultManager().clearSelectedPath();
				luckyWinner.doClick();
			}
		}
	}

	protected void addSubmenus(JMenu parent, Where w) {
		parent.add(new SelectColumnAction("From", w));
		parent.add(new SelectColumnAction("Subject", w));
		parent.add(new SelectColumnAction("Date", w));
	}

	private static class MnAction
		implements ActionListener {
		JMenuItem mi;
		MnAction(JMenuItem mi) {
			this.mi = mi;
		}
		public void actionPerformed(ActionEvent e) {
			System.out.println("MnAction "+mi.getText());
			// ((JPopupMenu) e.getSource()).setSelected(mi);
			mi.doClick();
			mi.setSelected(false);
		}
	}

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

	public void requestFreeText(String label, Runnable runnable) {
		// Ready the freeTextField, and
		// set up a continuation to be invoked if the freeTextField is
		// filled in and accepted.
		freeTextLabel.setText(label);
		freeTextField.requestFocus();
		pendingCommand = runnable;
		freeTextField.setEnabled(true);
	}

	public void doPendingCommand() {
		// free text has been entered; run the associated command and
		// void the text field.
		if (pendingCommand==null) {
			System.out.println("huh, I should have disabled the box...");
		} else {
			pendingCommand.run();
		}
		try {
			Document ftd = freeTextField.getDocument();
			ftd.remove(0, ftd.getLength());
		} catch (BadLocationException ex) {
			// gasp.
		}
		freeTextField.setEnabled(false);
		freeTextLabel.setText("");
		pendingCommand = null;
		transferFocus();
	}

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