/*********************************************************************************************
 *
 * 'JFileDataStoreChooser.java, in plugin ummisco.gama.ui.viewers, is part of the source code of the
 * GAMA modeling and simulation platform.
 * (c) 2007-2016 UMI 209 UMMISCO IRD/UPMC & Partners
 *
 * Visit https://github.com/gama-platform/gama for license information and developers contact.
 * 
 *
 **********************************************************************************************/
package ummisco.gama.ui.viewers.gis.geotools.control;

import java.io.File;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.TreeMap;

import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.FileDialog;
import org.eclipse.swt.widgets.Shell;
import org.geotools.data.FileDataStoreFactorySpi;
import org.geotools.data.FileDataStoreFinder;

/**
 * A file chooser dialog to get user choices for data stores.
 * <p>
 * Examples of use:
 * <pre>{@code
 * // prompt the user for a shapefile
 * File file = JFileDataStoreChooser.showOpenFile("shp", parentFrame);
 * if (file != null) {
 *    ...
 * }
 *
 * // prompt the user for a given data format
 *
 * }</pre>
 *
 * @author Jody Garnett
 * @author Andrea Antonello (www.hydrologis.com)
 *
 *
 *
 * @source $URL$
 */
public class JFileDataStoreChooser {
    private static final long serialVersionUID = -7482109609487216939L;
    private FileDialog fileDialog;

    /**
     * Create a dialog that filters for files with the specified extension.
     *
     * @param extension the file extension, with or without the leading '.'
     */
    public JFileDataStoreChooser( Shell parent, int style, final String extension ) {
        this(parent, style, new String[]{extension});
    }

    static Map<String, String> associations( List<String> extensions ) {
        Map<String, String> fileAssociations = new TreeMap<String, String>();

        for( String extension : extensions ) {
            String ext = extension.toLowerCase().trim();
            if (!ext.startsWith("*.")) {
                if (ext.startsWith(".")) {
                    ext = "*" + ext;
                } else {
                    ext = "*." + ext;
                }
            }

            FileDataStoreFactorySpi factory = FileDataStoreFinder.getDataStoreFactory(ext);
            if (factory != null) {
                fileAssociations.put(ext, factory.getDescription());

            } else {
                // guess some common ones
                if (".csv".equals(ext)) {
                    fileAssociations.put(ext, "Comma-delimited files (*.csv)");

                } else if (ext.startsWith(".tif")) {
                    fileAssociations.put(ext, "GeoTIFF files (*.tif; *.tiff)");

                } else {
                    // fallback
                    fileAssociations.put(ext, ext.toUpperCase().substring(1) + "files (*" + ext + ")");
                }
            }
        }
        return fileAssociations;
    }

    /**
     * Create a dialog that filters for files with the specified extensions.
     *
     * @param extensions the file extensions, with or without the leading '.'
     */
    public JFileDataStoreChooser( Shell parent, int style, final List<String> extensions ) {
        this(parent, style, associations(extensions));
    }

    /**
     * Create a dialog that filters for files with the specified extensions.
     *
     * @param extensions the file extensions, with or without the leading '.'
     */
    public JFileDataStoreChooser( Shell parent, int style, final String[] extensions ) {
        this(parent, style, associations(Arrays.asList(extensions)));
    }

    /**
     * Creates a dialog based on the given file associations.
     *
     * <pre><code>
     * Map<String, String> assoc = new HashMap<String, String>();
     * assoc.put(".foo", "Foo data files (*.foo)");
     * assoc.put(".bar", "Bar data files (*.bar)");
     * JFileDataStoreChooser chooser = new JFileDataStoreChooser(assoc);
     * </code></pre>
     *
     * @param fileAssociations a {@code Map} where keys are extensions (with or
     *        wirhout the leading dot) and values are descriptions.
     */
    public JFileDataStoreChooser( Shell parent, int style, final Map<String, String> fileAssociations ) {
        fileDialog = new FileDialog(parent, style);
        init(fileAssociations);
    }

    /**
     * Helper method for constructors that creates file filters.
     *
     * @param fileAssociations a {@code Map} where keys are extensions (with or
     *        wirhout the leading dot) and values are descriptions.
     */
    private void init( final Map<String, String> fileAssociations ) {
        String[] exts = new String[fileAssociations.size()];
        String[] descr = new String[fileAssociations.size()];
        int i = 0;
        for( final Entry<String, String> entry : fileAssociations.entrySet() ) {

            exts[i] = entry.getKey();
            descr[i] = entry.getValue();
            i++;
        }
        fileDialog.setFilterExtensions(exts);
        fileDialog.setFilterNames(descr);
    }

    /**
     * Creates a dialog that filters for files matching the specified
     * data format. 
     *
     * @param format data file format
     */
    public JFileDataStoreChooser( Shell parent, int style, final FileDataStoreFactorySpi format ) {
        fileDialog = new FileDialog(parent, style);
        fileDialog.setFilterExtensions(format.getFileExtensions());
    }

    public FileDialog getFileDialog() {
        return fileDialog;
    }

    /**
     * Show a file open dialog that filters for files with the given extension.
     *
     * @param extension file extension, with or without leading '.'
     * @param parent parent GUI component (may be {@code null})
     *
     * @return the selected file or null if the user cancelled the selection
     * @throws java.awt.HeadlessException if run in an unsupported environment
     */
    public static File showOpenFile( String extension, Shell parent ) {
        return showOpenFile(extension, null, parent);
    }

