/** BEGIN COPYRIGHT BLOCK
 * Copyright (C) 2001 Sun Microsystems, Inc.  Used by permission.
 * Copyright (C) 2005 Red Hat, Inc.
 * All rights reserved.
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; version 2
 * 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
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 * 
 * END COPYRIGHT BLOCK **/
package com.netscape.management.admserv.panel;

import java.util.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.border.*;
import com.netscape.management.client.util.*;
import com.netscape.management.client.console.*;
import com.netscape.management.admserv.config.*;
import com.netscape.management.client.*;
import com.netscape.management.nmclf.*;

/**
 * A generic class that implements an editable list with buttons add, edit
 * and remove. A list entry is edited in a custom popup dialog. The list
 * entry details can be shown in a custom group box panel which is placed
 * below (south) of the list and command buttons.
*/

/**
  *
  * @version 0.1 11/23/97
  * @author miodrag@netscape.com
  */
public abstract class EditableList extends JPanel implements SuiConstants {

    static ResourceSet _resource;
    static String _i18nAddButton, _i18nEditButton, _i18nRemoveButton;
    static String _i18nOKButton, _i18nCancelButton;
    static String _i18nAddDefaultToolTip, _i18nEditDefaultToolTip, _i18nRemoveDefaultToolTip;
    static String _i18nOKDefaultToolTip, _i18nCancelDefaultToolTip, _i18nHelpDefaultToolTip;

    static {
        _resource = new ResourceSet("com.netscape.management.admserv.panel.panel");
        _i18nAddButton = _resource.getString("editlist","AddButton");
        _i18nEditButton = _resource.getString("editlist","EditButton");
        _i18nRemoveButton = _resource.getString("editlist","RemoveButton");
        _i18nOKButton = _resource.getString("editlist","OKButton");
        _i18nCancelButton = _resource.getString("editlist","CancelButton");
        _i18nAddDefaultToolTip = _resource.getString("editlist","AddDefaultToolTip");
        _i18nEditDefaultToolTip = _resource.getString("editlist","EditDefaultToolTip");
        _i18nRemoveDefaultToolTip = _resource.getString("editlist","RemoveDefaultToolTip");
        _i18nOKDefaultToolTip = _resource.getString("editlist","OKDefaultToolTip");
        _i18nCancelDefaultToolTip = _resource.getString("editlist","CancelDefaultToolTip");
        _i18nHelpDefaultToolTip = _resource.getString("editlist","HelpDefaultToolTip");
    }

    protected JList _list;
    protected DefaultListModel _listModel;
    protected JScrollPane _listScroller;
    protected ButtonBar _buttons;
    protected ItemEditDialog _editDialog;
    protected JPanel _detailsPanel, _editPanel;
    static protected String _cmdAdd = "add", _cmdEdit = "edit",
    _cmdRemove = "remove";
    protected JPanel _north, _south;


    /**
      * A compoent to request focus when the dialog is shown
      */
    public JComponent _initialFocusComponent;

    /**
      * List of text fields where <enter> key press should be treated as dialog edit commit,
      * i.e. equivalent as if the user has pressed OK button
      */
    public JTextField[]_commitOnEnterComponents;


    public EditableList() {
        //setLayout(new BorderLayout(0, 10));
        setLayout( new BorderLayout(0,
                /*vgap=*/SuiLookAndFeel.SEPARATED_COMPONENT_SPACE));
        createLayout();
    }

    protected void finalize() {
        if (_editDialog != null) {
            _editDialog.dispose();
            ModalDialogUtil.sleep();
        }
    }

    public void addListDataListener(ListDataListener listener) {
        _listModel.addListDataListener(listener);
    }

    public void removeListDataListener(ListDataListener listener) {
        _listModel.removeListDataListener(listener);
    }


