/*
 * (c) Copyright: Artenum SARL, 101-103 Boulevard Mac Donald, 
 *                75019, Paris, France 2005.
 *                http://www.artenum.com
 *
 * License:
 *
 *  This program is free software; you can redistribute it 
 *  and/or modify it under the terms of the Q Public License; 
 *  either version 1 of the License.
 * 
 *  This program is distributed in the hope that it will be
 *  useful, but WITHOUT ANY WARRANTY; without even the implied
 *  warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
 *  PURPOSE. See the Q Public License for more details.
 * 
 *  You should have received a copy of the Q Public License 
 *  License along with this program;
 *  if not, write to:
 *    Artenum SARL, 101-103 Boulevard Mac Donald,
 *    75019, PARIS, FRANCE, e-mail: contact@artenum.com
 */

package com.artenum.jyconsole.ui;

import com.artenum.jyconsole.CommandLine;
import com.artenum.jyconsole.JyConsole;
import com.artenum.jyconsole.util.ToolBox;

import java.awt.Component;
import java.awt.Point;
import java.awt.Toolkit;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;

import java.lang.reflect.Method;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;

import javax.swing.AbstractListModel;
import javax.swing.JList;
import javax.swing.JScrollPane;
import javax.swing.JWindow;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;

/**
 * <pre>
 * <b>Project ref           :</b> JyConsole project
 * <b>Copyright and license :</b> See relevant sections
 * <b>Status                :</b> under development
 * <b>Creation              :</b> 04/03/2005
 * <b>Modification          :</b>
 *
 * <b>Description  :</b> The graphical component used to show the object methodes.
 *
 * </pre>
 * <table cellpadding="3" cellspacing="0" border="1" width="100%">
 * <tr BGCOLOR="#CCCCFF" CLASS="TableHeadingColor"><td><b>Version number</b></td><td><b>Author (name, e-mail)</b></td><td><b>Corrections/Modifications</b></td></tr>
 * <tr><td>0.1</td><td>Sebastien Jourdain, jourdain@artenum.com</td><td>Creation</td></tr>
 * </table>
 *
 * @author        Sebastien Jourdain
 * @version       0.1
 */
public class CompletionWindow extends JWindow implements KeyListener, ListSelectionListener {
    private JList list;
    private MethodeModel model;
    private JyConsole console;
    private JScrollPane scroll;
    private CommandLine cmdOrigine;
    private int maxHeight = -1;

    public CompletionWindow(JyConsole console) {
        super(ToolBox.getParentFrame(console));
        this.console = console;
        model = new MethodeModel();
        list = new JList(model);
        list.addListSelectionListener(this);
        scroll = new JScrollPane(list);
        getContentPane().add(scroll);
        setSize(300, 100);
        list.addKeyListener(this);
        list.getInputMap().clear();
        scroll.getInputMap().clear();
        maxHeight = (int) Toolkit.getDefaultToolkit().getScreenSize().getHeight();
    }

    public MethodeModel getModel() {
        return model;
    }

    public void showWindow() {
        cmdOrigine = console.getCommandLine();
        setVisible(true);
        Point p = console.getInputComponent().getCaretPositionPoint();
        Component current = (Component) console.getInputComponent();
        Point p2;
        current = current.getParent();
        while (current != null) {
            p2 = current.getLocation();
            p.translate(p2.x, p2.y);
            current = current.getParent();
        }

        p.x += console.getPromptWidth();
        // translateVerticaly if not enought space
        if ((p.y + getHeight()) > maxHeight) {
            p.y = (maxHeight - getHeight());
        }

        setLocation(p);
        list.setSelectedIndex(0);
    }

    private void selectMethodeWhichStartWith(String start) {
        model.setFilter(start);
        list.setSelectedIndex(0);
    }

