/*
 * $Id$
 *
 * Copyright (c) 2002, 2009, Oracle and/or its affiliates. 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.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */
package com.sun.javatest.report;

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

import javax.swing.Icon;
import javax.swing.JFileChooser;
import javax.swing.filechooser.FileFilter;
import javax.swing.filechooser.FileView;
import java.awt.Component;
import java.io.File;

/**
 * A custom JFileChooser, for a user to choose a report directory.
 */
//
// see also com.sun.javatest.tool.WorkDirChooser
//
public class ReportDirChooser extends JFileChooser {

    /**
     * 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;
    private FileInfoCache cache = new FileInfoCache();
    private int mode;
    private File reportDir;
    private UIFactory uif;
    private Icon icon;

    /**
     * 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, null); // 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.
    }

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

    /**
     * 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());
    }

    @Override
    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 = cache.get(f);
        if (b == null) {
            boolean v = Report.isReportDirectory(f);
            cache.put(f, v);
            return v;
        } else {
            return b;
        }
    }

    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 floppy dialogs!
        return f.getName().isEmpty();
    }

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

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

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

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

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

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

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