package icy.gui.dialog;

import java.awt.Dimension;
import java.awt.Rectangle;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.util.List;

import javax.swing.JFileChooser;
import javax.swing.filechooser.FileFilter;

import icy.file.FileImporter;
import icy.file.FileUtil;
import icy.file.Loader;
import icy.file.SequenceFileImporter;
import icy.main.Icy;
import icy.preferences.ApplicationPreferences;
import icy.preferences.GeneralPreferences;
import icy.preferences.XMLPreferences;
import icy.type.collection.CollectionUtil;
import icy.util.StringUtil;

/**
 * Loader dialog used to load resource or image from the {@link FileImporter} or
 * {@link SequenceFileImporter}.
 * 
 * @author Stephane
 * @see Loader
 */
public class LoaderDialog extends JFileChooser implements PropertyChangeListener
{
    /**
     * 
     */
    private static final long serialVersionUID = 5162434537949723956L;

    public static class AllImagesFileFilter extends FileFilter
    {
        @Override
        public boolean accept(File file)
        {
            if (file.isDirectory())
                return true;

            return !Loader.canDiscardImageFile(file.getName());
        }

        @Override
        public String getDescription()
        {
            return "All images file";
        }
    }

    public static class AllFileFilter extends FileFilter
    {
        @Override
        public boolean accept(File file)
        {
            return file.exists();
        }

        @Override
        public String getDescription()
        {
            return "All files";
        }
    }

    public static AllImagesFileFilter allImagesFileFilter = new AllImagesFileFilter();
    public static AllFileFilter allFileFilter = new AllFileFilter();

    private static final String PREF_ID = "frame/imageLoader";

    private static final String ID_WIDTH = "width";
    private static final String ID_HEIGTH = "heigth";
    private static final String ID_SEPARATE = "separate";
    private static final String ID_AUTOORDER = "autoOrder";
    private static final String ID_EXTENSION = "extension";

    // GUI
    protected final LoaderOptionPanel optionPanel;
    protected final List<SequenceFileImporter> sequenceImporters;
    protected final List<FileImporter> fileImporters;

    /**
     * Display a dialog to select image or resource file(s) and load them.<br>
     * <br>
     * To only get selected files from the dialog you must do:<br>
     * <code> LoaderDialog dialog = new LoaderDialog(false);</code><br>
     * <code> File[] selectedFiles = dialog.getSelectedFiles()</code><br>
     * <br>
     * To directly load selected files just use:<br>
     * <code>new LoaderDialog(true);</code><br>
     * or<br>
     * <code>new LoaderDialog();</code>
     * 
     * @param defaultPath
     *        default file path (can be null)
     * @param region
     *        default XY region (can be null)
     * @param autoLoad
     *        If true the selected file(s) are automatically loaded.
     */
    public LoaderDialog(String defaultPath, Rectangle region, boolean autoLoad)
    {
        super();

        final XMLPreferences preferences = ApplicationPreferences.getPreferences().node(PREF_ID);
        sequenceImporters = Loader.getSequenceFileImporters();
        fileImporters = Loader.getFileImporters();

        // create option panel
        optionPanel = new LoaderOptionPanel(preferences.getBoolean(ID_SEPARATE, false),
                preferences.getBoolean(ID_AUTOORDER, true));

        // we can only store dimension for JFileChooser
        setPreferredSize(new Dimension(preferences.getInt(ID_WIDTH, 600), preferences.getInt(ID_HEIGTH, 400)));

        setMultiSelectionEnabled(true);
        setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);

        setAcceptAllFileFilterUsed(false);
        resetChoosableFileFilters();

        // add file filter from importers
        for (SequenceFileImporter importer : sequenceImporters)
        {
            final List<FileFilter> filters = importer.getFileFilters();

            if (filters != null)
            {
                for (FileFilter filter : filters)
                    addChoosableFileFilter(filter);
            }
        }
        for (FileImporter importer : fileImporters)
        {
            final List<FileFilter> filters = importer.getFileFilters();

            if (filters != null)
            {
                for (FileFilter filter : filters)
                    addChoosableFileFilter(filter);
            }
        }
        // add "all files" filter
        addChoosableFileFilter(allFileFilter);

