/**
 *  Licensed under GPL. For more information, see
 *    http://jaxodraw.sourceforge.net/license.html
 *  or the LICENSE file in the jaxodraw distribution.
 */
package net.sf.jaxodraw.gui.panel;

import java.awt.BorderLayout;
import java.awt.Component;

import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import java.util.Iterator;
import java.util.List;

import javax.swing.Box;
import javax.swing.DefaultListModel;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JList;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.border.EmptyBorder;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;

import net.sf.jaxodraw.gui.JaxoDialogs;
import net.sf.jaxodraw.gui.swing.JaxoTitledBorder;
import net.sf.jaxodraw.io.imports.JaxoImport;
import net.sf.jaxodraw.io.exports.JaxoExport;
import net.sf.jaxodraw.plugin.JaxoPlugin;
import net.sf.jaxodraw.plugin.JaxoPluginInfo;
import net.sf.jaxodraw.plugin.JaxoPluginLoader;
import net.sf.jaxodraw.util.JaxoInfo;
import net.sf.jaxodraw.util.JaxoLanguage;
import net.sf.jaxodraw.util.JaxoLocalized;
import net.sf.jaxodraw.util.JaxoLog;

/**
 * Brings up a panel that allows to manipulate installed plugins.
 */
public class JaxoPluginManagerPanel
        implements ListSelectionListener, JaxoLocalized {
    private JDialog dialog;
    private List<Object> plugins; // FIXME: make it <JaxoPlugin>
    private final DefaultListModel listModel = new DefaultListModel();
    private final JTextArea describeArea = new JTextArea(5, 20);
    private JList list;
    private final JButton uninstallButton = new JButton();
    private final JButton installButton = new JButton();
    private final JButton closeButton = new JButton();
    private JPanel namePanel;
    private JPanel describePanel;
    private JPanel pluginPanel;

    /**
     * Creates a new JaxoPluginManagerPanel dialog which is always modal.
     *
     * @param parent the parent Component.
     */
    public JaxoPluginManagerPanel(final Component parent) {
        initListOfPlugins();
        initListModel();
        initComponents(parent);
    }

    /**
     * Activates the Plagin Manager Panel.
     *
     * @param b true to activate.
     */
    public void setVisible(final boolean b) {
        dialog.setVisible(b);
    }

    /** {@inheritDoc} */
    public final void valueChanged(final ListSelectionEvent e) {
        if (!e.getValueIsAdjusting()) {
            final int[] indices = list.getSelectedIndices();

            if (indices.length == 1) {
                uninstallButton.setEnabled(true);
                final JaxoPlugin selected = (JaxoPlugin) plugins.get(indices[0]);
                final String version = selected.version();
                final String description = selected.description();
                final String text = JaxoLanguage.translate("Name") + ": "
                        + selected.pluginName()
                        + "\n\n" + JaxoLanguage.translate("Version") + ": "
                        + version + "\n\n\n" + description;
                describeArea.setText(text);
            } else {
                uninstallButton.setEnabled(indices.length != 0);
                describeArea.setText("");
            }
        }
    }

      //
     // private
    //

    private void initListOfPlugins() {
        // get all export plugins, this includes built-ins and externals
        this.plugins = JaxoPluginInfo.getPluginInfo().getExportFormats();
        // remove built-ins
        this.plugins.removeAll(JaxoExport.getBuiltInFormats());

        // add all import formats
        this.plugins.addAll(JaxoPluginInfo.getPluginInfo().getImportFormats());
        // remove built-ins
        this.plugins.removeAll(JaxoImport.getBuiltInFormats());
    }

    private void initListModel() {

        for (int i = 0; i < plugins.size(); i++) {
            listModel.add(i, ((JaxoPlugin) plugins.get(i)).pluginName());
        }
    }

    private void initComponents(final Component parent) {
        list = new JList(listModel);
        list.setBorder(new EmptyBorder(new Insets(5, 5, 5, 5)));
        list.addListSelectionListener(this);

        setupPluginPanel();
        final JPanel buttonPanel = getButtonPanel();

        dialog =
            JaxoDialogs.newDialog(parent,
            JaxoDialogs.windowTitle("JaxoPluginManagerPanel.title"), true);
        dialog.getContentPane().add(pluginPanel, BorderLayout.CENTER);
        dialog.getContentPane().add(buttonPanel, BorderLayout.PAGE_END);

        dialog.pack();
        dialog.setLocationRelativeTo(parent);

    }

    private void setupPluginPanel() {
        final JScrollPane listScrollPane = new JScrollPane(list);
        namePanel = JaxoDialogs.newTitledBorderLayoutPanel(
                "JaxoPluginManagerPanel.namePanel.title");
        namePanel.add(listScrollPane);

        describeArea.setEditable(false);
        describeArea.setLineWrap(true);
        describeArea.setWrapStyleWord(true);
        describeArea.setMargin(new Insets(5, 5, 5, 5));
        final JScrollPane describeScrollPane = new JScrollPane(describeArea);
        describePanel = JaxoDialogs.newTitledBorderLayoutPanel(
                "JaxoPluginManagerPanel.describePanel.title");
        describePanel.add(describeScrollPane);

        pluginPanel =
                JaxoDialogs.newTitledBorderLayoutPanel(
                "JaxoPluginManagerPanel.pluginPanel.title");
        pluginPanel.add(namePanel, BorderLayout.WEST);
        pluginPanel.add(describePanel, BorderLayout.EAST);
    }

    private JPanel getButtonPanel() {
        installButton.setText(JaxoLanguage.translate(
                "JaxoPluginManagerPanel.buttonPanel.install"));
        installButton.addActionListener(new ActionListener() {
                public final void actionPerformed(final ActionEvent e) {
                    installButtonClicked();
                }
            });
        installButton.setEnabled(true);

        uninstallButton.setText(JaxoLanguage.translate(
                "JaxoPluginManagerPanel.buttonPanel.uninstall"));
        uninstallButton.addActionListener(new ActionListener() {
                public final void actionPerformed(final ActionEvent e) {
                    removeButtonClicked();
                }
            });
        uninstallButton.setEnabled(false);

        closeButton.setText(JaxoLanguage.translate("Close"));

        closeButton.addActionListener(new ActionListener() {
                public final void actionPerformed(final ActionEvent e) {
                    list.clearSelection();
                    setVisible(false);
                }
            });

        final JPanel buttonPanel = new JPanel();
        buttonPanel.add(uninstallButton);
        buttonPanel.add(Box.createHorizontalStrut(60));
        buttonPanel.add(installButton);
        buttonPanel.add(Box.createHorizontalStrut(20));
        buttonPanel.add(closeButton);

        return buttonPanel;
    }

    private void installButtonClicked() {
        final JaxoChooseFile c = new JaxoChooseFile(dialog);
        c.setApproveText(JaxoLanguage.translate("OK"));

        final String fileName =
            c.chooseFile(new String[]{"jar"}, "Java Archives", "").trim();

        if (fileName.length() == 0) {
            return;
        }

        final JaxoPluginLoader pluginLoader = new JaxoPluginLoader();
        final List<JaxoPlugin> loadedPlugins = pluginLoader.loadPluginsFromJar(fileName);

        if (loadedPlugins == null) {
            JOptionPane.showMessageDialog(
                    dialog,
                    JaxoLanguage.translate("JaxoPluginManagerPanel.error.loading.check.logs"),
                    JaxoLanguage.translate("JaxoPluginManagerPanel.install.error.title"),
                    JOptionPane.ERROR_MESSAGE);
            return;
        }

        for (final Iterator<JaxoPlugin> it = loadedPlugins.iterator(); it.hasNext();) {
            final JaxoPlugin loadedPlugin = it.next();
            JaxoPluginInfo.getPluginInfo().addPlugin(loadedPlugin);
            listModel.addElement(loadedPlugin.pluginName());
        }

        initListOfPlugins();

        final File jarFile = new File(fileName);
        final File destFile = new File(JaxoInfo.PLUGIN_DIR, jarFile.getName());

        InputStream in = null;
        OutputStream out = null;

        try {
            in = new FileInputStream(jarFile);
            out = new FileOutputStream(destFile);

            final byte[] buf = new byte[1024];
            int len;
            while ((len = in.read(buf)) > 0) {
                out.write(buf, 0, len);
            }
        } catch (IOException ex) {
            JaxoLog.debug("Could not install plugin in: "
                    + JaxoInfo.PLUGIN_DIR, ex);
            JOptionPane.showMessageDialog(
                    dialog,
                    JaxoLanguage.message("JaxoPluginManagerPanel.install.error%0",
                    JaxoInfo.PLUGIN_DIR),
                    JaxoLanguage.translate("JaxoPluginManagerPanel.install.error.title"),
                    JOptionPane.ERROR_MESSAGE);
        } finally {
            try {
                if (in != null) {
                    in.close();
                }
            } catch (IOException ex) {
                JaxoLog.debug(ex);
            }
            try {
                if (out != null) {
                    out.close();
                }
            } catch (IOException ex) {
                JaxoLog.debug(ex);
            }
        }
    }

    private void removeButtonClicked() {
        final int[] indices = list.getSelectedIndices();
        final JaxoPlugin[] toRemove = new JaxoPlugin[indices.length];
        final StringBuffer pluginNames = new StringBuffer(128);
        pluginNames.append(' ');

        for (int i = 0; i < indices.length; i++) {
            toRemove[i] = (JaxoPlugin) plugins.get(indices[i]);
            pluginNames.append(toRemove[i].pluginName());
            if (i != indices.length - 1) {
                pluginNames.append(", ");
            }
        }

        final int n = JOptionPane.showConfirmDialog(
                dialog,
                JaxoLanguage.translate("JaxoPluginManagerPanel.confirm.remove")
                + pluginNames.toString() + "?",
                JaxoLanguage.translate(
                "JaxoPluginManagerPanel.buttonPanel.uninstall"),
                JOptionPane.YES_NO_OPTION,
                JOptionPane.QUESTION_MESSAGE);

        if (n == JOptionPane.NO_OPTION) {
            return;
        }

        // list gets modified so we have to start from above
        for (int i = toRemove.length - 1; i >= 0; i--) {
            final File jar = JaxoPluginLoader.getPluginJar(toRemove[i], JaxoInfo.PLUGIN_DIR);

            if (jar == null) {
                // this can happen if one jar contains several plugins
                continue;
            }

            if (jar.delete()) {
                JaxoLog.debug("Deleted: " + jar.getAbsolutePath());
                JaxoLog.info("Successfully removed " + toRemove[i].pluginName());
            } else {
                JaxoLog.debug("Failed to delete: " + jar.getAbsolutePath());
                JOptionPane.showMessageDialog(
                        dialog,
                        JaxoLanguage.message("JaxoPluginManagerPanel.uninstall.error%0",
                            jar.getAbsolutePath()),
                        JaxoLanguage.translate("JaxoPluginManagerPanel.uninstall.error.title"),
                        JOptionPane.ERROR_MESSAGE);
            }
        }

        JaxoLog.info("Re-scanning plugin dir...");

        JaxoPluginInfo.getPluginInfo().reValidate();
        initListOfPlugins();
        listModel.clear();
        initListModel();
    }

    /** {@inheritDoc} */
    public void updateLanguage() {
        installButton.setText(JaxoLanguage.translate(
                "JaxoPluginManagerPanel.buttonPanel.install"));
        uninstallButton.setText(JaxoLanguage.translate(
                "JaxoPluginManagerPanel.buttonPanel.uninstall"));
        closeButton.setText(JaxoLanguage.translate("Close"));
        ((JaxoTitledBorder) pluginPanel.getBorder()).
                setTitle(JaxoLanguage.translate(
                "JaxoPluginManagerPanel.pluginPanel.title"));
        ((JaxoTitledBorder) describePanel.getBorder()).
                setTitle(JaxoLanguage.translate(
                "JaxoPluginManagerPanel.describePanel.title"));
        ((JaxoTitledBorder) namePanel.getBorder()).
                setTitle(JaxoLanguage.translate(
                "JaxoPluginManagerPanel.namePanel.title"));
    }

}
