/** 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.config;

import java.lang.reflect.Method;

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.*;
import com.netscape.management.nmclf.*;
import com.netscape.management.admserv.panel.DialogFrame;

/**
  *
  * @version 0.1 11/28/97
  * @author miodrag@netscape.com
  */

/**
 * Base Config Panel provides a framework for configing panels. It hosts a plugin config
 * panel IPluginConfigPanel and a common command button bar. The changes inside the
 * plugin config panel are monitored using the edit monitor.
*/
public class BaseConfigPanel extends JPanel implements SuiConstants {

    static ResourceSet _resource;
    static String _i18nStatusLoading, _i18nStatusSaving, _i18nLoadFailed;
    static String _i18nSaveToolTip, _i18nResetToolTip, _i18nHelpToolTip;

    static {
        _resource = new ResourceSet("com.netscape.management.admserv.config.config");
        _i18nStatusLoading = _resource.getString("baseconfig", "StatusLoading");
        _i18nStatusSaving = _resource.getString("baseconfig", "StatusSaving");
        _i18nLoadFailed = _resource.getString("baseconfig", "LoadFailed");
        _i18nSaveToolTip = _resource.getString("baseconfig", "SaveToolTip");
        _i18nResetToolTip = _resource.getString("baseconfig", "ResetToolTip");
        _i18nHelpToolTip = _resource.getString("baseconfig", "HelpToolTip");
    }

    static private String[] _toolTips = { _i18nSaveToolTip, 
                                         _i18nResetToolTip, 
                                         null,  
                                         _i18nHelpToolTip};

    protected JLabel _heading;
    protected ButtonBar _cmdButtons;
    protected JComboBox _targetChoice;
    protected EditMonitor _editMonitor;
    protected JPanel _editPanel;
    protected ActionMonitorPanel _actionPanel = new ActionMonitorPanel();
    protected CardLayout _card;
    protected boolean _initialized, _dataValid, _dataSaved;

    static protected Font _fontHeadingLabel = UIManager.getFont("Title.font");
    static protected Font _fontLabel = UIManager.getFont("Panel.font");

    protected IPluginConfigPanel _pluginPanel;
    protected boolean _hasTitle;
    protected boolean _inDialog;

    /**
     * Constructor
     *
     * @param pluginPanel A plugin panel
    */
    public BaseConfigPanel(IPluginConfigPanel pluginPanel) {
        this(pluginPanel, false);
    }

    /**
      * Constructor
      *
      * @param pluginPanel A plugin panel
      * @param inDialog A flag whether the base config panel will reside in a dialog
     */
    public BaseConfigPanel(IPluginConfigPanel pluginPanel,
            boolean inDialog) {
        int vi = VERT_WINDOW_INSET - COMPONENT_SPACE;
        int hi = HORIZ_WINDOW_INSET - COMPONENT_SPACE;
        setLayout(_card = new CardLayout());
        if (!inDialog)
            setBorder(BorderFactory.createEmptyBorder(vi, hi, vi, hi));

        _inDialog = inDialog;

        //_actionPanel = new ActionMonitorPanel();
        //add(_actionPanel, "action");

        _editPanel = new JPanel();
        _editPanel.setLayout( new BorderLayout(/*hgap=*/0,
                /*vgap=*/SuiLookAndFeel.SEPARATED_COMPONENT_SPACE));
        add(_editPanel, "edit");

        setPluginPanel(pluginPanel);
    }

    /**
      * Set the plugin planel
      * @param pluginPanel A plugin panel
     */
    public void setPluginPanel(IPluginConfigPanel pluginPanel) {
        _pluginPanel = pluginPanel;
        _cmdButtons = createCommandButtonBar();
        _editMonitor = createEditMonitor();

        _hasTitle = (_pluginPanel.getTitleText() != null &&
                _pluginPanel.getTitleText().length() != 0);
        _heading = createHeadingLabel(_pluginPanel.getTitleText());

        createEditLayout();

        if (isPluginModelLoaded()) {
            showPanel(_editPanel); //_card.show(this, "edit");
            initPluginPanel();
        } else {
            monitorPluginPanelInit();
        }
    }


    public boolean isInitialized() {
        if (_initialized)
            showPanel(_editPanel);
        return _initialized;
    }

    /**
      * Return the plugin panel
      * @returns The plugin panel
     */
    public IPluginConfigPanel getPluginPanel() {
        return _pluginPanel;
    }


