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

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
//
public 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);
	    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 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");
	}
    }
}
