/*
 * $Id$
 *
 * Copyright 1996-2008 Sun Microsystems, Inc.  All Rights Reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Sun designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Sun in the LICENSE file that accompanied this code.
 *
 * This code 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
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
 * CA 95054 USA or visit www.sun.com if you need additional information or
 * have any questions.
 */
package com.sun.javatest.mrep;

import java.awt.Component;
import java.io.File;

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

import com.sun.javatest.report.Report;
import com.sun.javatest.tool.IconFactory;
import com.sun.javatest.tool.UIFactory;
import com.sun.javatest.util.FileInfoCache;

/**
 * A custom JFileChooser, for a user to choose a report directory.
 */
//
// see also com.sun.javatest.tool.WorkDirChooser
//
class ReportDirChooser extends JFileChooser
{
    // the following is a workaround to force the Report class to
    // be safely loaded, before the JFileChooser starts running its background
    // thread, exposing a JVM bug in class loading.
    static {
        Class reportClass = Report.class;
    }

    /**
     * Create a ReportDirChooser, initially showing the user's current directory.
     */
    public ReportDirChooser() {
        this(new File(System.getProperty("user.dir")));
    }

    /**
     * Create a ReportDirChooser, initially showing a given directory.
     * @param initialDir the initial directory to be shown
     */
    public ReportDirChooser(File initialDir) {
        super(initialDir);
        uif = new UIFactory(this); // no helpBroker required

        icon = IconFactory.getSelectableFolderIcon();

        // we want a filter that only selects directories
        setAcceptAllFileFilterUsed(false);
        setFileFilter(new RDC_FileFilter());

        // we want a file view that displays special icons for
        // report directories and sets directories non-traversable,
        // so that double clicking in the list view will select
        // (ie choose) them
        setFileView(new RDC_FileView());

        // we need FILES in the mode so that non-traversable
        // items appear in the list
        // -- see BasicDirectoryModel.LoadFilesThread
        // we need DIRECTORIES in the mode so that directories
        // (eg report directories) can be selected (ie chosen)
        // -- see BasicFileChooserUI.ApproveSelectionAction
        setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);