    private void createLayout() {

        setBorder( BorderFactory.createEmptyBorder(
                DIFFERENT_COMPONENT_SPACE, 0, 0, 0));

        //_north = new JPanel(new BorderLayout(0, 10));
        _north = new JPanel(new BorderLayout(DIFFERENT_COMPONENT_SPACE, 0));

        // list
        _list = new JList();
        _list.setModel(_listModel = new DefaultListModel());
        _list.addListSelectionListener(_selectListener);
        _list.addMouseListener(_dblclkListener);
        _list.setVisibleRowCount(5);
        _list.setBackground(Color.white);
        _listScroller = new JScrollPane();
        _listScroller.getViewport().setView(_list);
        _listScroller.setBorder(BorderFactory.createLoweredBevelBorder());

        // buttons
        _buttons = new ButtonBar(new String[]{_cmdAdd, _cmdEdit, _cmdRemove}, 
                                 new String[]{_i18nAddButton, _i18nEditButton, _i18nRemoveButton}, 
                                 new String[]{getAddToolTip(), getEditToolTip(), getRemoveToolTip()},
                                 false);
        _buttons.addActionListener(_btnListener);
        _buttons.getButton(_cmdEdit).setEnabled(false);
        _buttons.getButton(_cmdRemove).setEnabled(false);

        _north.add(_listScroller, BorderLayout.CENTER);
        _north.add(_buttons, BorderLayout.EAST);

        //detals
        _detailsPanel = getDetailsPanel();

        add(_north, BorderLayout.CENTER);
        if (_detailsPanel != null) {
            _south = new JPanel(new BorderLayout());
            _south.setBorder(
                    BaseConfigPanel.createGroupBorder(getDetailsTitle()));
            _south.add(_detailsPanel);
            add(_south, BorderLayout.SOUTH);
        }

        _editPanel = getEditPanel();
    }

    int findItemIndex(String item) {
        for (int i = 0; i < _listModel.getSize(); i++) {
            String curItem = (String)_listModel.getElementAt(i);
            if (curItem.equals(item)) {
                return i;
            }
        }
        return -1;
    }

    void addItem(String item) {
        if (item.length() == 0)
            return;
        if (findItemIndex(item) == -1) {
            createItem(item);
            _listModel.addElement(item);
        } else {
            updateItem(item, false);
        }
        selectItem(item);
        _list.setSelectedIndex(findItemIndex(item));
    }

    void processEditOperation(String item) {
        int idx = _list.getSelectedIndex();
        if (idx < 0) {
            return;
        } else if (item.length() == 0) {
            _listModel.removeElementAt(idx);
            removeItem(item);
        } else {
            String itemOldName = (String)_listModel.getElementAt(idx);
            boolean isRenamed = !itemOldName.equals(item);
            _listModel.setElementAt(item, idx);
            updateItem(itemOldName, isRenamed);
            _list.setSelectedIndex(findItemIndex(item));
            if (isRenamed)
                _list.repaint();
        }
    }

    // A check for a JFC 0.5.1 bug; If the last element in the list is deleted,
    // no element is selected, but the call getSelectedIndex() returns the
    // index of the non-existing element that was just deleted
    int getSelectedIndex() {
        int idx = _list.getSelectedIndex();
        return (idx < _listModel.size()) ? idx : -1;
    }

    void adjustSelection() {
        int idx = getSelectedIndex();
        _buttons.getButton(_cmdEdit).setEnabled(idx >= 0);
        _buttons.getButton(_cmdRemove).setEnabled(idx >= 0);
        selectItem((idx >= 0) ? (String)_listModel.getElementAt(idx) :
                null);
    }

    ListSelectionListener _selectListener = new ListSelectionListener() {
                public void valueChanged(ListSelectionEvent e) {
                    if (!e.getValueIsAdjusting()) {
                        EditableList.this.adjustSelection();
                    }
                }
            };


    protected void createEditDialog(boolean setAddTitle) {
        if (setAddTitle) {
            _editDialog = new ItemEditDialog(_north, getAddTitle(),
                    _editPanel, this);
        } else {
            _editDialog = new ItemEditDialog(_north, getEditTitle(),
                    _editPanel, this);
        }
        _editDialog.setHelpResourceSet(getHelpResourceSet());
        _editDialog.setHelpToken(getHelpToken());
    }


    /**
      * Edit a list member when double-clicked
      */
    MouseListener _dblclkListener = new MouseAdapter () {
                public void mousePressed(MouseEvent e) {
                    if (e.getClickCount() == 2) {
                        JList list = (JList) e.getSource();
                        int idx = list.locationToIndex(e.getPoint());

                        if (idx >= 0) {
                            createEditDialog(false);
                            String item =
                                    (String)_listModel.getElementAt(idx);
                            setEditPanelParameters(item);
                            while (true) {
                                _editDialog.setVisible(true);
                                if (_editDialog.getCommand().equals(
                                        ButtonBar.cmdOK)) {
                                    try {
                                        validateEdit();
                                        String newName =
                                                getEditPanelItem().trim();
                                        processEditOperation(newName);
                                        break;
                                    } catch (ValidationException ex) {
                                        ConfigErrorDialog.showDialog(
                                                _north, ex);
                                    }
                                } else {
                                    break;
                                }
                            }
                        }
                    }
                }
            };


