/*
 * Copyright 2008 Ayman Al-Sairafi ayman.alsairafi@gmail.com
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License
 *       at http://www.apache.org/licenses/LICENSE-2.0
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package jsyntaxpane.actions.gui;

import java.awt.Font;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Window;
import java.awt.event.KeyEvent;
import java.util.List;
import java.util.Vector;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.SwingUtilities;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.text.BadLocationException;
import javax.swing.text.JTextComponent;
import jsyntaxpane.actions.ActionUtils;
import jsyntaxpane.util.StringUtils;
import jsyntaxpane.util.SwingUtils;

/**
 *
 * @author Ayman Al-Sairafi
 */
public class ComboCompletionDialog 
	extends javax.swing.JDialog implements EscapeListener {

    /**
     * The result returned to the caller
     */
    private String result = null;
    /**
     * Our target component
     */
    private JTextComponent target;
    public String escapeChars = ";(= \t\n\r";
    public List<String> items;

    /**
     * Creates new form ComboCompletionDialog
     * @param target
     */
    public ComboCompletionDialog(JTextComponent target) {
        super(ActionUtils.getFrameFor(target), true);
        initComponents();
        jTxtItem.getDocument().addDocumentListener(new DocumentListener() {

            @Override
            public void insertUpdate(DocumentEvent e) {
                refilterList();
            }

            @Override
            public void removeUpdate(DocumentEvent e) {
                refilterList();
            }

            @Override
            public void changedUpdate(DocumentEvent e) {
                refilterList();
            }
        });
        // This will allow the textfield to receive TAB keys
        jTxtItem.setFocusTraversalKeysEnabled(false);
        this.target = target;
        SwingUtils.addEscapeListener(this);
    }

    /**
     * Display the Completion Dialog with initial abbrev and using the given items
     * The dialog is responsible for showing itself and for updating the target
     * with the text, depending on user actions.
     *
     * The dialog will be aligned to the selectionStart of the target component
     * and when a selection is made, replaceSelection will be called on dialog
     *
     * @param abbrev
     * @param items
     */
    public void displayFor(String abbrev, List<String> items) {
        this.items = items;
        try {
            Window window = SwingUtilities.getWindowAncestor(target);
            Rectangle rt = target.modelToView(target.getSelectionStart());
            Point loc = new Point(rt.x, rt.y);
            setLocationRelativeTo(window);
            // convert the location from Text Componet coordinates to
            // Frame coordinates...
            loc = SwingUtilities.convertPoint(target, loc, window);
            // and then to Screen coordinates
            SwingUtilities.convertPointToScreen(loc, window);
            setLocation(loc);
        } catch (BadLocationException ex) {
            Logger.getLogger(ComboCompletionDialog.class.getName()).log(Level.SEVERE, null, ex);
        } finally {
            Font font = target.getFont();
            jTxtItem.setFont(font);
            jLstItems.setFont(font);
            doLayout();
            jTxtItem.setText(abbrev);
            refilterList();
            setVisible(true);
        }
    }

    private void refilterList() {
        String prefix = jTxtItem.getText();
        Vector<String> filtered = new Vector<String>();
        Object selected = jLstItems.getSelectedValue();
        for (String s : items) {
            if (StringUtils.camelCaseMatch(s, prefix)) {
                filtered.add(s);
            }
        }
        jLstItems.setListData(filtered);
        if (selected != null && filtered.contains(selected)) {
            jLstItems.setSelectedValue(selected, true);
        } else {
            jLstItems.setSelectedIndex(0);
        }
    }

    /** This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        jTxtItem = new javax.swing.JTextField();
        jScrollPane1 = new javax.swing.JScrollPane();
        jLstItems = new javax.swing.JList();

        setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
        setResizable(false);
        setUndecorated(true);

        jTxtItem.setBorder(null);
        jTxtItem.addKeyListener(new java.awt.event.KeyAdapter() {
            public void keyPressed(java.awt.event.KeyEvent evt) {
                jTxtItemKeyPressed(evt);
            }
        });

        jLstItems.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION);
        jLstItems.setFocusable(false);
        jLstItems.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                jLstItemsMouseClicked(evt);
            }
        });
        jScrollPane1.setViewportView(jLstItems);

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addComponent(jTxtItem, javax.swing.GroupLayout.DEFAULT_SIZE, 375, Short.MAX_VALUE)
            .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 375, Short.MAX_VALUE)
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addComponent(jTxtItem, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addGap(0, 0, 0)
                .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 111, Short.MAX_VALUE))
        );

        pack();
    }// </editor-fold>//GEN-END:initComponents

    private void jTxtItemKeyPressed(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_jTxtItemKeyPressed

        int i = jLstItems.getSelectedIndex();
        switch (evt.getKeyCode()) {
            case KeyEvent.VK_ESCAPE:
                result = jTxtItem.getText();
                target.replaceSelection(result);
                setVisible(false);
                return;
            case KeyEvent.VK_DOWN:
                if (i < jLstItems.getModel().getSize() - 1) {
                    i++;
                }
                jLstItems.setSelectedIndex(i);
                jLstItems.ensureIndexIsVisible(i);
                break;
            case KeyEvent.VK_UP:
                if (i > 0) {
                    i--;
                }
                jLstItems.setSelectedIndex(i);
                jLstItems.ensureIndexIsVisible(i);
                break;
        }

        if (escapeChars.indexOf(evt.getKeyChar()) >= 0) {
            if (jLstItems.getSelectedIndex() >= 0) {
                result = jLstItems.getSelectedValue().toString();
            } else {
                result = jTxtItem.getText();
            }
            char pressed = evt.getKeyChar();
            // we need to just accept ENTER, and replace the tab with a single
            // space
            if (pressed != '\n') {
                result += (pressed == '\t') ? ' ' : pressed;
            }
            target.replaceSelection(result);
            setVisible(false);
        }
    }//GEN-LAST:event_jTxtItemKeyPressed

	private void jLstItemsMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_jLstItemsMouseClicked
		if(evt.getClickCount() == 2) {
			String selected = jLstItems.getSelectedValue().toString();
			target.replaceSelection(selected);
			setVisible(false);
		}
	}//GEN-LAST:event_jLstItemsMouseClicked

    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JList jLstItems;
    private javax.swing.JScrollPane jScrollPane1;
    private javax.swing.JTextField jTxtItem;
    // End of variables declaration//GEN-END:variables

	@Override
	public void escapePressed() {
		setVisible(false);
	}
}
