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

import net.sf.jabref.Globals;
import net.sf.jabref.AbstractWorker;

import javax.swing.*;
import java.io.File;
import java.io.FileFilter;
import java.awt.*;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.util.*;
import java.util.List;

import com.jgoodies.forms.builder.DefaultFormBuilder;
import com.jgoodies.forms.layout.FormLayout;

/**
 * Tools for automatically detecting jar and executable paths to OpenOffice.
 */
public class AutoDetectPaths extends AbstractWorker {

    boolean foundPaths = false;
    boolean fileSearchCancelled = false;
    JDialog prog;
    private JDialog parent;

    public AutoDetectPaths(JDialog parent) {
        this.parent = parent;
    }

    public boolean runAutodetection() {
        try {
            if (checkAutoDetectedPaths())
                return true;
            init();
            getWorker().run();
            update();
            return foundPaths;
        } catch (Throwable e) {
            e.printStackTrace();
            return false;
        }
    }

    public void run() {
        foundPaths = autoDetectPaths();
    }

    public boolean getResult() {
        return foundPaths;
    }

    public boolean cancelled() {
        return fileSearchCancelled;
    }

    public void init() throws Throwable {
        prog = showProgressDialog(parent, Globals.lang("Autodetecting paths..."),
            Globals.lang("Please wait..."), true);
    }

    public void update() {
        prog.dispose();
    }