        // we use a default path ?
        if (defaultPath != null)
        {
            // set all files filter
            setFileFilter(allFileFilter);

            final File file = new File(defaultPath);

            if (file.isDirectory())
                setCurrentDirectory(file);
            else
                setSelectedFile(file);
        }
        else
        {
            // set last used file filter
            setFileFilter(getFileFilter(preferences.get(ID_EXTENSION, allImagesFileFilter.getDescription())));
            // set last used directory
            setCurrentDirectory(new File(GeneralPreferences.getLoaderFolder()));
        }

        // setting GUI
        updateGUI();

        // we use a default path ?
        if (defaultPath != null)
        {
            // refresh preview
            optionPanel.updatePreview(defaultPath);
            updateOptionPanel();
        }

        // have a default XY region ?
        if (region != null)
            optionPanel.setXYRegion(region);

        // listen file filter change
        addPropertyChangeListener(this);

        // display loader
        final int value = showOpenDialog(Icy.getMainInterface().getMainFrame());

        // action confirmed ?
        if (value == JFileChooser.APPROVE_OPTION)
        {
            // store current path
            GeneralPreferences.setLoaderFolder(getCurrentDirectory().getAbsolutePath());
            preferences.putBoolean(ID_SEPARATE, isSeparateSequenceSelected());
            preferences.putBoolean(ID_AUTOORDER, isAutoOrderSelected());
            preferences.put(ID_EXTENSION, getFileFilter().getDescription());

            // load if requested
            if (autoLoad)
            {
                // get selected paths
                final List<String> paths = CollectionUtil.asList(FileUtil.toPaths(getSelectedFiles()));
                // first path
                final String firstPath = paths.get(0);
                // get the selected importer from file filter
                final Object importer = getImporter(getFileFilterIndex());

                // multi file or folder loading
                if ((paths.size() > 1) || FileUtil.isDirectory(firstPath))
                {
                    if (importer instanceof FileImporter)
                    {
                        // load selected non image file(s)
                        Loader.load((FileImporter) importer, paths, true);
                    }
                    else if (importer instanceof SequenceFileImporter)
                    {

                        // load selected image file(s)
                        Loader.load((SequenceFileImporter) importer, paths, isSeparateSequenceSelected(),
                                isAutoOrderSelected(), true);
                    }
                    else
                    {
                        // load selected file(s)
                        Loader.load(paths, isSeparateSequenceSelected(), isAutoOrderSelected(), true);
                    }
                }
                else
                {
                    // single file loading
                    if (importer instanceof FileImporter)
                    {
                        // load selected non image file
                        Loader.load((FileImporter) importer, paths, true);
                    }
                    else if (importer instanceof SequenceFileImporter)
                    {

                        // load selected image file with advanced option
                        Loader.load((SequenceFileImporter) importer, firstPath, -1, optionPanel.getResolutionLevel(),
                                optionPanel.getXYRegion(), optionPanel.getZMin(), optionPanel.getZMax(),
                                optionPanel.getTMin(), optionPanel.getTMax(), optionPanel.getChannel(), true, true);
                    }
                    else
                    {
                        // load selected file
                        Loader.load(null, firstPath, -1, optionPanel.getResolutionLevel(), optionPanel.getXYRegion(),
                                optionPanel.getZMin(), optionPanel.getZMax(), optionPanel.getTMin(),
                                optionPanel.getTMax(), optionPanel.getChannel(), true, true);
                    }
                }
            }
        }