    /**
      * Return the base config panel for the contained component
      * @returns BaseConfigPanel instance or null
     */
    static public BaseConfigPanel getInstance(Component c) {
        while (c != null && ! (c instanceof BaseConfigPanel)) {
            c = c.getParent();
        }
        return (BaseConfigPanel) c;
    }



    // Check if plugin panel data model is loaded
    protected boolean isPluginModelLoaded() {
        IConfigDataModel data = _pluginPanel.getDataModel();
        return (data != null && data.isLoaded());
    }

    /**
      * Initialize the plugin panel. The method will block the current thread if
      * data.load() is called.
      * @see monitorPluginPanelInit()
     */
    protected void initPluginPanel() {
        _initialized = false;
        try {
            IConfigDataModel data = _pluginPanel.getDataModel();
            if (data != null && !data.isLoaded()) {
                data.load();
            }
            _pluginPanel.initialize();
        } catch (ConfigPanelException e) {
            ConfigErrorDialog.showDialog(_pluginPanel.getPanel(), e);
            return;
        }

        _pluginPanel.registerEditComponents(_editMonitor);
        _editMonitor.start();
        _initialized = true;
    }

    /**
      * This method is used as a wrapper for initPluginPanel() if plugin data model is
      * not loaded. It runs in a separate thread so that AWR Event Queue thread does not
      * get locked dupring the remote operation. The method is interacting with the
      * action panel to show the status and to enable the user to interrupt the action.
     */
    protected void monitorPluginPanelInit() {

        /* DT 1/6/98 granting UniversalThreadGroupAccess before creating thread groups */
        Method m = Permissions.getEnablePrivilegeMethod();

        if (m != null) {
            Object[] args = new Object[1];
            args[0] = "UniversalThreadGroupAccess";

            try {
                m.invoke(null, args);
            } catch (Exception e) {
                Debug.println(
                        "ThreadGroup:monitorPluginPanelInit():unable to grant ThreadGroup privileges:" + e);
            }
        }

        showPanel(_actionPanel); //_card.show(this, "action");

        final ThreadGroup tg = new ThreadGroup("LongActionTG");
        Thread t = new Thread(tg, "LongAction") {
                    public void run() {
                        // Give GUI time to complete setup
                        try {
                            Thread.currentThread().sleep(200);
                        } catch (Exception e) {}
                        _actionPanel.monitorProgressStart(
                                _i18nStatusLoading, tg, _stopAction);
                        BaseConfigPanel.this.initPluginPanel();

                        _actionPanel.monitorProgressStop(_initialized ?
                                "" : _i18nLoadFailed);

                        try {
                            SwingUtilities.invokeAndWait(new Runnable() {
                                        public void run() {
                                            if (_initialized) {
                                                showPanel(_editPanel);
                                                        //_card.show(BaseConfigPanel.this, "edit");
                                                        } else if (
                                                        _inDialog) {
                                                closeDialog();
                                            }
                                        }
                                    }
                                    );
                        } catch (Exception e1) {
                            Debug.println(
                                    "monitorPluginPanelInit invokeAndWait " +
                                    e1);
                        }

                    }

                    ActionListener _stopAction = new ActionListener() {
                                public void actionPerformed(
                                        ActionEvent e) {
                                    closeDialog();
                                }
                            };

                };
        t.start();
    }


    /**
      * Panel layout
     */
    protected void createEditLayout() {
        if (_heading != null) {
            _editPanel.add(createNorthPanel(), BorderLayout.NORTH);
        }

        if (_pluginPanel != null) {
            _editPanel.add(createCenterPanel(), BorderLayout.CENTER);
        }

        if (_cmdButtons != null) {
            _editPanel.add(createSouthPanel(), BorderLayout.SOUTH);
        }
    }

    // North panel in border layout hosts heading
    protected JPanel createNorthPanel() {
        JPanel p = new JPanel( new BorderLayout(/*hgap=*/0,
                /*vgap=*/SuiLookAndFeel.SEPARATED_COMPONENT_SPACE));

        if (_heading != null) {
            p.add(_heading, BorderLayout.NORTH);
        }
        return p;
    }

    // Center panel is border layout is hosting the plugin panel
    protected JPanel createCenterPanel() {
        JPanel p = new JPanel(new BorderLayout());
        p.add(_pluginPanel.getPanel(), BorderLayout.CENTER);
        return p;
    }

    // Heading label
    protected JLabel createHeadingLabel(String label) {

        if (label == null || label.length() == 0) {
            return null;
        }
        JLabel heading = new JLabel(label, SwingConstants.LEFT);
        heading.setFont(_fontHeadingLabel);
        return heading;
    }