    ActionListener _btnListener = new ActionListener() {

                public void actionPerformed(ActionEvent e) {

                    int idx = getSelectedIndex();
                    String cmd = e.getActionCommand();

                    if (cmd.equals(_cmdAdd)) {
                        createEditDialog(true);
                        setEditPanelParameters(null);
                        while (true) {
                            _editDialog.setVisible(true);
                            if (_editDialog.getCommand().equals(
                                    ButtonBar.cmdOK)) {
                                try {
                                    validateEdit();
                                    String item = getEditPanelItem().trim();
                                    addItem(item);
                                    break;
                                } catch (ValidationException ex) {
                                    ConfigErrorDialog.showDialog(
                                            _north, ex);
                                }
                            } else {
                                break;                            }
                        }
                    }
                    else if (cmd.equals(_cmdEdit)) {
                        createEditDialog(false);
                        if (idx >= 0) {
                            String item =
                                    (String)_listModel.getElementAt(idx);
                            setEditPanelParameters(item);
                            while (true) {
                                _editDialog.setVisible(true);
                                if (_editDialog.getCommand().equals(
                                        ButtonBar.cmdOK)) {
                                    try {
                                        validateEdit();
                                        String newName =
                                                getEditPanelItem().trim();
                                        processEditOperation(newName);
                                        break;
                                    } catch (ValidationException ex) {
                                        ConfigErrorDialog.showDialog(
                                                _north, ex);
                                    }
                                } else {
                                    break;
                                }
                            }
                        }
                    }
                    else if (cmd.equals(_cmdRemove)) {
                        if (idx >= 0) {
                            removeItem(
                                    (String)_listModel.getElementAt(idx));
                            _listModel.removeElementAt(idx);
                        }
                    }

                    EditableList.this.adjustSelection();

                }
            };

    public void setList(Vector v) {
        _listModel.removeAllElements();
        for (int i = 0; i < v.size(); i++) {
            _listModel.addElement(v.elementAt(i));
        }
        _buttons.getButton(_cmdEdit).setEnabled(false);
        _buttons.getButton(_cmdRemove).setEnabled(false);
        if (v.size() > 0) {
            _list.setSelectedIndex(0);
            _buttons.getButton(_cmdEdit).setEnabled(true);
            _buttons.getButton(_cmdRemove).setEnabled(true);
        }
    }

    public Vector getList() {
        Vector v = new Vector();
        for (int i = 0; i < _listModel.getSize(); i++) {
            v.addElement(_listModel.getElementAt(i));
        }
        return v;
    }

    /**
      * A panel that shows details for the selected entry in the list
     */
    abstract public JPanel getDetailsPanel();
    abstract public String getDetailsTitle();

    /**
     * A custom panel to be shown in the edit dialog
    */
    abstract public JPanel getEditPanel();
    abstract public String getEditTitle();
    abstract public String getAddTitle();
    abstract public ResourceSet getHelpResourceSet();
    abstract public String getHelpToken();
    abstract public void validateEdit() throws ValidationException;

    /**
     * Custom Tool Tips to Display
     */
    protected String getEditToolTip() {
        return _i18nEditDefaultToolTip;
    }

    protected String getAddToolTip() {
        return _i18nAddDefaultToolTip;
    }

    protected String getRemoveToolTip() {
        return _i18nRemoveDefaultToolTip;
    }

    /**
     * A method called to populate the contents of edit panel.
     * Paremeter <code>item</code> is null for ADD operation. A
     * non-null string is passed for EDIT operation
    */
    abstract public void setEditPanelParameters(String item);

    /**
     * A method called to craete a new item. The item parameters should
     * be read from the custom edit panel
    */
    abstract public void createItem(String name);

    /**
     * A method called to update an item. The item parameters should
     * be read from the custom edit panel
    */
    abstract public void updateItem(String name, boolean isRenamed);

    /**
     * A method called to remove an item.
    */
    abstract public void removeItem(String name);

    /**
     * A method called when an item is selected
    */
    abstract public void selectItem(String item);


    /**
     * A method called to read the item from the edit panel
    */
    abstract public String getEditPanelItem();