        // these parameters are still not ideal ...
        // approveSelection(File) below gets called for all directories:
        // we still have to redispatch according to whether it is a report
        // directory or not.
    }

    /**
     * A constant to indicate that a new report directory is to be created.
     * @see #setMode
     */
    public static final int NEW = 0;

    /**
     * A constant to indicate that an existing report directory is to be opened.
     * @see #setMode
     */
    public static final int OPEN = 1;

    /**
     * Set whether the chooser is to be used to create a new report directory
     * or to open an existing report directory.
     * @param mode a constant to indicate how the chooser is to operate
     * @see #NEW
     * @see #OPEN
     */
    public void setMode(int mode) {
        this.mode = mode;
        switch (mode) {
        case NEW:
            setApproveButtonText(uif.getI18NString("rdc.new.btn"));
            setApproveButtonToolTipText(uif.getI18NString("rdc.new.tip"));
            setDialogTitle(uif.getI18NString("rdc.new.title"));
            break;
        case OPEN:
            setApproveButtonText(uif.getI18NString("rdc.open.btn"));
            setApproveButtonToolTipText(uif.getI18NString("rdc.open.tip"));
            setDialogTitle(uif.getI18NString("rdc.open.title"));
            break;
        default:
            throw new IllegalStateException();
        }
    }

    /**
     * Get the report directory that was most recently selected by the user.
     * @return the report directory that was most recently selected by the user
     * @see #showDialog
     */
    public File getSelectedReportDirectory() {
        return reportDir;
    }

    /**
     * Show a dialog to allow the user to select a report directory.
     * If a report directory is selected, it can be accessed via getSelectedReportDirectory.
     * @param parent the component to be used at the parent of this dialog
     * @return an integer signifying how the dialog was dismissed
     * (APPROVE_OPTION or CANCEL_OPTION).
     * @see #APPROVE_OPTION
     * @see #CANCEL_OPTION
     * @see #getSelectedReportDirectory
     */
    public int showDialog(Component parent) {
        return showDialog(parent, getApproveButtonText());
    }

    public void approveSelection() {
        // the validity of the selection depends on whether the
        // selected directory is to be created or opened.
        File file = getSelectedFile();
        if (mode == NEW)
            approveNewSelection(file);
        else
            approveOpenSelection(file);
    }

    //-------------------------------------------------------------------------

    private void approveNewSelection(File file) {
        if (file.exists()) {
            if (isReportDirectory(file) || isEmptyDirectory(file)) {
                // create new report in existing or empty report directory
                reportDir = file;
                super.approveSelection();
            }
            else if (file.isDirectory()) {
                // the directory exists, but is neither a report dir nor empty: open it
                setCurrentDirectory(file);
                setSelectedFile(null);
                setSelectedFiles(null);
            }
            else {
                uif.showError("rdc.notADir", file);
            }
        }
        else {
            // create new report in new report directory
            reportDir = file;
            super.approveSelection();
        }
    }

    //-------------------------------------------------------------------------

    private void approveOpenSelection(File file) {
        if (file.exists()) {
            if (isReportDirectory(file)) {
                reportDir = file;
                super.approveSelection();
            }
            else if (isDirectory(file)) {
                // the directory exists, but is not a report dir: open it
                setCurrentDirectory(file);
                setSelectedFile(null);
                setSelectedFiles(null);
            }
            else
                uif.showError("rdc.notADir", file);
        }
        else
            uif.showError("rdc.cantOpen", file);
    }

    //-------------------------------------------------------------------------

    private boolean isDirectory(File f) {
        return (f.isDirectory());
    }

    private boolean isReportDirectory(File f) {
        if (isIgnoreable(f))
            return false;

        Boolean b = (Boolean) (cache.get(f));
        if (b == null) {
            boolean v = Report.isReportDirectory(f);
            String[] l = f.list();
            if (l != null) {
                for (int i = 0; i < l.length; i++) {
                    if (l[i].endsWith(".html")) {
                        v = true;
                    }
                }
            }
            cache.put(f, (v ? Boolean.TRUE : Boolean.FALSE));
            return v;
        }
        else
            return b.booleanValue();
    }

    private boolean isEmptyDirectory(File f) {
        return (f.isDirectory() && f.list().length == 0);
    }

    private boolean isIgnoreable(File f) {
        // Take care not touch the floppy disk drive on Windows
        // because if there is no disk in it, the user will get a dialog.
        // Root directories (such as A:) have an empty name,
        // so use that to avoid touching the file itself.
        // This means we can't put a work directory in the root of
        // the file system, but that is a lesser inconvenience
        // than those floppy dialogs!
        return (f.getName().equals(""));
    }

    private FileInfoCache cache = new FileInfoCache(60*1000);

    private int mode;
    private File reportDir;
    private UIFactory uif;
    private Icon icon;

    private class RDC_FileView extends FileView {
        public String getDescription(File f) {
            return null;
        }

        public Icon getIcon(File f) {
            return (isReportDirectory(f) ? icon : null);
        }

        public String getName(File f) {
            // Take care to get names of file system roots correct
            String name = f.getName();
            return (name.length() == 0 ? f.getPath() : name);
        }

        public String getTypeDescription(File f) {
            return null;
        }

        public Boolean isTraversable(File f) {
            return (isDirectory(f) && !isReportDirectory(f) ? Boolean.TRUE : Boolean.FALSE);
        }
    }

    private class RDC_FileFilter extends FileFilter {
        public boolean accept(File f) {
            return (isDirectory(f));
        }

        public String getDescription() {
            return uif.getI18NString("rdc.ft");
        }
    }
}