        // store interface option
        preferences.putInt(ID_WIDTH, getWidth());
        preferences.putInt(ID_HEIGTH, getHeight());
    }

    /**
     * Display a dialog to select image or resource file(s) and load them.<br>
     * <br>
     * To only get selected files from the dialog you must do:<br>
     * <code> LoaderDialog dialog = new LoaderDialog(false);</code><br>
     * <code> File[] selectedFiles = dialog.getSelectedFiles()</code><br>
     * <br>
     * To directly load selected files just use:<br>
     * <code>new LoaderDialog(true);</code><br>
     * or<br>
     * <code>new LoaderDialog();</code>
     * 
     * @param autoLoad
     *        If true the selected file(s) are automatically loaded.
     */
    public LoaderDialog(boolean autoLoad)
    {
        this(null, null, autoLoad);
    }

    /**
     * Display a dialog to select file(s) and load them.
     */
    public LoaderDialog()
    {
        this(null, null, true);
    }

    protected FileFilter getFileFilter(String description)
    {
        final FileFilter[] filters = getChoosableFileFilters();

        for (FileFilter filter : filters)
            if (StringUtil.equals(filter.getDescription(), description))
                return filter;

        // take first filter by default
        if (filters.length > 0)
            return filters[0];

        return null;
    }

    protected int getFileFilterIndex()
    {
        final FileFilter[] filters = getChoosableFileFilters();
        final FileFilter filter = getFileFilter();

        for (int i = 0; i < filters.length; i++)
            if (filter == filters[i])
                return i;

        return -1;
    }

    protected boolean isImageFilter()
    {
        return getImporter(getFileFilterIndex()) instanceof SequenceFileImporter;
    }

    protected boolean isAllFileFilter()
    {
        return getFileFilter() == allFileFilter;
    }

    protected Object getImporter(int filterIndex)
    {
        int ind = 0;

        for (SequenceFileImporter importer : sequenceImporters)
        {
            final List<FileFilter> filters = importer.getFileFilters();
            final int count = (filters != null) ? filters.size() : 0;

            if (filterIndex < (ind + count))
                return importer;

            ind += count;
        }
        for (FileImporter importer : fileImporters)
        {
            final List<FileFilter> filters = importer.getFileFilters();
            final int count = (filters != null) ? filters.size() : 0;

            if (filterIndex < (ind + count))
                return importer;

            ind += count;
        }

        return null;
    }

    /**
     * Returns true if user checked the "Separate sequence" option.
     */
    public boolean isSeparateSequenceSelected()
    {
        return optionPanel.isSeparateSequenceSelected();
    }

    /**
     * Returns true if user checked the "Auto Ordering" option.
     */
    public boolean isAutoOrderSelected()
    {
        return optionPanel.isAutoOrderSelected();
    }

    /**
     * Get selected resolution level
     */
    public int getResolutionLevel()
    {
        return optionPanel.getResolutionLevel();
    }

    /**
     * Get selected XY region (when region loading is enabled)
     */
    public Rectangle getXYRegion()
    {
        return optionPanel.getXYRegion();
    }

    /**
     * Get minimum Z (Z range selection)
     */
    public int getZMin()
    {
        return optionPanel.getZMin();
    }

    /**
     * Get maximum Z (Z range selection)
     */
    public int getZMax()
    {
        return optionPanel.getZMax();
    }

    /**
     * Get minimum T (T range selection)
     */
    public int getTMin()
    {
        return optionPanel.getTMin();
    }

    /**
     * Get maximum T (T range selection)
     */
    public int getTMax()
    {
        return optionPanel.getTMax();
    }

    /**
     * Get channel selection (-1 for all)
     */
    public int getChannel()
    {
        return optionPanel.getChannel();
    }

    @Override
    public void propertyChange(PropertyChangeEvent evt)
    {
        final String prop = evt.getPropertyName();

        if (prop.equals(JFileChooser.FILE_FILTER_CHANGED_PROPERTY))
            updateGUI();
        else if (prop.equals(JFileChooser.SELECTED_FILE_CHANGED_PROPERTY))
        {
            File f = (File) evt.getNewValue();

            if ((f != null) && (f.isDirectory() || !f.exists()))
                f = null;

            // refresh preview
            if (f != null)
                optionPanel.updatePreview(f.getAbsolutePath());
            else
                optionPanel.updatePreview("");

            updateOptionPanel();
        }
        // closing ? --> do some final operation on option panel
        else if (prop.equals("JFileChooserDialogIsClosingProperty"))
            optionPanel.closingFromEDT();
        else
            updateOptionPanel();
    }

    protected void updateGUI()
    {
        if (isImageFilter() || isAllFileFilter())
        {
            setDialogTitle("Load image file(s)");
            setAccessory(optionPanel);
            updateOptionPanel();
            revalidate();
        }
        else
        {
            setDialogTitle("Load file(s)");
            setAccessory(null);
            revalidate();
        }
    }

    protected void updateOptionPanel()
    {
        final int numFile = getSelectedFiles().length;
        final boolean multi;

        if (numFile > 1)
            multi = true;
        else
        {
            final File file = getSelectedFile();
            multi = (file != null) && file.isDirectory();
        }

        optionPanel.setMultiFile(multi);
    }
}