/*
 * Copyright 2010-2015 Institut Pasteur.
 * 
 * This file is part of Icy.
 * 
 * Icy 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, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * Icy 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 Icy. If not, see <http://www.gnu.org/licenses/>.
 */
package icy.gui.preferences;

import icy.gui.dialog.ConfirmDialog;
import icy.plugin.PluginDescriptor;
import icy.plugin.PluginInstaller;
import icy.plugin.PluginInstaller.PluginInstallerListener;
import icy.plugin.PluginLoader;
import icy.plugin.PluginLoader.PluginLoaderEvent;
import icy.plugin.PluginLoader.PluginLoaderListener;
import icy.plugin.PluginRepositoryLoader;
import icy.plugin.PluginRepositoryLoader.PluginRepositoryLoaderListener;
import icy.plugin.PluginUpdater;

import java.util.ArrayList;
import java.util.List;

/**
 * @author Stephane
 */
public class PluginLocalPreferencePanel extends PluginListPreferencePanel implements PluginLoaderListener,
        PluginInstallerListener, PluginRepositoryLoaderListener
{
    private enum PluginLocalState
    {
        NULL, UPDATING, REMOVING, HAS_UPDATE, NO_UPDATE, CHECKING_UPDATE
    }

    /**
     * 
     */
    private static final long serialVersionUID = -6732331255351202922L;

    public static final String NODE_NAME = "Local Plugin";

    PluginLocalPreferencePanel(PreferenceFrame parent)
    {
        super(parent, NODE_NAME, PluginPreferencePanel.NODE_NAME);

        PluginRepositoryLoader.addListener(this);
        PluginLoader.addListener(this);
        PluginInstaller.addListener(this);

        // remove last column not used here
        table.removeColumn(table.getColumn(columnIds[4]));

        action1Button.setText("Delete");
        action1Button.setVisible(true);
        action2Button.setText("Check update");
        action2Button.setVisible(true);

        pluginsChanged();
    }

    @Override
    protected void closed()
    {
        super.closed();

        PluginRepositoryLoader.removeListener(this);
        PluginInstaller.removeListener(this);
        PluginLoader.removeListener(this);
    }

    private PluginLocalState getPluginLocalState(PluginDescriptor plugin)
    {
        if (plugin != null)
        {
            if (!PluginRepositoryLoader.isLoaded())
                return PluginLocalState.CHECKING_UPDATE;

            if ((PluginInstaller.isDesinstallingPlugin(plugin)))
                return PluginLocalState.REMOVING;

            // get online version
            final PluginDescriptor onlinePlugin = PluginUpdater.getUpdate(plugin);

            // update available ?
            if (onlinePlugin != null)
            {
                if (PluginInstaller.isInstallingPlugin(onlinePlugin))
                    return PluginLocalState.UPDATING;

                return PluginLocalState.HAS_UPDATE;
            }

            if (plugin.isInstalled())
                return PluginLocalState.NO_UPDATE;

            // here the plugin has just been removed but plugin list is not yet updated
            return PluginLocalState.REMOVING;
        }

        return PluginLocalState.NULL;
    }

    @Override
    protected void doAction1()
    {
        final List<PluginDescriptor> selectedPlugins = getSelectedPlugins();
        final List<PluginDescriptor> toRemove = new ArrayList<PluginDescriptor>();

        for (PluginDescriptor plugin : selectedPlugins)
            if (!PluginInstaller.isDesinstallingPlugin(plugin) && plugin.isInstalled())
                toRemove.add(plugin);

        // nothing to remove
        if (toRemove.isEmpty())
            return;

        // get dependants plugins
        final List<PluginDescriptor> dependants = PluginInstaller.getLocalDependenciesFrom(toRemove);
        // delete the one we plan to remove
        dependants.removeAll(toRemove);

        String message = "<html>";

        if (!dependants.isEmpty())
        {
            message = message + "The following plugin(s) won't work anymore :<br>";

            for (PluginDescriptor depPlug : dependants)
                message = message + depPlug.getName() + " " + depPlug.getVersion() + "<br>";

            message = message + "<br>";
        }

        message = message + "Are you sure you want to remove selected plugin(s) ?</html>";

        if (ConfirmDialog.confirm(message))
        {
            // remove plugins
            for (PluginDescriptor plugin : toRemove)
                PluginInstaller.desinstall(plugin, false, true);
        }

        // refresh state
        refreshTableData();
    }

    @Override
    protected void doAction2()
    {
        final List<PluginDescriptor> selectedPlugins = getSelectedPlugins();

        for (PluginDescriptor plugin : selectedPlugins)
        {
            final PluginDescriptor onlinePlugin = PluginUpdater.getUpdate(plugin);
            // install update
            if (onlinePlugin != null)
                PluginInstaller.install(onlinePlugin, true);
        }

        // refresh state
        refreshTableData();
    }

    @Override
    protected void repositoryChanged()
    {
        // do nothing here
    }

    @Override
    protected void reloadPlugins()
    {
        PluginLoader.reloadAsynch();
        // so we display the empty list during reload
        pluginsChanged();
    }

    @Override
    protected String getStateValue(PluginDescriptor plugin)
    {
        if (plugin == null)
            return "";

        switch (getPluginLocalState(plugin))
        {
            case REMOVING:
                return "removing...";

            case CHECKING_UPDATE:
                return "checking...";

            case UPDATING:
                return "updating...";

            case HAS_UPDATE:
                return "update available";

            case NO_UPDATE:
                return "";
        }

        return "";
    }

    @Override
    protected List<PluginDescriptor> getPlugins()
    {
        // loading...
        if (PluginLoader.isLoading())
            return new ArrayList<PluginDescriptor>();

        final List<PluginDescriptor> result = PluginLoader.getPlugins(false);

        // only display installed plugins (this hide inner or dev plugins)
        for (int i = result.size() - 1; i >= 0; i--)
            if (!result.get(i).isInstalled())
                result.remove(i);

        return result;
    }

    @Override
    protected void updateButtonsStateInternal()
    {
        super.updateButtonsStateInternal();

        final List<PluginDescriptor> selectedPlugins = getSelectedPlugins();
        final boolean selected = (selectedPlugins.size() > 0);

        if (PluginLoader.isLoading())
        {
            refreshButton.setText("Reloading...");
            refreshButton.setEnabled(false);
        }
        else
        {
            refreshButton.setText("Reload list");
            refreshButton.setEnabled(true);
        }

        if (!selected)
        {
            action1Button.setEnabled(false);
            action2Button.setEnabled(false);
            return;
        }

        boolean removing = true;
        for (PluginDescriptor plugin : selectedPlugins)
        {
            if (!PluginInstaller.isDesinstallingPlugin(plugin))
            {
                removing = false;
                break;
            }
        }

        // special case where plugins are currently begin removed
        if (removing)
        {
            action1Button.setText("Removing...");
            action1Button.setEnabled(false);
        }
        else
        {
            action1Button.setText("Remove");
            action1Button.setEnabled(true);
        }

        PluginLocalState state = PluginLocalState.NULL;

        for (PluginDescriptor plugin : selectedPlugins)
        {
            switch (getPluginLocalState(plugin))
            {
                case CHECKING_UPDATE:
                    if ((state == PluginLocalState.NULL) || (state == PluginLocalState.NO_UPDATE))
                        state = PluginLocalState.CHECKING_UPDATE;
                    break;

                case UPDATING:
                    if ((state == PluginLocalState.NULL) || (state == PluginLocalState.NO_UPDATE)
                            || (state == PluginLocalState.CHECKING_UPDATE))
                        state = PluginLocalState.UPDATING;
                    break;

                case HAS_UPDATE:
                    state = PluginLocalState.HAS_UPDATE;
                    break;

                case NO_UPDATE:
                    if (state == PluginLocalState.NULL)
                        state = PluginLocalState.NO_UPDATE;
                    break;
            }
        }

        switch (state)
        {
            case CHECKING_UPDATE:
                action1Button.setEnabled(false);
                action2Button.setText("Checking...");
                action2Button.setEnabled(false);
                break;

            case UPDATING:
                action1Button.setEnabled(false);
                action2Button.setText("Updating...");
                action2Button.setEnabled(false);
                break;

            case HAS_UPDATE:
                action2Button.setText("Update");
                action2Button.setEnabled(true);
                break;

            case NO_UPDATE:
                action2Button.setText("No update");
                action2Button.setEnabled(false);
                break;

            case NULL:
                action1Button.setEnabled(false);
                action2Button.setEnabled(false);
                break;
        }

        // keep delete button enabled only if we can actually delete the plugin
        if (action1Button.isEnabled())
        {
            boolean canDelete = false;
            for (PluginDescriptor plugin : selectedPlugins)
            {
                if (plugin.isInstalled())
                {
                    canDelete = true;
                    break;
                }
            }

            action1Button.setEnabled(canDelete);
        }
    }

    @Override
    public void pluginLoaderChanged(PluginLoaderEvent e)
    {
        pluginsChanged();
    }

    @Override
    public void pluginInstalled(PluginDescriptor plugin, boolean success)
    {
        updateButtonsState();
    }

    @Override
    public void pluginRemoved(PluginDescriptor plugin, boolean success)
    {
        updateButtonsState();
    }

    @Override
    public void pluginRepositeryLoaderChanged(PluginDescriptor plugin)
    {
        refreshTableData();
    }
}