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

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Timer;
import java.util.TimerTask;

import javax.swing.Box;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.ScrollPaneConstants;

import icy.common.exception.UnsupportedFormatException;
import icy.file.Loader;
import icy.file.SequenceFileImporter;
import icy.gui.component.ThumbnailComponent;
import icy.gui.util.ComponentUtil;
import icy.image.IcyBufferedImage;
import icy.image.IcyBufferedImageUtil;
import icy.main.Icy;
import icy.resource.ResourceUtil;
import icy.sequence.MetaDataUtil;
import icy.util.OMEUtil;
import loci.formats.IFormatReader;
import loci.formats.meta.IMetadata;
import loci.formats.ome.OMEXMLMetadata;
import loci.formats.ome.OMEXMLMetadataImpl;

/**
 * Dialog used to select which serie to open for multi serie image.
 * 
 * @author Stephane
 */
public class SeriesSelectionDialog extends ActionDialog implements Runnable
{
    /**
     * 
     */
    private static final long serialVersionUID = -2133845128887305016L;

    protected static final int NUM_COL = 4;
    protected static final int THUMB_X = 160;
    protected static final int THUMB_Y = 140;

    // GUI
    protected JScrollPane scrollPane;
    protected JPanel gridPanel;
    protected ThumbnailComponent[] serieComponents;
    protected JButton selectAllBtn;
    protected JButton unselectAllBtn;

    // internal
    protected IFormatReader reader;
    protected SequenceFileImporter importer;
    protected String id;
    protected OMEXMLMetadata metadata;
    protected int[] selectedSeries;
    protected final MouseAdapter serieDoubleClickAction;
    protected final Thread loadingThread;

    /**
     * @deprecated Use {@link #SeriesSelectionDialog(SequenceFileImporter, String)} instead.
     */
    @Deprecated
    public SeriesSelectionDialog(IFormatReader reader)
    {
        super("Series selection", null, Icy.getMainInterface().getMainFrame());

        this.reader = reader;
        // default is empty
        selectedSeries = new int[] {};

        initialize();

        // double cick action = direct selection
        serieDoubleClickAction = new MouseAdapter()
        {
            @Override
            public void mouseClicked(MouseEvent e)
            {
                if (e.getClickCount() == 2)
                {
                    final ThumbnailComponent thumb = (ThumbnailComponent) e.getSource();

                    for (int i = 0; i < serieComponents.length; i++)
                    {
                        if (serieComponents[i] == thumb)
                        {
                            selectedSeries = new int[] {i};
                            dispose();
                        }
                    }
                }
            }
        };

        final int series;

        if (reader != null)
        {
            metadata = OMEUtil.getOMEMetadata((IMetadata) reader.getMetadataStore());
            series = reader.getSeriesCount();
        }
        else
        {
            metadata = null;
            series = 0;
        }

        serieComponents = new ThumbnailComponent[series];

        // adjust number of row
        int numRow = series / NUM_COL;
        if (series > (NUM_COL * numRow))
            numRow++;

        ((GridLayout) gridPanel.getLayout()).setRows(numRow);

        for (int i = 0; i < numRow; i++)
        {
            for (int j = 0; j < NUM_COL; j++)
            {
                final int index = (i * NUM_COL) + j;

                if (index < series)
                {
                    final ThumbnailComponent thumb = new ThumbnailComponent(true);

                    // add mouse listener (double click action)
                    thumb.addMouseListener(serieDoubleClickAction);

                    // remove mouse listener (double click action)
                    if (serieComponents[index] != null)
                        serieComponents[index].removeMouseListener(serieDoubleClickAction);

                    serieComponents[index] = thumb;
                    thumb.setEnabled(true);
                    thumb.setTitle("loading...");
                    thumb.setInfos("");
                    thumb.setInfos2("");
                    gridPanel.add(thumb);
                }
                else
                    gridPanel.add(Box.createGlue());
            }
        }

        // load thumbnails...
        loadingThread = new Thread(this, "Series thumbnail loading");
        loadingThread.start();

        // action on "OK"
        setOkAction(new ActionListener()
        {
            @Override
            public void actionPerformed(ActionEvent e)
            {
                int numSelected = 0;
                for (int i = 0; i < serieComponents.length; i++)
                    if (serieComponents[i].isSelected())
                        numSelected++;

                selectedSeries = new int[numSelected];

                int ind = 0;
                for (int i = 0; i < serieComponents.length; i++)
                    if (serieComponents[i].isSelected())
                        selectedSeries[ind++] = i;
            }
        });

        // action on "Select All"
        selectAllBtn.addActionListener(new ActionListener()
        {
            @Override
            public void actionPerformed(ActionEvent e)
            {
                for (ThumbnailComponent thumb : serieComponents)
                    thumb.setSelected(true);
            }
        });

        // action on "Unselect All"
        unselectAllBtn.addActionListener(new ActionListener()
        {
            @Override
            public void actionPerformed(ActionEvent e)
            {
                for (ThumbnailComponent thumb : serieComponents)
                    thumb.setSelected(false);
            }
        });

        setPreferredSize(new Dimension(740, 520));
        pack();
        ComponentUtil.center(this);
        setVisible(true);
    }

