/*
 * 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.component.IcyTable;
import icy.gui.component.IcyTextField;
import icy.gui.component.IcyTextField.TextChangeListener;
import icy.gui.util.ComponentUtil;
import icy.preferences.RepositoryPreferences;
import icy.preferences.RepositoryPreferences.RepositoryInfo;
import icy.system.thread.ThreadUtil;
import icy.util.StringUtil;
import icy.workspace.Workspace;

import java.awt.BorderLayout;
import java.awt.Dimension;
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.JComboBox;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.ListSelectionModel;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel;

/**
 * @author Stephane
 */
public abstract class WorkspaceListPreferencePanel extends PreferencePanel implements TextChangeListener,
        ListSelectionListener
{
    /**
     * 
     */
    private static final long serialVersionUID = -264986966623111155L;

    private static final String BOTTOM_MESS = "*Workspace modification need application restart to take effect";

    static final String[] columnNames = {"Name", "Description", "", "Enabled"};

    // protected final Preferences preferences;
    List<Workspace> workspaces;

    final AbstractTableModel tableModel;
    final JTable table;

    final JComboBox repository;
    final JPanel repositoryPanel;
    final IcyTextField filter;
    final JButton refreshButton;
    final JButton action1Button;

    private final Runnable buttonsStateUpdater;
    private final Runnable tableDataRefresher;
    private final Runnable workspaceListRefresher;
    private final Runnable repositoriesUpdater;

    final ActionListener repositoryActionListener;

    WorkspaceListPreferencePanel(PreferenceFrame parent, String nodeName)
    {
        super(parent, nodeName, WorkspacePreferencePanel.NODE_NAME);

        // preferences = Preferences.userRoot().node(preferencesId);
        workspaces = new ArrayList<Workspace>();

        buttonsStateUpdater = new Runnable()
        {
            @Override
            public void run()
            {
                // need to be done on EDT
                ThreadUtil.invokeNow(new Runnable()
                {
                    @Override
                    public void run()
                    {
                        updateButtonsStateInternal();
                    }
                });
            }
        };
        tableDataRefresher = new Runnable()
        {
            @Override
            public void run()
            {
                // need to be done on EDT
                ThreadUtil.invokeNow(new Runnable()
                {
                    @Override
                    public void run()
                    {
                        refreshTableDataInternal();
                    }
                });
            }
        };
        workspaceListRefresher = new Runnable()
        {
            @Override
            public void run()
            {
                refreshWorkspacesInternal();
            }
        };
        repositoriesUpdater = new Runnable()
        {
            @Override
            public void run()
            {
                // need to be done on EDT
                ThreadUtil.invokeNow(new Runnable()
                {
                    @Override
                    public void run()
                    {
                        updateRepositoriesInternal();
                    }
                });
            }
        };

        repositoryActionListener = new ActionListener()
        {
            @Override
            public void actionPerformed(ActionEvent e)
            {
                repositoryChanged();
            }
        };

        // build repository comboBox
        repository = new JComboBox();
        repository.setToolTipText("Select a repository");
        repository.addActionListener(repositoryActionListener);

        repositoryPanel = new JPanel();
        repositoryPanel.setLayout(new BoxLayout(repositoryPanel, BoxLayout.PAGE_AXIS));
        repositoryPanel.setVisible(false);

        final JPanel internalRepPanel = new JPanel();
        internalRepPanel.setLayout(new BoxLayout(internalRepPanel, BoxLayout.LINE_AXIS));

        internalRepPanel.add(new JLabel("Repository :"));
        internalRepPanel.add(Box.createHorizontalStrut(8));
        internalRepPanel.add(repository);
        internalRepPanel.add(Box.createHorizontalGlue());

        repositoryPanel.add(internalRepPanel);
        repositoryPanel.add(Box.createVerticalStrut(8));

        // need filter before load()
        filter = new IcyTextField();
        filter.addTextChangeListener(this);

        load();

        // build buttons panel
        final Dimension buttonsDim = new Dimension(100, 24);

        refreshButton = new JButton("Reload list");
        refreshButton.addActionListener(new ActionListener()
        {
            @Override
            public void actionPerformed(ActionEvent e)
            {
                reloadWorkspaces();
            }
        });
        ComponentUtil.setFixedSize(refreshButton, buttonsDim);

        action1Button = new JButton("null");
        action1Button.addActionListener(new ActionListener()
        {
            @Override
            public void actionPerformed(ActionEvent e)
            {
                doAction1(getSelectedWorkspace());
            }
        });
        action1Button.setVisible(false);
        ComponentUtil.setFixedSize(action1Button, buttonsDim);

        final JPanel buttonsPanel = new JPanel();
        buttonsPanel.setBorder(BorderFactory.createEmptyBorder(4, 8, 8, 8));
        buttonsPanel.setLayout(new BoxLayout(buttonsPanel, BoxLayout.PAGE_AXIS));

        buttonsPanel.add(refreshButton);
        buttonsPanel.add(Box.createVerticalStrut(64));
        buttonsPanel.add(action1Button);
        buttonsPanel.add(Box.createVerticalStrut(8));
        buttonsPanel.add(Box.createVerticalGlue());

        // build table
        tableModel = new AbstractTableModel()
        {
            /**
             * 
             */
            private static final long serialVersionUID = -8573364273165723214L;

            @Override
            public int getColumnCount()
            {
                return WorkspaceListPreferencePanel.this.getColumnCount();
            }

            @Override
            public String getColumnName(int column)
            {
                return columnNames[column];
            }

            @Override
            public int getRowCount()
            {
                return workspaces.size();
            }

            @Override
            public Object getValueAt(int row, int column)
            {
                final Workspace workspace = workspaces.get(row);

                switch (column)
                {
                    case 0:
                        return workspace.getName();

                    case 1:
                        return workspace.getDescription();

                    case 2:
                        return getStateValue(workspace);

                    case 3:
                        return isWorkspaceEnable(workspace);
                }

                return "";
            }

            @Override
            public boolean isCellEditable(int row, int column)
            {
                return (column == 3);
            }

            @Override
            public Class<?> getColumnClass(int columnIndex)
            {
                if (columnIndex == 3)
                    return Boolean.class;

                return String.class;
            }

            @Override
            public void setValueAt(Object aValue, int rowIndex, int columnIndex)
            {
                final Workspace workspace = workspaces.get(rowIndex);

                if ((columnIndex == 3) && (aValue instanceof Boolean))
                    setWorkspaceEnable(workspace, (Boolean) aValue);

                // workspace setting changed, restart needed
                getPreferenceFrame().setNeedRestart();
            }
        };

        table = new IcyTable(tableModel);

        final TableColumnModel colModel = table.getColumnModel();
        TableColumn col;

        // columns setting
        col = colModel.getColumn(0);
        col.setMinWidth(80);
        col.setPreferredWidth(100);
        col.setMaxWidth(120);

        col = colModel.getColumn(1);
        col.setMinWidth(160);
        col.setPreferredWidth(240);
        col.setMaxWidth(500);

        col = colModel.getColumn(2);
        col.setMinWidth(60);
        col.setPreferredWidth(70);
        col.setMaxWidth(80);

        if (colModel.getColumnCount() > 3)
        {
            col = colModel.getColumn(3);
            col.setMinWidth(60);
            col.setPreferredWidth(60);
            col.setMaxWidth(60);
        }

        table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
        table.getSelectionModel().addListSelectionListener(this);
        table.setColumnSelectionAllowed(false);
        table.setRowSelectionAllowed(true);
        table.setRowHeight(24);
        table.setShowVerticalLines(false);
        table.setAutoCreateRowSorter(true);
        table.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);

        final JPanel tableTopPanel = new JPanel();

        tableTopPanel.setLayout(new BoxLayout(tableTopPanel, BoxLayout.PAGE_AXIS));

        tableTopPanel.add(Box.createVerticalStrut(2));
        tableTopPanel.add(repositoryPanel);
        tableTopPanel.add(filter);
        tableTopPanel.add(Box.createVerticalStrut(8));
        tableTopPanel.add(table.getTableHeader());

        final JPanel tablePanel = new JPanel();

        tablePanel.setLayout(new BorderLayout());

        tablePanel.add(tableTopPanel, BorderLayout.NORTH);
        tablePanel.add(new JScrollPane(table), BorderLayout.CENTER);

        mainPanel.setLayout(new BorderLayout());

        mainPanel.add(tablePanel, BorderLayout.CENTER);
        mainPanel.add(buttonsPanel, BorderLayout.EAST);
        mainPanel.add(new JLabel(BOTTOM_MESS), BorderLayout.SOUTH);

        mainPanel.validate();
    }

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

        // do not retains workspaces when frame is closed
        workspaces.clear();
    }

    private List<Workspace> filterList(List<Workspace> list, String filter)
    {
        final List<Workspace> result = new ArrayList<Workspace>();
        final boolean empty = StringUtil.isEmpty(filter, true);
        final String filterUp;

        if (!empty)
            filterUp = filter.toUpperCase();
        else
            filterUp = "";

        for (Workspace workspace : list)
        {
            final String name = workspace.getName().toUpperCase();
            final String desc = workspace.getDescription().toUpperCase();

            // search in name and description
            if (empty || (name.indexOf(filterUp) != -1) || (desc.indexOf(filterUp) != -1))
                result.add(workspace);
        }

        return result;
    }

    protected abstract void doAction1(Workspace workspace);

    protected abstract void repositoryChanged();

    protected abstract void reloadWorkspaces();

    protected abstract String getStateValue(Workspace workspace);

    protected abstract int getColumnCount();

    protected abstract List<Workspace> getWorkspaces();

    protected abstract void updateButtonsStateInternal();

    protected final void updateButtonsState()
    {
        ThreadUtil.runSingle(buttonsStateUpdater);
    }

    protected void updateRepositoriesInternal()
    {
        // final RepositoryPreferencePanel panel = (RepositoryPreferencePanel)
        // getPreferencePanel(RepositoryPreferencePanel.class);
        // // refresh repositories list (use list from GUI)
        // final ArrayList<RepositoryInfo> repositeries = panel.repositories;

        // refresh repositories list
        final ArrayList<RepositoryInfo> repositeries = RepositoryPreferences.getRepositeries();

        final RepositoryInfo savedRepository = (RepositoryInfo) repository.getSelectedItem();

        // needed to disable events during update time
        repository.removeActionListener(repositoryActionListener);

        repository.removeAllItems();
        for (RepositoryInfo repos : repositeries)
            if (repos.isEnabled())
                repository.addItem(repos);
        repository.setEnabled(true);

        repository.addActionListener(repositoryActionListener);

        boolean selected = false;

        // try to set back the old selected repository
        if (savedRepository != null)
        {
            final String repositoryName = savedRepository.getName();

            for (int ind = 0; ind < repository.getItemCount(); ind++)
            {
                final RepositoryInfo repo = (RepositoryInfo) repository.getItemAt(ind);

                if ((repo != null) && (repo.getName().equals(repositoryName)))
                {
                    repository.setSelectedIndex(ind);
                    selected = true;
                    break;
                }
            }
        }

        // manually launch the action
        if (!selected)
            repository.setSelectedIndex((repository.getItemCount() > 0) ? 0 : -1);

        // avoid automatic minimum size here
        repository.setMinimumSize(new Dimension(48, 18));
    }

    protected final void updateRepositories()
    {
        ThreadUtil.runSingle(repositoriesUpdater);
    }

    protected Boolean isWorkspaceEnable(Workspace workspace)
    {
        return Boolean.FALSE;
    }

    protected void setWorkspaceEnable(Workspace workspace, Boolean value)
    {
        // ignore
    }

    protected void refreshWorkspacesInternal()
    {
        workspaces = filterList(getWorkspaces(), filter.getText());
        // refresh table data
        refreshTableData();
    }

    protected final void refreshWorkspaces()
    {
        ThreadUtil.runSingle(workspaceListRefresher);
    }

    protected int getWorkspaceIndex(Workspace workspace)
    {
        return workspaces.indexOf(workspace);
    }

    protected int getWorkspaceModelIndex(Workspace workspace)
    {
        return getWorkspaceIndex(workspace);
    }

    protected int getWorkspaceTableIndex(Workspace workspace)
    {
        final int ind = getWorkspaceModelIndex(workspace);

        if (ind == -1)
            return ind;

        try
        {

            return table.convertRowIndexToView(ind);
        }
        catch (IndexOutOfBoundsException e)
        {
            return -1;
        }
    }

    Workspace getSelectedWorkspace()
    {
        int index;

        index = table.getSelectedRow();
        if (index == -1)
            return null;

        try
        {
            index = table.convertRowIndexToModel(index);
        }
        catch (IndexOutOfBoundsException e)
        {
            index = -1;
        }

        if ((index < 0) || (index >= workspaces.size()))
            return null;

        return workspaces.get(index);
    }

    void setSelectedWorkspace(Workspace workspace)
    {
        final int index = getWorkspaceTableIndex(workspace);

        if (index > -1)
        {
            table.clearSelection();
            table.getSelectionModel().setSelectionInterval(index, index);
        }
    }

    @Override
    protected void load()
    {

    }

    @Override
    protected void save()
    {
        // reload repositories as some parameter as beta flag can have changed
        updateRepositories();
    }

    protected void refreshTableDataInternal()
    {
        final Workspace workspace = getSelectedWorkspace();

        tableModel.fireTableDataChanged();

        // restore previous selected workspace if possible
        setSelectedWorkspace(workspace);

        // update buttons state
        updateButtonsState();
    }

    protected final void refreshTableData()
    {
        ThreadUtil.runSingle(tableDataRefresher);
    }

    protected void workspacesChanged()
    {
        refreshWorkspaces();
    }

    @Override
    public void textChanged(IcyTextField source, boolean validate)
    {
        workspacesChanged();
    }

    @Override
    public void valueChanged(ListSelectionEvent e)
    {
        updateButtonsState();
    }
}