    /**
     * A method to set initial focus
    */
    public void setEditPanelInitalFocusComponent(JComponent c) {
        _initialFocusComponent = c;
    }

    public void setEditPanelCommitOnEnterComponents(
            JTextField[] components) {
        _commitOnEnterComponents = components;
    }



}

/**
  * Item Edit Dialog
 */
class ItemEditDialog extends AbstractDialog {
    String _cmd = "";
    ButtonBar _buttons;
    final Component _parent;
    final JPanel _editPanel;
    final EditableList _editList;
    ResourceSet _helpResourceSet;
    String _helpToken;

    public ItemEditDialog(Component parent, String title,
            JPanel editPanel, EditableList editList) {
        super(SuiOptionPane.getFrameForComponent(parent), title,
                /*modal=*/true);
        _parent = parent;
        getContentPane().add(createLayout(_editPanel = editPanel));
        _editList = editList;
        pack();
        Dimension size = getSize();
        setMinimumSize(size);

        // This size make the dialog look better then when it is packed
        if (size.width < 320) {
            size.width = 320;
            setSize(size);
        }

        addWindowListener(_winListener);
        _buttons.addActionListener(_actionListener);
    }

    public void setHelpResourceSet(ResourceSet rs) {
        _helpResourceSet = rs;
    }

    public void setHelpToken(String token) {
        _helpToken = token;
    }

    private boolean _firstCall = true;

    public void setVisible(boolean show) {
        if (show && _firstCall) {
            if (_editList._initialFocusComponent != null) {
                _editList._initialFocusComponent.requestFocus();
            }
            if (_editList._commitOnEnterComponents != null) {
                for (int i = 0;
                        i < _editList._commitOnEnterComponents.length;
                        i++) {
                    _editList._commitOnEnterComponents[i]
                            .addKeyListener(_keyListener);
                }
            }
            _firstCall = false;
        }
        super.setVisible(show);
    }

    private KeyListener _keyListener = new KeyAdapter() {
                public void keyPressed (KeyEvent e) {
                    if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {
                        _cmd = ButtonBar.cmdCancel;
                        ItemEditDialog.this.setVisible(false);
                    } else if (e.getKeyCode() == KeyEvent.VK_ENTER) {
                        _cmd = ButtonBar.cmdOK;
                        ItemEditDialog.this.setVisible(false);
                    }
                }
            };


    private WindowListener _winListener = new WindowAdapter() {
                public void windowClosing(WindowEvent e) {
                    _cmd = ButtonBar.cmdCancel;
                    ItemEditDialog.this.setVisible(false);
                }
            };


    protected void showHelp() {
        if (_helpResourceSet != null && _helpToken != null) {
            (new Help(_helpResourceSet)).contextHelp(_helpToken);
        }
    }

    private ActionListener _actionListener = new ActionListener() {
                public void actionPerformed(ActionEvent e) {
                    _cmd = e.getActionCommand();
                    if (_cmd.equals(ButtonBar.cmdHelp)) {
                        showHelp();
                    } else {
                        ItemEditDialog.this.setVisible(false);
                    }
                }
            };

    private JPanel createLayout(JPanel editPanel) {
        JPanel p = new JPanel(new BorderLayout());

        GBC gbc = new GBC();
        JPanel north = new JPanel(new GridBagLayout());

        gbc.setInsets(0, 0, 0, 0);
        gbc.setGrid(0, 0, 1, 1);
        gbc.setSpace(1.0, 1.0, GBC.WEST, //anchor
                GBC.BOTH); //fill
        north.add(editPanel, gbc);

        p.add(north, BorderLayout.NORTH);
        _buttons = 
            new ButtonBar(new String[]{ButtonBar.cmdOK, ButtonBar.cmdCancel, null, ButtonBar.cmdHelp},
                          new String[]{ EditableList._i18nOKButton, EditableList._i18nCancelButton, null,
                                        ButtonBar._i18nHelpButton}, 
                          new String[]{EditableList._i18nOKDefaultToolTip, EditableList._i18nCancelDefaultToolTip, 
                                       null, EditableList._i18nHelpDefaultToolTip}, 
                          true);

        _buttons.setBorder( new EmptyBorder(
                SuiLookAndFeel.SEPARATED_COMPONENT_SPACE, 0, 0, 0));
        p.add(_buttons, BorderLayout.SOUTH);

        return p;
    }

    public String getCommand() {
        return _cmd;
    }
}