    public void keyPressed(KeyEvent e) {
        CommandLine localCmdLine = console.getCommandLine();
        if (e.getKeyCode() == KeyEvent.VK_ENTER) {
            setVisible(false);
            String selectedMethode = ((String) list.getSelectedValue());
            if (cmdOrigine.askForDictionnary()) {
                StringBuffer cmdLine = new StringBuffer();
                cmdLine.append(cmdOrigine.getTxtBefore());
                if (!cmdOrigine.getCompletionCommand().equals(cmdOrigine.getFilterCommand())) {
                    cmdLine.append(cmdOrigine.getCompletionCommand());
                }

                cmdLine.append(selectedMethode);
                cmdLine.append(cmdOrigine.getTxtAfter());
                console.updateCommandLine(cmdLine.toString());
            } else if ((selectedMethode != null) && selectedMethode.toLowerCase().startsWith(localCmdLine.getFilterCommand().toLowerCase()) &&
                    !selectedMethode.equals(localCmdLine.getFilterCommand())) {
                StringBuffer cmdLine = new StringBuffer();
                cmdLine.append(localCmdLine.getTxtBefore());
                cmdLine.append(localCmdLine.getCompletionCommand());
                if (cmdLine.length() > 0) {
                    cmdLine.append(".");
                }

                if ((selectedMethode.indexOf("(") == -1) || selectedMethode.endsWith("()")) {
                    cmdLine.append(selectedMethode);
                } else {
                    cmdLine.append(selectedMethode.substring(0, selectedMethode.lastIndexOf("(") + 1));
                }

                cmdLine.append(localCmdLine.getTxtAfter());
                console.updateCommandLine(cmdLine.toString());
            }

            console.setFocusForInput();
        } else if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {
            setVisible(false);
            console.setFocusForInput();
        } else if (e.getKeyCode() == KeyEvent.VK_DOWN) {
            if (model.getSize() > 0) {
                list.setSelectedIndex((list.getSelectedIndex()) % model.getSize());
            }
        } else if (e.getKeyCode() == KeyEvent.VK_UP) {
            if (model.getSize() > 0) {
                list.setSelectedIndex((model.getSize() + list.getSelectedIndex()) % model.getSize());
            }
        } else if (e.getKeyCode() == KeyEvent.VK_BACK_SPACE) {
            if (localCmdLine.getFilterCommand().length() > 0) {
                StringBuffer buffer = new StringBuffer(localCmdLine.getCommand()).deleteCharAt(localCmdLine.getCaretPosition() - 1);
                console.updateCommandLine(buffer.toString());
                localCmdLine = console.getCommandLine();
                selectMethodeWhichStartWith(localCmdLine.getFilterCommand());
            } else {
                setVisible(false);
                console.setFocusForInput();
            }
        } else {
            if (e.getKeyChar() != KeyEvent.CHAR_UNDEFINED) {
                console.getInputComponent().appendText("" + e.getKeyChar(), JyConsole.STYLE_DEFAULT);
                localCmdLine = console.getCommandLine();
                selectMethodeWhichStartWith(localCmdLine.getFilterCommand());
            }
        }
    }

    public void keyReleased(KeyEvent e) {}

    public void keyTyped(KeyEvent e) {}

    public void valueChanged(ListSelectionEvent e) {
        list.ensureIndexIsVisible(list.getSelectedIndex());
    }

    public class MethodeModel extends AbstractListModel {
        private ArrayList data;
        private ArrayList filteredData;
        private String filter;

        public MethodeModel() {
            data = new ArrayList();
            filteredData = new ArrayList();
        }

        public Object getElementAt(int index) {
            return (filter != null) ? filteredData.get(index) : data.get(index);
        }

        public int getSize() {
            return (filter != null) ? filteredData.size() : data.size();
        }

        public void setFilter(String filter) {
            if ((filter == null) || ((filter != null) && (filter.length() == 0))) {
                this.filter = null;
            } else {
                this.filter = filter;
                filteredData.clear();
                String currentLine;
                for (Iterator i = data.iterator(); i.hasNext();) {
                    currentLine = (String) i.next();
                    if (currentLine.toLowerCase().startsWith(filter.toLowerCase())) {
                        filteredData.add(currentLine);
                    }
                }
            }

            fireContentsChanged(this, 0, getSize());
        }

        public void updateData(Collection list) {
            Object[] sort = list.toArray();
            Arrays.sort(sort);
            data.clear();
            for (int i = 0; i < sort.length; i++) {
                data.add(sort[i].toString());
            }

            setFilter(null);
        }

        public void updateData(Method[] methodList) {
            data.clear();
            StringBuffer buffer;
            for (int i = 0; i < methodList.length; i++) {
                buffer = new StringBuffer(methodList[i].getName());
                buffer.append("( ");
                for (int j = 0; j < methodList[i].getParameterTypes().length; j++) {
                    buffer.append(" ");
                    buffer.append(methodList[i].getParameterTypes()[j].getName());
                    buffer.append(" ,");
                }

                data.add(buffer.substring(0, buffer.length() - 1) + ")");
            }

            Object[] sort = data.toArray();
            Arrays.sort(sort);
            data.clear();
            for (int i = 0; i < sort.length; i++) {
                data.add(sort[i]);
            }

            setFilter(null);
        }
    }
}
