/*
 * 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.gui.frame.IcyFrame;
import icy.gui.util.ComponentUtil;
import icy.gui.util.WindowPositionSaver;
import icy.main.Icy;
import icy.preferences.IcyPreferences;
import icy.util.StringUtil;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;

import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSeparator;
import javax.swing.JSplitPane;
import javax.swing.JTree;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.TreeSelectionModel;

/**
 * @author fab & stephane
 */
public class PreferenceFrame extends IcyFrame implements TreeSelectionListener
{
    public static final String NODE_NAME = "Preferences";

    /** singleton frame */
    private static PreferenceFrame instance = null;

    // needed to keep trace of non singleton frame
    List<PreferencePanel> preferencePanels;

    private JTree tree;
    private JSplitPane mainPanel;
    private DefaultMutableTreeNode node;

    boolean needRestart;
    boolean doRestart;

    // we need to keep reference on it as the object only use weak reference
    WindowPositionSaver positionSaver;

    public PreferenceFrame(String section)
    {
        super(NODE_NAME, true, true, false, false);

        // frame already opened ?
        if (instance != null)
        {
            // just put it on front
            instance.toFront();
            return;
        }
        instance = this;

        positionSaver = new WindowPositionSaver(this, "frame/preference", new Point(100, 100), new Dimension(640, 480));

        needRestart = false;
        doRestart = false;

        preferencePanels = new ArrayList<PreferencePanel>();

        preferencePanels.add(new GeneralPreferencePanel(this));
        preferencePanels.add(new NetworkPreferencePanel(this));
        preferencePanels.add(new CanvasPreferencePanel(this));
        preferencePanels.add(new ChatPreferencePanel(this));
        preferencePanels.add(new RepositoryPreferencePanel(this));
        preferencePanels.add(new PluginPreferencePanel(this));
        preferencePanels.add(new PluginLocalPreferencePanel(this));
        preferencePanels.add(new PluginOnlinePreferencePanel(this));
        preferencePanels.add(new PluginStartupPreferencePanel(this));
        preferencePanels.add(new WorkspacePreferencePanel(this));
        preferencePanels.add(new WorkspaceLocalPreferencePanel(this));
        preferencePanels.add(new WorkspaceOnlinePreferencePanel(this));
        // TODO : add here PreferencePanel type plugins

        // build selection tree
        node = new DefaultMutableTreeNode(NODE_NAME);

        for (PreferencePanel panel : preferencePanels)
            getNode(panel.getParentName()).add(panel.getNode());

        tree = new JTree(node);

        tree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
        // tree.setOpaque(true);
        tree.setRootVisible(false);
        // select first node
        tree.setSelectionRow(0);
        ComponentUtil.expandAllTree(tree);
        // disable double click expansion/reduction
        tree.setToggleClickCount(0);
        tree.addTreeSelectionListener(this);

        final JScrollPane treeScrollPane = new JScrollPane(tree);
        treeScrollPane.setMinimumSize(new Dimension(136, 100));

        mainPanel = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, treeScrollPane, null);
        mainPanel.setDividerSize(6);
        mainPanel.setDividerLocation(150);

        // final JButton helpButton = new Help(NODE_NAME).getHelpButton("Help");
        final JButton resetButton = new JButton("Reset to default");
        final JButton cancelButton = new JButton("Cancel");
        final JButton okButton = new JButton("Ok");
        final JButton applyButton = new JButton("Apply");

        resetButton.addActionListener(new ActionListener()
        {
            @Override
            public void actionPerformed(ActionEvent e)
            {
                // need confirmation
                if (ConfirmDialog.confirm("Reset all setting to default values ?"))
                {
                    // clear all preferences
                    IcyPreferences.clear();
                    // reload
                    for (PreferencePanel panel : preferencePanels)
                        panel.load();
                }
            }
        });

        cancelButton.addActionListener(new ActionListener()
        {
            @Override
            public void actionPerformed(ActionEvent e)
            {
                // just close the frame
                close();
            }
        });

