package ij.macro;
import ij.*;
import ij.plugin.*;
import ij.plugin.frame.*;
import ij.gui.GUI;
import ij.util.Tools;
import java.awt.*;
import java.awt.event.*;
import java.util.Hashtable;
import java.util.Arrays;
import java.util.Iterator;
import java.util.Set;

/** This class implements the text editor's Macros/Find Functions command.
	It was written by jerome.mutterer at ibmp.fr, and is based on Mark Longair's CommandFinder plugin.
*/
public class FunctionFinder implements TextListener,  WindowListener, KeyListener, ItemListener, ActionListener {
	private static String url = "http://wsr.imagej.net/developer/macro/functions.html";
	private static Dialog dialog;
	private TextField prompt;
	private List functions;
	private Button insertButton, infoButton, closeButton;
	private String [] commands;
	private Editor editor;

	public FunctionFinder(Editor editor) {

		this.editor = editor;
		String f = Tools.openFromIJJarAsString("/functions.html");
		if (f==null) {
			IJ.error("\"functions.html\" not found in ij.jar");
			return;
		}
		f = f.replaceAll("&quot;", "\"");
		String [] l = f.split("\n");
		commands= new String [l.length];
		int c=0;
		for (int i=0; i<l.length; i++) {
			String line = l[i];
			if (line.startsWith("<b>")) {
				commands[c]=line.substring(line.indexOf("<b>")+3,line.indexOf("</b>"));
				c++;
			}
		}
		if (c==0) {
			IJ.error("ImageJ/macros/functions.html is corrupted");
			return;
		}

		ImageJ imageJ = IJ.getInstance();
		if (dialog==null) {
			dialog = new Dialog(imageJ, "Built-in Functions");
			dialog.setLayout(new BorderLayout());
			dialog.addWindowListener(this);
			Panel northPanel = new Panel();
			prompt = new TextField("", 32);
			prompt.addTextListener(this);
			prompt.addKeyListener(this);
			northPanel.add(prompt);
			dialog.add(northPanel, BorderLayout.NORTH);
			functions = new List(12);
			functions.addKeyListener(this);
			populateList("");
			dialog.add(functions, BorderLayout.CENTER);
			Panel buttonPanel = new Panel();
			insertButton = new Button("Insert");
			insertButton.addActionListener(this);
			buttonPanel.add(insertButton);
			infoButton = new Button("Info");
			infoButton.addActionListener(this);
			buttonPanel.add(infoButton);
			closeButton = new Button("Close");
			closeButton.addActionListener(this);
			buttonPanel.add(closeButton);
			dialog.add(buttonPanel, BorderLayout.SOUTH);
			GUI.scale(dialog);
			dialog.pack();
		}

		Frame frame = WindowManager.getFrontWindow();
		if (frame==null) return;
		java.awt.Point posi=frame.getLocationOnScreen();
		int initialX = (int)posi.getX() + 38;
		int initialY = (int)posi.getY() + 84;
		dialog.setLocation(initialX,initialY);
		dialog.setVisible(true);
		dialog.toFront();
	}

	public FunctionFinder() {
		this(null);
	}

	public void populateList(String matchingSubstring) {
		String substring = matchingSubstring.toLowerCase();
		functions.removeAll();
		try {
			for(int i=0; i<commands.length; ++i) {
				String commandName = commands[i];
				if (commandName.length()==0)
					continue;
				String lowerCommandName = commandName.toLowerCase();
				if( lowerCommandName.indexOf(substring) >= 0 ) {
					functions.add(commands[i]);
				}
			}
		} catch (Exception e){}
	}

	public void edPaste(String arg) {
		Frame frame = WindowManager.getFrontWindow();
		if (!(frame instanceof Editor))
			return;

		try {
			TextArea ta = ((Editor)frame).getTextArea();
			editor = (Editor)frame;
			int start = ta.getSelectionStart( );
			int end = ta.getSelectionEnd( );
			try {
				ta.replaceRange(arg.substring(0,arg.length()), start, end);
			} catch (Exception e) { }
			if (IJ.isMacOSX())
				ta.setCaretPosition(start+arg.length());
		} catch (Exception e) { }
	}

	public void itemStateChanged(ItemEvent ie) {
		populateList(prompt.getText());
	}

	protected void runFromLabel(String listLabel) {
		edPaste(listLabel);
		closeAndRefocus();
	}

	public void close() {
		closeAndRefocus();
	}

	public void closeAndRefocus() {
		if (dialog!=null)
			dialog.dispose();
		if (editor!=null)
			editor.toFront();
	}

	public void keyPressed(KeyEvent ke) {
		int key = ke.getKeyCode();
		int items = functions.getItemCount();
		Object source = ke.getSource();
		if (source==prompt) {
			if (key==KeyEvent.VK_ENTER) {
				if (1==items) {
					String selected = functions.getItem(0);
					edPaste(selected);
				}
			} else if (key==KeyEvent.VK_UP) {
				functions.requestFocus();
				if(items>0)
					functions.select(functions.getItemCount()-1);
			} else if (key==KeyEvent.VK_ESCAPE) {
				closeAndRefocus();
			} else if (key==KeyEvent.VK_DOWN)  {
				functions.requestFocus();
				if (items>0)
					functions.select(0);
			}
		} else if (source==functions) {
			if (key==KeyEvent.VK_ENTER) {
				String selected = functions.getSelectedItem();
				if (selected!=null)
					edPaste(selected);
			}
			else if (key==KeyEvent.VK_ESCAPE) {
				closeAndRefocus();
			}
			else if (key==KeyEvent.VK_BACK_SPACE || key==KeyEvent.VK_DELETE) {
			/* If someone presses backspace or delete they probably
			   want to remove the last letter from the search string, so
			   switch the focus back to the prompt: */
			prompt.requestFocus();
		}
		}
	}

	public void keyReleased(KeyEvent ke) { }

	public void keyTyped(KeyEvent ke) { }

	public void textValueChanged(TextEvent te) {
		populateList(prompt.getText());
	}

	public void actionPerformed(ActionEvent e) {
		String url2 = this.url;
		Object b = e.getSource();
		if (b==insertButton) {
			int index = functions.getSelectedIndex();
			if (index>=0) {
				String selected = functions.getItem(index);
				edPaste(selected);
			}
		} else if (b==infoButton) {
			int index = functions.getSelectedIndex();
			if (index>=0) {
				String selected = functions.getItem(index);
				int index2 = selected.indexOf("(");
				if (index2==-1)
					index2 = selected.length();
				url2 = url2 + "#" + selected.substring(0, index2);
			}
			IJ.runPlugIn("ij.plugin.BrowserLauncher", url2);
		} else if (b==closeButton)
		closeAndRefocus();
	}

	public void windowClosing(WindowEvent e) {
		closeAndRefocus();
	}

	public void windowActivated(WindowEvent e) { }
	public void windowDeactivated(WindowEvent e) { }
	public void windowClosed(WindowEvent e) { }
	public void windowOpened(WindowEvent e) { }
	public void windowIconified(WindowEvent e) { }
	public void windowDeiconified(WindowEvent e) { }
}