    // South panel in border layout is hosting command button bar
    protected JPanel createSouthPanel() {
        JPanel p = new JPanel(new BorderLayout());
        p.add(_cmdButtons, BorderLayout.EAST);
        return p;
    }


    /**
      * Command Button Bar
     */
    public ButtonBar createCommandButtonBar() {
        ButtonBar bb = new ButtonBar(_inDialog, _toolTips);
        bb.addActionListener(_btnListener);
        return bb;
    }


    public ButtonBar getCommandButtonBar() {
        return _cmdButtons;
    }


    /**
     * Edit Monitor
    */
    protected EditMonitor createEditMonitor() {
        if (_cmdButtons == null)
            return null;
        EditMonitor monitor = new EditMonitor();
        monitor.addEnableComponent(_cmdButtons.getButton(ButtonBar.cmdOK));

        // When in dialog, sematic for Cancel button is Close the dialog.
        // When in RHP, sematic for Cancel button is Reset the panel content
        if (!_inDialog) {
            monitor.addEnableComponent(
                    _cmdButtons.getButton(ButtonBar.cmdCancel));
        }

        return monitor;
    }

    public EditMonitor getEditMonitor() {
        return _editMonitor;
    }

    /**
      * Listener for events from the button bar
     */
    ActionListener _btnListener = new ActionListener() {
                public void actionPerformed(ActionEvent e) {
                    commandButtonAction(e.getActionCommand());
                }
            };


    /**
     * Handling of command buttons actions
    */
    protected void commandButtonAction(String command) {
        if (command.equals(ButtonBar.cmdOK)) {
            _dataValid = _dataSaved = false;
            try {
                _pluginPanel.applyChanges();
                _dataValid = true;
                if (isPluginModelModified()) {
                    //monitorPluginPanelSave();
                    monitorLongAction(_i18nStatusSaving, new Runnable() {
                                public void run () {
                                    BaseConfigPanel.this.savePluginPanel();
                                }
                            }
                            );
                } else {
                    _editMonitor.setDirtyFlag(false);
                }
            } catch (ConfigPanelException e) {
                ConfigErrorDialog.showDialog(_pluginPanel.getPanel(), e);
            }

        } else if (command.equals(ButtonBar.cmdCancel)) {
            if (_inDialog) {
                closeDialog();
                return;
            }
            try {
                _pluginPanel.resetContent();
            } catch (ConfigPanelException e) {
                ConfigErrorDialog.showDialog(_pluginPanel.getPanel(), e);
                return;
            }

            _editMonitor.setDirtyFlag(false);
        }
        /*else if (command.equals(ButtonBar.cmdClose)) {
             closeDialog();
         }*/
        else if (command.equals(ButtonBar.cmdHelp)) {
            _pluginPanel.showHelp();
        }
        else {
            Debug.println(command + " not implemented");
        }
    }

    // Check if model is modified
    protected boolean isPluginModelModified() {
        IConfigDataModel data = _pluginPanel.getDataModel();
        return (data != null && data.isModified());
    }


    /**
      * Save the plugin panel data model, i.e. copy the data to the server.
      * The method will block the current thread
     */
    void savePluginPanel() {
        try {
            _pluginPanel.getDataModel().save();
            _dataSaved = true;
            _pluginPanel.resetContent();
            _editMonitor.setDirtyFlag(false);
            closeDialog();
        } catch (ConfigPanelException e) {
            ConfigErrorDialog.showDialog(_pluginPanel.getPanel(), e);
        }
    }

    /**
      * This method is used to execute long actions. It runs in a separate
      * thread so that AWR Event Queue thread does not get locked during the remote
      * operation. The method is interacting with the action panel to show the status
      * and to enable the user to interrupt the action.
     */
    public void monitorLongAction(Runnable action) {
        monitorLongAction(_i18nStatusLoading, action);
    }