    public boolean autoDetectPaths() {

        if (Globals.ON_WIN) {
            List<File> progFiles = findProgramFilesDir();
            File sOffice = null;
            if (fileSearchCancelled)
                return false;
            for (File dir : progFiles) {
                sOffice = findFileDir(dir, "soffice.exe");
                if (sOffice != null)
                    break;
            }
            if (sOffice == null) {
                JOptionPane.showMessageDialog(parent, Globals.lang("Unable to autodetect OpenOffice installation. Please choose the installation directory manually."),
                        Globals.lang("Could not find OpenOffice installation"), JOptionPane.INFORMATION_MESSAGE);
                JFileChooser jfc = new JFileChooser(new File("C:\\"));
                jfc.setDialogType(JFileChooser.OPEN_DIALOG);
                jfc.setFileFilter(new javax.swing.filechooser.FileFilter() {
                    public boolean accept(File file) {
                        return file.isDirectory();
                    }

                    public String getDescription() {
                        return Globals.lang("Directories");
                    }
                });
                jfc.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
                jfc.showOpenDialog(parent);
                if (jfc.getSelectedFile() != null)
                    sOffice = jfc.getSelectedFile();
            }
            if (sOffice == null)
                return false;

            Globals.prefs.put("ooExecutablePath", new File(sOffice, "soffice.exe").getPath());
            File unoil = findFileDir(sOffice.getParentFile(), "unoil.jar");
            if (fileSearchCancelled)
                return false;
            File jurt = findFileDir(sOffice.getParentFile(), "jurt.jar");
            if (fileSearchCancelled)
                return false;
            if ((unoil != null) && (jurt != null)) {
                Globals.prefs.put("ooUnoilPath", unoil.getPath());
                Globals.prefs.put("ooJurtPath", jurt.getPath());
                return true;
            }
            else return false;

        }
        else if (Globals.ON_MAC) {
            File rootDir = new File("/Applications");
            File[] files = rootDir.listFiles();
            for (int i = 0; i < files.length; i++) {
                File file = files[i];
                if (file.isDirectory() && file.getName().equals("OpenOffice.org.app")) {
                    rootDir = file;
                    //System.out.println("Setting starting dir to: "+file.getPath());
                    break;
                }
            }
            //System.out.println("Searching for soffice.bin");
            File sOffice = findFileDir(rootDir, "soffice.bin");
            //System.out.println("Found: "+(sOffice != null ? sOffice.getPath() : "-"));
            if (fileSearchCancelled)
                return false;
            if (sOffice != null) {
                Globals.prefs.put("ooExecutablePath", new File(sOffice, "soffice.bin").getPath());
                //System.out.println("Searching for unoil.jar");
                File unoil = findFileDir(rootDir, "unoil.jar");
                //System.out.println("Found: "+(unoil != null ? unoil.getPath(): "-"));
                if (fileSearchCancelled)
                    return false;
                //System.out.println("Searching for jurt.jar");
                File jurt = findFileDir(rootDir, "jurt.jar");
                //System.out.println("Found: "+(jurt != null ? jurt.getPath(): "-"));
                if (fileSearchCancelled)
                    return false;
                if ((unoil != null) && (jurt != null)) {
                    Globals.prefs.put("ooUnoilPath", unoil.getPath());
                    Globals.prefs.put("ooJurtPath", jurt.getPath());
                    return true;
                }
                else return false;
            }
            else return false;
        }
        else {
            // Linux:
            String usrRoot = "/usr/lib";
            File inUsr = findFileDir(new File("/usr/lib"), "soffice");
            if (fileSearchCancelled)
                return false;
            if (inUsr == null) {
                inUsr = findFileDir(new File("/usr/lib64"), "soffice");
                if (inUsr != null) usrRoot = "/usr/lib64";
            }

            if (fileSearchCancelled)
                return false;
            File inOpt = findFileDir(new File("/opt"), "soffice");
            if (fileSearchCancelled)
                return false;
            if ((inUsr != null) && (inOpt == null)) {
                Globals.prefs.put("ooExecutablePath", new File(inUsr, "soffice.bin").getPath());
                File unoil = findFileDir(new File(usrRoot), "unoil.jar");
                if (fileSearchCancelled)
                    return false;
                File jurt = findFileDir(new File(usrRoot), "jurt.jar");
                if (fileSearchCancelled)
                    return false;
                if ((unoil != null) && (jurt != null)) {
                    Globals.prefs.put("ooUnoilPath", unoil.getPath());
                    Globals.prefs.put("ooJurtPath", jurt.getPath());
                    return true;
                }
                else return false;
            }
            else if ((inOpt != null) && (inUsr == null)) {
                Globals.prefs.put("ooExecutablePath", new File(inOpt, "soffice.bin").getPath());
                File unoil = findFileDir(new File("/opt"), "unoil.jar");
                File jurt = findFileDir(new File("/opt"), "jurt.jar");
                if ((unoil != null) && (jurt != null)) {
                    Globals.prefs.put("ooUnoilPath", unoil.getPath());
                    Globals.prefs.put("ooJurtPath", jurt.getPath());
                    return true;
                }
                else return false;
            }
            else if (inOpt != null) { // Found both
                JRadioButton optRB = new JRadioButton(inOpt.getPath(), true);
                JRadioButton usrRB = new JRadioButton(inUsr.getPath(), false);
                ButtonGroup bg = new ButtonGroup();
                bg.add(optRB);
                bg.add(usrRB);
                DefaultFormBuilder b = new DefaultFormBuilder(new FormLayout("left:pref", ""));
                b.append(Globals.lang("Found more than one OpenOffice executable. Please choose which one to connect to:"));
                b.append(optRB);
                b.append(usrRB);
                int answer = JOptionPane.showConfirmDialog(null, b.getPanel(), Globals.lang("Choose OpenOffice executable"),
                        JOptionPane.OK_CANCEL_OPTION);
                if (answer == JOptionPane.CANCEL_OPTION)
                    return false;
                else {
                    if (optRB.isSelected()) {
                        Globals.prefs.put("ooExecutablePath", new File(inOpt, "soffice.bin").getPath());
                        File unoil = findFileDir(new File("/opt"), "unoil.jar");
                        if (fileSearchCancelled)
                            return false;
                        File jurt = findFileDir(new File("/opt"), "jurt.jar");
                        if (fileSearchCancelled)
                            return false;
                        if ((unoil != null) && (jurt != null)) {
                            Globals.prefs.put("ooUnoilPath", unoil.getPath());
                            Globals.prefs.put("ooJurtPath", jurt.getPath());
                            return true;
                        }
                        else return false;
                    }
                    else {
                        Globals.prefs.put("ooExecutablePath", new File(inUsr, "soffice.bin").getPath());
                        File unoil = findFileDir(new File(usrRoot), "unoil.jar");
                        if (fileSearchCancelled)
                            return false;
                        File jurt = findFileDir(new File(usrRoot), "jurt.jar");
                        if (fileSearchCancelled)
                            return false;
                        if ((unoil != null) && (jurt != null)) {
                            Globals.prefs.put("ooUnoilPath", unoil.getPath());
                            Globals.prefs.put("ooJurtPath", jurt.getPath());
                            return true;
                        }
                        else return false;
                    }

                }
            }
            else return false;
        }


    }

