/*  Copyright (C) 2003-2011 JabRef contributors.
    This program 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 2 of the License, or
    (at your option) any later version.

    This program 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 this program; if not, write to the Free Software Foundation, Inc.,
    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package net.sf.jabref.external;

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import javax.swing.JDialog;
import javax.swing.JProgressBar;
import javax.swing.SwingUtilities;

import net.sf.jabref.BaseAction;
import net.sf.jabref.BasePanel;
import net.sf.jabref.BibtexEntry;
import net.sf.jabref.GUIGlobals;
import net.sf.jabref.MetaData;
import net.sf.jabref.Util;
import net.sf.jabref.gui.FileListEntry;
import net.sf.jabref.gui.FileListTableModel;


/**
 * This class handles the task of looking up all external files linked for a set
 * of entries. This is useful for tasks where the user wants e.g. to send a database
 * with external files included.
 */
public class AccessLinksForEntries {

    /**
     * Look up all external files linked from (at least) one of the entries in a set.
     * This method does not verify the links.
     *
     * @param entries The set of entries.
     * @return A list of FileListEntry objects pointing to the external files.
     */
    public static List<FileListEntry> getExternalLinksForEntries(List<BibtexEntry> entries) {
        List<FileListEntry> files = new ArrayList<FileListEntry>();
        FileListTableModel model = new FileListTableModel();
        for (Iterator<BibtexEntry> iterator = entries.iterator(); iterator.hasNext();) {
            BibtexEntry entry = iterator.next();
            String links = entry.getField(GUIGlobals.FILE_FIELD);
            if (links == null)
                continue;
            model.setContent(links);
            for (int i=0; i<model.getRowCount(); i++)
                files.add(model.getEntry(i));
        }
        return files;
    }

    /**
     * Take a list of external links and copy the referred files to a given directory.
     * This method should be run off the Event Dispatch Thread. A progress bar, if given,
     * will be updated on the EDT.
     *
     * @param files The list of file links.
     * @param toDir The directory to copy the files to.
     * @param metaData The MetaData for the database containing the external links. This is needed
     *  because the database might have its own file directory.
     * @param prog A JProgressBar which will be updated to show the progress of the process.
     *  This argument can be null if no progress bar is needed.
     * @param deleteOriginalFiles if true, the files in their original locations will be deleted
     *  after copying, for each file whose source directory is different from the destination
     *  directory differs.
     * @param callback An ActionListener which should be notified when the process is finished.
     *  This parameter can be null if no callback is needed.
     */
    public static void copyExternalLinksToDirectory(final List<FileListEntry> files, File toDir,
                                                    MetaData metaData, final JProgressBar prog,
                                                    boolean deleteOriginalFiles,
                                                    final ActionListener callback) {

        if (prog != null) SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                prog.setMaximum(files.size());
                prog.setValue(0);
                prog.setIndeterminate(false);
            }
        });

        Set<String> fileNames = new HashSet<String>();

        int i=0;

        for (Iterator<FileListEntry> iterator = files.iterator(); iterator.hasNext();) {
            FileListEntry entry = iterator.next();
            File file = new File(entry.getLink());

            // We try to check the extension for the file:
            String name = file.getName();
            int pos = name.lastIndexOf('.');
            String extension = ((pos >= 0) && (pos < name.length() - 1)) ? name.substring(pos + 1)
                .trim().toLowerCase() : null;

            // Find the default directory for this field type, if any:
            String[] dir = metaData.getFileDirectory(extension);
            // Include the standard "file" directory:
            String[] fileDir = metaData.getFileDirectory(GUIGlobals.FILE_FIELD);
            // Include the directory of the bib file:
            ArrayList<String> al = new ArrayList<String>();
            for (int i2 = 0; i2 < dir.length; i2++)
                if (!al.contains(dir[i2])) al.add(dir[i2]);
            for (int i2 = 0; i2 < fileDir.length; i2++)
                if (!al.contains(fileDir[i2])) al.add(fileDir[i2]);

            String[] dirs = al.toArray(new String[al.size()]);
            File tmp = Util.expandFilename(entry.getLink(), dirs);
            if (tmp != null)
                file = tmp;

            // Check if we have arrived at an existing file:
            if (file.exists()) {
                if (fileNames.contains(name)) {
                    // Oops, a file of that name already exists....
                }
                else {
                    fileNames.add(name);
                    File destination = new File(toDir, name);

                    // Check if the source and destination locations differ:
                    if (!destination.equals(file)) {
                        try {
                            // Copy the file:
                            Util.copyFile(file, destination, false);
                            // Delete the original file if requested:
                            if (deleteOriginalFiles)
                                file.delete();
                            
                        } catch (IOException ex) {
                            ex.printStackTrace();
                        }
                    }
                    else {
                        // Destination and source is the same. Do nothing.
                    }
                    // Update progress bar:
                    i++;
                    final int j = i;

                    if (prog != null) SwingUtilities.invokeLater(new Runnable() {
                        public void run() {
                            prog.setValue(j);
                        }
                    });
                }
            }
            else {
                // The link could not be resolved to an existing file.
                
            }
        }

        if (callback != null) {
            callback.actionPerformed(null);
        }
    }


    public static class SaveWithLinkedFiles extends BaseAction {
        private BasePanel panel;

        public SaveWithLinkedFiles(BasePanel panel) {

            this.panel = panel;
        }

        @Override
        public void action() throws Throwable {

            ArrayList<BibtexEntry> entries = new ArrayList<BibtexEntry>();
            BibtexEntry[] sel = panel.getSelectedEntries();
            for (int i = 0; i < sel.length; i++) {
                BibtexEntry bibtexEntry = sel[i];
                entries.add(bibtexEntry);
            }
            final List<FileListEntry> links =
                    AccessLinksForEntries.getExternalLinksForEntries(entries);
            for (Iterator<FileListEntry> iterator = links.iterator(); iterator.hasNext();) {
                FileListEntry entry = iterator.next();
                System.out.println("Link: " + entry.getLink());
            }

            final JProgressBar prog = new JProgressBar();
            prog.setIndeterminate(true);
            final JDialog diag = new JDialog(panel.frame(), false);
            diag.getContentPane().add(prog, BorderLayout.CENTER);
            diag.pack();
            diag.setLocationRelativeTo(panel.frame());
            diag.setVisible(true);
            Thread t = new Thread(new Runnable() {
                public void run() {
                    AccessLinksForEntries.copyExternalLinksToDirectory(links,
                            new File("/home/alver/tmp"), panel.metaData(), prog, false,
                            new ActionListener() {
                                public void actionPerformed(ActionEvent actionEvent) {
                                    diag.dispose();
                                }
                            });
                }
            });
            t.start();

            
        }
    }


}
