package net.sf.jabref.gui.contentselector;

import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Window;
import java.awt.event.ActionEvent;

import javax.swing.AbstractAction;
import javax.swing.Box;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.KeyStroke;

import net.sf.jabref.gui.BasePanel;
import net.sf.jabref.gui.JabRefFrame;
import net.sf.jabref.gui.fieldeditors.FieldEditor;
import net.sf.jabref.logic.l10n.Localization;
import net.sf.jabref.model.entry.Keyword;
import net.sf.jabref.model.entry.KeywordList;
import net.sf.jabref.model.metadata.MetaData;

import com.jgoodies.forms.layout.Sizes;
import com.jgoodies.looks.Options;

/**
 * A combo-box and a manage button that will add selected strings to an
 * associated entry editor.
 *
 * Used to manage keywords and authors for instance.
 */
public class FieldContentSelector extends JComponent {

    private static final int MAX_CONTENT_SELECTOR_WIDTH = 240; // The max width of the combobox for content selectors.
    private final JComboBox<String> comboBox;

    private final FieldEditor editor;

    private final MetaData metaData;

    private final AbstractAction action;
    private final String delimiter;


    /**
     *
     * Create a new FieldContentSelector.
     *
     * @param frame
     *            The one JabRef-Frame.
     * @param panel
     *            The basepanel the entry-editor is on.
     * @param owner
     *            The window/frame/dialog which should be the owner of the
     *            content selector dialog.
     * @param editor
     *            The entry editor which will be appended by the text selected
     *            by the user from the combobox.
     * @param action
     *            The action that will be performed to after an item from the
     *            combobox has been appended to the text in the entryeditor.
     * @param horizontalLayout
     *            Whether to put a 2 pixel horizontal strut between combobox and
     *            button.
     */
    public FieldContentSelector(JabRefFrame frame, final BasePanel panel, Window owner, final FieldEditor editor,
                                final AbstractAction action, boolean horizontalLayout, String delimiter) {


        this.editor = editor;
        this.metaData = panel.getBibDatabaseContext().getMetaData();
        this.action = action;
        this.delimiter = delimiter;

        comboBox = new JComboBox<String>() {

            @Override
            public Dimension getPreferredSize() {
                Dimension parents = super.getPreferredSize();
                if (parents.width > MAX_CONTENT_SELECTOR_WIDTH) {
                    parents.width = MAX_CONTENT_SELECTOR_WIDTH;
                }
                return parents;
            }
        };

        GridBagLayout gbl = new GridBagLayout();
        GridBagConstraints con = new GridBagConstraints();

        setLayout(gbl);

        // comboBox.setEditable(true);

        comboBox.setMaximumRowCount(35);

        // Set the width of the popup independent of the size of th box itself:
        comboBox.putClientProperty(Options.COMBO_POPUP_PROTOTYPE_DISPLAY_VALUE_KEY,
                "The longest text in the combo popup menu. And even longer.");

        rebuildComboBox();

        con.gridwidth = horizontalLayout ? 3 : GridBagConstraints.REMAINDER;
        con.fill = GridBagConstraints.HORIZONTAL;
        con.weightx = 1;
        gbl.setConstraints(comboBox, con);

        comboBox.addActionListener(e -> {
            /*
             * These conditions signify arrow key navigation in the dropdown
             * list, so we should not react to it. I'm not sure if this is
             * well defined enough to be guaranteed to work everywhere.
             */
            if ("comboBoxChanged".equals(e.getActionCommand()) && (e.getModifiers() == 0)) {
                return;
            }

            selectionMade();
        });
        // Add an action for the Enter key that signals a selection:
        comboBox.getInputMap().put(KeyStroke.getKeyStroke("ENTER"), "enter");
        comboBox.getActionMap().put("enter", new AbstractAction() {

            @Override
            public void actionPerformed(ActionEvent actionEvent) {
                selectionMade();
                comboBox.setPopupVisible(false);
            }
        });

        add(comboBox);

        if (horizontalLayout) {
            add(Box.createHorizontalStrut(Sizes.dialogUnitXAsPixel(2, this)));
        }

        JButton manage = new JButton(Localization.lang("Manage"));
        gbl.setConstraints(manage, con);
        add(manage);

        manage.addActionListener(e -> {
            ContentSelectorDialog csd = new ContentSelectorDialog(owner, frame, panel, true, editor.getFieldName());
            csd.setLocationRelativeTo(frame);

            // Calling setVisible(true) will open the modal dialog and block
            // for the dialog to close.
            csd.setVisible(true);

            // So we need to rebuild the ComboBox afterwards
            rebuildComboBox();
        });
    }

    private void selectionMade() {
        // The first element is empty to avoid a preselection
        if (comboBox.getSelectedIndex() == 0) {
            return;
        }

        String chosen = (String) comboBox.getSelectedItem();
        if ((chosen == null) || chosen.isEmpty()) {
            return;
        }


        String currentText = editor.getText();
        KeywordList words = KeywordList.parse(currentText, this.delimiter.charAt(0));
        boolean alreadyInList = words.contains(new Keyword(chosen));

        // not the first word and no duplicate -> we need a comma
        if (!"".equals(currentText) && !alreadyInList) {
            editor.append(FieldContentSelector.this.delimiter);
        }

        // no duplicate -> add it
        if (!alreadyInList) {
            editor.append(chosen);
        }

        comboBox.setSelectedIndex(0);

        // Fire event that we changed the editor
        if (action != null) {
            action.actionPerformed(new ActionEvent(editor, 0, ""));
        }

        // Transfer focus to the editor.
        editor.requestFocus();
    }

    public void rebuildComboBox() {
        comboBox.removeAllItems();

        // To have an empty field as the default for the combobox
        comboBox.addItem("");
        for (String item : metaData.getContentSelectorValuesForField(editor.getFieldName())) {
            comboBox.addItem(item);
        }
    }

}