    /**
     * Show a file open dialog that filters for files with the given extension.
     *
     * @param extension file extension, with or without leading '.'
     * @param initialDir initial directory to display; if {@code null} the initial directory
     *        will be the user's default directory
     * @param parent parent GUI component (may be {@code null})
     *
     * @return the selected file or null if the user cancelled the selection
     * @throws java.awt.HeadlessException if run in an unsupported environment
     */
    public static File showOpenFile( String extension, File initialDir, Shell parent ) {
        JFileDataStoreChooser dialog = new JFileDataStoreChooser(parent, SWT.OPEN, extension);
        FileDialog fileDialogInternal = dialog.getFileDialog();
        if (initialDir != null) {
            if (initialDir.isDirectory()) {
                fileDialogInternal.setFilterPath(initialDir.getAbsolutePath());
            } else {
                fileDialogInternal.setFilterPath(initialDir.getParentFile().getAbsolutePath());
            }
        }

        fileDialogInternal.open();
        String filePath = fileDialogInternal.getFileName();
        File file = new File(filePath);
        return file;
    }

    /**
     * Show a file open dialog that filters for files with the given extensions.
     *
     * @param extensions array of file extension, with or without leading '.'
     * @param parent parent GUI component (may be null)
     *
     * @return the selected file or null if the user cancelled the selection
     * @throws java.awt.HeadlessException if run in an unsupported environment
     */
    public static File showOpenFile( String[] extensions, Shell parent ) {
        return showOpenFile(extensions, null, parent);
    }

    /**
     * Show a file open dialog that filters for files with the given extensions.
     *
     * @param extensions array of file extension, with or without leading '.'
     * @param initialDir initial directory to display; if {@code null} the initial directory
     *        will be the user's default directory
     * @param parent parent GUI component (may be null)
     *
     * @return the selected file or null if the user cancelled the selection
     * @throws java.awt.HeadlessException if run in an unsupported environment
     */
    public static File showOpenFile( String[] extensions, File initialDir, Shell parent ) {

        JFileDataStoreChooser dialog = new JFileDataStoreChooser(parent, SWT.OPEN, extensions);
        FileDialog fileDialogInternal = dialog.getFileDialog();
        if (initialDir != null) {
            if (initialDir.isDirectory()) {
                fileDialogInternal.setFilterPath(initialDir.getAbsolutePath());
            } else {
                fileDialogInternal.setFilterPath(initialDir.getParentFile().getAbsolutePath());
            }
        }

        String filePath = fileDialogInternal.open();
        if (filePath != null) {
            File file = new File(filePath);
            return file;
        }
        return null;
    }

    /**
     * Show a file open dialog that filters for files that match a given file
     * data store format
     *
     * @param format the file data store format
     * @param parent parent GUI component (may be null)
     *
     * @return the selected file or null if the user cancelled the selection
     * @throws java.awt.HeadlessException if run in an unsupported environment
     */
    public static File showOpenFile( FileDataStoreFactorySpi format, Shell parent ) {
        return showOpenFile(format, null, parent);
    }

    /**
     * Show a file open dialog that filters for files that match a given file
     * data store format
     *
     * @param format the file data store format
     * @param initialDir initial directory to display; if {@code null} the initial directory
     *        will be the user's default directory
     * @param parent parent GUI component (may be null)
     *
     * @return the selected file or null if the user cancelled the selection
     * @throws java.awt.HeadlessException if run in an unsupported environment
     */
    public static File showOpenFile( FileDataStoreFactorySpi format, File initialDir, Shell parent ) {

        JFileDataStoreChooser dialog = new JFileDataStoreChooser(parent, SWT.OPEN, format);
        FileDialog fileDialogInternal = dialog.getFileDialog();
        if (initialDir != null) {
            if (initialDir.isDirectory()) {
                fileDialogInternal.setFilterPath(initialDir.getAbsolutePath());
            } else {
                fileDialogInternal.setFilterPath(initialDir.getParentFile().getAbsolutePath());
            }
        }

        fileDialogInternal.open();
        String filePath = fileDialogInternal.getFileName();
        File file = new File(filePath);
        return file;
    }

    /**
     * Demonstrates the file data store dialog by prompting for a shapefile
     *
     * @param arg ignored
     */
    public static void main( String arg[] ) {
        Display display = new Display();
        Shell shell = new Shell(display);
        File file = JFileDataStoreChooser.showOpenFile("shp", shell);
        if (file != null) {
            MessageDialog.openInformation(shell, "INFO", "Selected " + file.getAbsolutePath());
        } else {
            MessageDialog.openInformation(shell, "INFO", "Selection cancelled");
        }
    }
    /**
     * Consider the provided file as a candidate for a new filename. 
     * A number will be appended to the filename if there is a
     * conflict.
     * 
     * @param file the candidate file name
     */
    public void setSaveFile( File file ) {
        String path = file.getAbsolutePath();
        int split = path.lastIndexOf('.');
        String base;
        String extension;
        if (split == -1) {
            base = path;
            extension = "";
        } else {
            base = path.substring(0, split);
            extension = path.substring(split);
        }
        File saveFile = new File(path);
        int number = 0;
        while( saveFile.exists() ) {
            saveFile = new File(base + (number++) + extension);
        }
        fileDialog.setFileName(saveFile.getAbsolutePath());
    }
}