    /**
     * Search for Program files directory.
     * @return the File pointing to the Program files directory, or null if not found.
     *   Since we are not including a library for Windows integration, this method can't
     *   find the Program files dir in localized Windows installations.
     */
    private static java.util.List<File> findProgramFilesDir() {
        List<File> dirList = new ArrayList<File>();
        File root = new File("C:\\");
        File[] dirs = root.listFiles(new FileFilter() {
            public boolean accept(File file) {
                return file.isDirectory();
            }
        });
        for (int i = 0; i < dirs.length; i++) {
            File dir = dirs[i];
            if (dir.getName().toLowerCase().equals("program files"))
                dirList.add(dir);
            else if (dir.getName().toLowerCase().equals("program files (x86)"))
                dirList.add(dir);
        }
        return dirList;
    }

    public static boolean checkAutoDetectedPaths() {

        if (Globals.prefs.hasKey("ooUnoilPath") && Globals.prefs.hasKey("ooJurtPath")
                && Globals.prefs.hasKey("ooExecutablePath")) {
            if (new File(Globals.prefs.get("ooUnoilPath"), "unoil.jar").exists()
                && new File(Globals.prefs.get("ooJurtPath"), "jurt.jar").exists()
                && new File(Globals.prefs.get("ooExecutablePath")).exists()) {
                return true;
            }
            else {
                return false;
            }
        }
        else return false;
    }

    /**
     * Search for a file, starting at the given directory.
     * @param startDir The starting point.
     * @param filename The name of the file to search for.
     * @return The directory where the file was first found, or null if not found.
     */
    public File findFileDir(File startDir, String filename) {
        if (fileSearchCancelled)
            return null;
        File[] files = startDir.listFiles();
        if (files == null)
            return null;
        File result = null;
        for (int i=0; i<files.length; i++) {
            if (fileSearchCancelled)
                return null;
            if (files[i].isDirectory()) {
                result = findFileDir(files[i], filename);
                if (result != null)
                    break;
            }
            else if (files[i].getName().equals(filename)) {
                result = startDir;
                break;
            }
        }
        return result;
    }

    public JDialog showProgressDialog(JDialog parent, String title, String message, boolean includeCancelButton) {
        fileSearchCancelled = false;
        final JDialog prog;
        JProgressBar bar = new JProgressBar(JProgressBar.HORIZONTAL);
        JButton cancel = new JButton(Globals.lang("Cancel"));
        cancel.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent event) {
                fileSearchCancelled = true;
                ((JButton)event.getSource()).setEnabled(false);
            }
        });
        prog = new JDialog(parent, title, false);
        bar.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
        bar.setIndeterminate(true);
        if (includeCancelButton)
            prog.add(cancel, BorderLayout.SOUTH);
        prog.add(new JLabel(message), BorderLayout.NORTH);
        prog.add(bar, BorderLayout.CENTER);
        prog.pack();
        prog.setLocationRelativeTo(null);//parent);
        //SwingUtilities.invokeLater(new Runnable() {
        //    public void run() {
                prog.setVisible(true);
        //    }
        //});
        return prog;
    }
}