    /**
     * Create a new dialog to select the series to open from an image.
     * 
     * @throws IOException
     * @throws UnsupportedFormatException
     */
    public SeriesSelectionDialog(SequenceFileImporter importer, String id, OMEXMLMetadata metadata)
            throws UnsupportedFormatException, IOException
    {
        super("Series selection", null, Icy.getMainInterface().getMainFrame());

        this.importer = importer;
        this.id = id;
        this.metadata = metadata;
        // default is empty
        selectedSeries = new int[] {};

        initialize();

        final int series = MetaDataUtil.getNumSerie(metadata);

        // double cick action = direct selection
        serieDoubleClickAction = new MouseAdapter()
        {
            @Override
            public void mouseClicked(MouseEvent e)
            {
                if (e.getClickCount() == 2)
                {
                    final ThumbnailComponent thumb = (ThumbnailComponent) e.getSource();

                    for (int i = 0; i < serieComponents.length; i++)
                    {
                        if (serieComponents[i] == thumb)
                        {
                            selectedSeries = new int[] {i};
                            dispose();
                        }
                    }
                }
            }
        };

        serieComponents = new ThumbnailComponent[series];

        // adjust number of row
        int numRow = series / NUM_COL;
        if (series > (NUM_COL * numRow))
            numRow++;

        ((GridLayout) gridPanel.getLayout()).setRows(numRow);

        for (int i = 0; i < numRow; i++)
        {
            for (int j = 0; j < NUM_COL; j++)
            {
                final int index = (i * NUM_COL) + j;

                if (index < series)
                {
                    final ThumbnailComponent thumb = new ThumbnailComponent(true);

                    // add mouse listener (double click action)
                    thumb.addMouseListener(serieDoubleClickAction);

                    // remove mouse listener (double click action)
                    if (serieComponents[index] != null)
                        serieComponents[index].removeMouseListener(serieDoubleClickAction);

                    serieComponents[index] = thumb;
                    thumb.setEnabled(true);
                    thumb.setTitle("loading...");
                    thumb.setInfos("");
                    thumb.setInfos2("");
                    gridPanel.add(thumb);
                }
                else
                    gridPanel.add(Box.createGlue());
            }
        }

        // load thumbnails...
        loadingThread = new Thread(this, "Series thumbnail loading");
        loadingThread.start();

        // action on "OK"
        setOkAction(new ActionListener()
        {
            @Override
            public void actionPerformed(ActionEvent e)
            {
                int numSelected = 0;
                for (int i = 0; i < serieComponents.length; i++)
                    if (serieComponents[i].isSelected())
                        numSelected++;

                selectedSeries = new int[numSelected];

                int ind = 0;
                for (int i = 0; i < serieComponents.length; i++)
                    if (serieComponents[i].isSelected())
                        selectedSeries[ind++] = i;
            }
        });

        // action on "Select All"
        selectAllBtn.addActionListener(new ActionListener()
        {
            @Override
            public void actionPerformed(ActionEvent e)
            {
                for (ThumbnailComponent thumb : serieComponents)
                    thumb.setSelected(true);
            }
        });

        // action on "Unselect All"
        unselectAllBtn.addActionListener(new ActionListener()
        {
            @Override
            public void actionPerformed(ActionEvent e)
            {
                for (ThumbnailComponent thumb : serieComponents)
                    thumb.setSelected(false);
            }
        });

        setPreferredSize(new Dimension(740, 520));
        pack();
        ComponentUtil.center(SeriesSelectionDialog.this);
        setVisible(true);
    }

    /**
     * @deprecated Use {@link #SeriesSelectionDialog(SequenceFileImporter, String, OMEXMLMetadata)} instead.
     */
    @Deprecated
    public SeriesSelectionDialog(SequenceFileImporter importer, String id, OMEXMLMetadataImpl metadata)
            throws UnsupportedFormatException, IOException
    {
        this(importer, id, (OMEXMLMetadata) metadata);
    }