    public void monitorLongAction(String msg, Runnable action) {

        /* DT 1/6/98 granting UniversalThreadGroupAccess before creating thread groups */
        Method m = Permissions.getEnablePrivilegeMethod();

        if (m != null) {
            Object[] args = new Object[1];
            args[0] = "UniversalThreadGroupAccess";

            try {
                m.invoke(null, args);
            } catch (Exception e) {
                Debug.println(
                        "BaseConfigPanel:monitorLongAction():unable to grant ThreadGroup privileges:" + e);
            }
        }

        showPanel(_actionPanel); //_card.show(this, "action");

        final ThreadGroup tg = new ThreadGroup("LongActionTG");
        final Runnable actionTask = action;
        final String actionMsg = msg;
        Thread t = new Thread(tg, "LongAction") {
                    public void run() {
                        _actionPanel.monitorProgressStart(actionMsg,
                                tg, _stopAction);
                        actionTask.run();
                        _actionPanel.monitorProgressStop("");
                        SwingUtilities.invokeLater( new Runnable() {
                            public void run() {                            
                                showPanel(_editPanel); //_card.show(BaseConfigPanel.this, "edit");
                            }
                        });
                    }

                    ActionListener _stopAction = new ActionListener() {
                                public void actionPerformed(
                                        ActionEvent e) {
                                    showPanel(_editPanel); //_card.show(BaseConfigPanel.this, "edit");
                                }
                            };

                };

        t.start();
    }


    /**
      * Check if running inside a dialog.
     */
    protected Window getDialog() {
        Component c = this;
        while ((c = c.getParent()) != null) {
            if (c instanceof Dialog || c instanceof JDialog) {
                return (Window) c;
            }
        }
        return null;
    }


    /**
      * Close dialog if base config panel is running inside a dialog
     */
    protected void closeDialog() {
        Window dialog = getDialog();
        if (dialog != null) {
            dialog.setVisible(false);
            //dialog.dispose();
            //ModalDialogUtil.sleep();
        }
    }

    /**
      * Consistent UI methods
     */
    public static Border createGroupBorder(String label) {
        TitledBorder titleBorder = BorderFactory.createTitledBorder(
                BorderFactory.createEtchedBorder(), label);
        Border interiorBorder =
                BorderFactory.createEmptyBorder(COMPONENT_SPACE,
                COMPONENT_SPACE, COMPONENT_SPACE, COMPONENT_SPACE);
        return BorderFactory.createCompoundBorder(titleBorder,
                interiorBorder);
    }

    public static Font getLabelFont() {
        return _fontLabel;
    }

    public static Font getHeadingLabelFont() {
        return _fontHeadingLabel;
    }


    // A workaround for a repaint problem which started with Swing 0.7. The problem
    // is related to using CardLayout
    private JPanel _currentPanel;
    protected void showPanel(JPanel panel) {
        if (panel == _actionPanel) {
            add(_actionPanel, "action");
            _card.show(this, "action");
            _currentPanel = panel;
        } else {
            if (_currentPanel == _actionPanel) {
                _actionPanel.setVisible(false);
                remove(_actionPanel);
            }
            _card.show(this, "edit");
            _currentPanel = panel;
        }
    }


    /**
      * Methods for busy status set /clear
      *
      */

    /**
     * Find the current ResourcePage in the current Frame
     */
    private static Framework getActiveFrame(Component c) {
        Framework activeFrame = null;
        if (c != null) {
            activeFrame = (Framework) SwingUtilities.getAncestorOfClass(
                    Framework.class, c);
                }
        if (activeFrame == null) {
            activeFrame =
                    (Framework) UtilConsoleGlobals.getActivatedFrame();
        }
        return activeFrame;
    }

    /**
      * Set progress indicators
      *
      */
    public static void setProgressIndicator(Component c, String text,
            int total, int done) {

        DialogFrame dialog =
                (DialogFrame) SwingUtilities.getAncestorOfClass(
                DialogFrame.class, c);
        if (dialog != null) {
            dialog.setBusyCursor(true);
        }
        Framework activeFrame = getActiveFrame(c);
        if (activeFrame == null)
            return;

        Integer proc = StatusItemProgress.STATE_BUSY;

        if (total > 0) {
            proc = new Integer ((total == done) ? 100 :
                    (int)((done * 100.) / total));
        }

        activeFrame.setBusyCursor(true);
        activeFrame.changeStatusItemState(Framework.STATUS_TEXT, text);
        activeFrame.changeStatusItemState(ResourcePage.STATUS_PROGRESS,
                proc);
    }


    /**
      * Clear progress indicators
      *
      */
    public static void clearProgressIndicator(Component c) {
        DialogFrame dialog =
                (DialogFrame) SwingUtilities.getAncestorOfClass(
                DialogFrame.class, c);
        if (dialog != null) {
            dialog.setBusyCursor(false);
        }
        Framework activeFrame = getActiveFrame(c);
        if (activeFrame == null)
            return;

        activeFrame.setBusyCursor(false);
        activeFrame.changeStatusItemState(Framework.STATUS_TEXT, "");
        activeFrame.changeStatusItemState(ResourcePage.STATUS_PROGRESS,
                new Integer(0));
    }
}