        okButton.addActionListener(new ActionListener()
        {
            @Override
            public void actionPerformed(ActionEvent e)
            {
                // set back panel informations in preferences
                for (PreferencePanel panel : preferencePanels)
                    panel.save();
                // validate the need restart state
                doRestart = needRestart;
                // then close the frame
                close();
            }
        });

        applyButton.addActionListener(new ActionListener()
        {
            @Override
            public void actionPerformed(ActionEvent e)
            {
                // set back panel informations in preferences
                for (PreferencePanel panel : preferencePanels)
                    panel.save();
                // validate the need restart state
                doRestart = needRestart;
            }
        });

        final JPanel bottomPanel = new JPanel();

        bottomPanel.setBorder(BorderFactory.createEmptyBorder(8, 8, 8, 8));
        bottomPanel.setLayout(new BoxLayout(bottomPanel, BoxLayout.LINE_AXIS));

        // bottomPanel.add(helpButton);
        // bottomPanel.add(Box.createHorizontalStrut(8));
        bottomPanel.add(resetButton);
        bottomPanel.add(Box.createHorizontalStrut(8));
        bottomPanel.add(Box.createHorizontalGlue());
        bottomPanel.add(applyButton);
        bottomPanel.add(Box.createHorizontalStrut(8));
        bottomPanel.add(okButton);
        bottomPanel.add(Box.createHorizontalStrut(8));
        bottomPanel.add(cancelButton);

        final JPanel tmpPanel = new JPanel();
        tmpPanel.setLayout(new BoxLayout(tmpPanel, BoxLayout.PAGE_AXIS));
        tmpPanel.add(new JSeparator());
        tmpPanel.add(bottomPanel);

        setLayout(new BorderLayout());

        add(mainPanel, BorderLayout.CENTER);
        add(tmpPanel, BorderLayout.SOUTH);

        updateRightPanel();

        setMinimumSize(new Dimension(480, 400));
        setPreferredSize(new Dimension(800, 600));

        addToDesktopPane();
        setVisible(true);

        if (!StringUtil.isEmpty(section))
            setSelection(section);
    }

    @Override
    public void onClosed()
    {
        // test if we are on singleton frame
        if (preferencePanels != null)
        {
            for (PreferencePanel panel : preferencePanels)
                panel.closed();

            instance = null;

            // restart needed and validated, ask user
            if (doRestart)
            {
                Icy.confirmRestart();
                doRestart = false;
            }
        }

        super.onClosed();
    }

    private DefaultMutableTreeNode getNode(String name)
    {
        // first search directly on root node
        if (node.getUserObject().equals(name))
            return node;

        // then search in children
        final int len = node.getChildCount();
        for (int i = 0; i < len; i++)
        {
            final DefaultMutableTreeNode n = (DefaultMutableTreeNode) node.getChildAt(i);

            if (n.getUserObject().equals(name))
                return n;
        }

        return null;
    }

    private void updateRightPanel()
    {
        final Object selected = tree.getSelectionPath().getLastPathComponent();

        for (PreferencePanel panel : preferencePanels)
        {
            if (panel.getNode().equals(selected))
            {
                final int divLocation = mainPanel.getDividerLocation();

                panel.setVisible(true);
                mainPanel.setRightComponent(panel);
                // restore divider location (lost with setRightComponent)
                mainPanel.setDividerLocation(divLocation);
            }
            else
                panel.setVisible(true);
        }
    }

    @Override
    public void valueChanged(TreeSelectionEvent e)
    {
        updateRightPanel();
    }

    public void setSelection(String selection)
    {
        for (PreferencePanel panel : preferencePanels)
        {
            final DefaultMutableTreeNode node = panel.getNode();

            if (node.getUserObject().equals(selection))
            {
                tree.setSelectionPath(ComponentUtil.buildTreePath(node));
                return;
            }
        }
    }

    public void setNeedRestart()
    {
        needRestart = true;
    }
}