    /**
     * Create a new dialog to select the series to open from an image.
     * 
     * @throws IOException
     * @throws UnsupportedFormatException
     */
    public SeriesSelectionDialog(SequenceFileImporter importer, String id)
            throws UnsupportedFormatException, IOException
    {
        this(importer, id, Loader.getMetaData(importer, id));
    }

    /**
     * @return the selectedSeries
     */
    public int[] getSelectedSeries()
    {
        return selectedSeries;
    }

    void initialize()
    {
        JPanel panel = new JPanel();
        getContentPane().add(panel, BorderLayout.NORTH);
        panel.setLayout(new FlowLayout(FlowLayout.CENTER, 5, 5));

        JLabel lblSelect = new JLabel("Click on a serie to select / unselect it or double click to directly open it.");
        ComponentUtil.setFontBold(lblSelect);
        ComponentUtil.setFontSize(lblSelect, 12);
        panel.add(lblSelect);

        scrollPane = new JScrollPane(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS,
                ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
        mainPanel.add(scrollPane, BorderLayout.CENTER);

        gridPanel = new JPanel();
        scrollPane.setViewportView(gridPanel);
        gridPanel.setLayout(new GridLayout(2, NUM_COL, 0, 0));

        buttonPanel.removeAll();

        selectAllBtn = new JButton("Select all");
        unselectAllBtn = new JButton("Unselect all");

        buttonPanel.add(Box.createHorizontalStrut(4));
        buttonPanel.add(selectAllBtn);
        buttonPanel.add(Box.createHorizontalStrut(8));
        buttonPanel.add(unselectAllBtn);
        buttonPanel.add(Box.createHorizontalStrut(8));
        buttonPanel.add(Box.createHorizontalGlue());
        buttonPanel.add(Box.createHorizontalStrut(8));
        buttonPanel.add(okBtn);
        buttonPanel.add(Box.createHorizontalStrut(8));
        buttonPanel.add(cancelBtn);
        buttonPanel.add(Box.createHorizontalStrut(4));
    }

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

        // kill loading task in 2 seconds
        new Timer().schedule(new TimerTask()
        {
            @SuppressWarnings("deprecation")
            @Override
            public void run()
            {
                if ((loadingThread != null) && loadingThread.isAlive())
                {
                    try
                    {
                        loadingThread.stop();
                    }
                    catch (Throwable t)
                    {
                        // ignore
                    }
                }
            }
        }, 2000);
    }

    @Override
    public void run()
    {
        try
        {
            // start by filling metadata only...
            for (int i = 0; i < serieComponents.length; i++)
            {
                // interrupt
                if (isClosed())
                    return;

                try
                {
                    final int sizeC = MetaDataUtil.getSizeC(metadata, i);

                    serieComponents[i].setTitle(metadata.getImageName(i));
                    serieComponents[i].setInfos(MetaDataUtil.getSizeX(metadata, i) + " x "
                            + MetaDataUtil.getSizeY(metadata, i) + " - " + MetaDataUtil.getSizeZ(metadata, i) + "Z x "
                            + MetaDataUtil.getSizeT(metadata, i) + "T");
                    serieComponents[i].setInfos2(sizeC + ((sizeC > 1) ? " channels (" : " channel (")
                            + MetaDataUtil.getDataType(metadata, i) + ")");
                }
                catch (Exception e)
                {
                    serieComponents[i].setTitle("Cannot read file");
                    serieComponents[i].setInfos("");
                    serieComponents[i].setInfos2("");
                }

                try
                {
                    // why does this sometime fails ???
                    serieComponents[i].setImage(ResourceUtil.ICON_PICTURE);
                }
                catch (Exception e)
                {
                    // ignore
                }
            }

            // then try to load thumbnail
            for (int i = 0; i < serieComponents.length; i++)
            {
                // interrupt
                if (isClosed())
                    return;

                try
                {
                    if (importer.open(id, 0))
                    {
                        try
                        {
                            final IcyBufferedImage img = importer.getThumbnail(i);
                            serieComponents[i]
                                    .setImage(IcyBufferedImageUtil.toBufferedImage(img, BufferedImage.TYPE_INT_ARGB));
                        }
                        finally
                        {
                            importer.close();
                        }
                    }
                    else
                        serieComponents[i].setImage(ResourceUtil.ICON_DELETE);
                }
                catch (OutOfMemoryError e)
                {
                    // error image, we just totally ignore error here...
                    serieComponents[i].setImage(ResourceUtil.ICON_DELETE);
                }
                catch (Exception e)
                {
                    // error image, we just totally ignore error here...
                    serieComponents[i].setImage(ResourceUtil.ICON_DELETE);
                }
            }
        }
        catch (ThreadDeath t)
        {
            // just stop process...
        }
    }
}