/* Author: Jeff Dalton <J.Dalton@ed.ac.uk>
 * Updated: Sat Jul 14 01:39:54 2001 by Jeff Dalton
 * Copyright: (c) 2001, AIAI, University of Edinburgh
 */

package ix.iface.domain;

import java.io.IOException;
import java.io.File;

import java.awt.Component;

import javax.swing.*;

import ix.icore.domain.Domain;
import ix.util.*;

/**
 * An object that writes a domain description in textual form.
 */
public abstract class DomainWriter {

    public abstract void writeDomain(Domain domain) throws IOException;

    /**
     * Factory method that returns an appropriate writer for the
     * indicated file.  It converts the filename to an abstract
     * pathname (File) and calls the <code>makeWriter(File)</code>
     * method.
     */
    public static DomainWriter
    makeWriter(String filename) throws IOException {
	return makeWriter(new File(filename));
    }

    /**
     * Factory method that returns an appropriate writer for the
     * indicated file, based on the file's type or extension.
     * If the file does not specify a parent directory, the result
     * of calling <code>DomainParser.getLibraryDirectory()</code> is used. <p>
     *
     * Here's how it might be used:
     * <pre>
     *   File domainName = ...;
     *   Domain domain = ...;
     *   ...
     *   try {
     *       DomainWriter.makeWriter(domainName).writeDomain(domain);
     *   }
     *   catch (IOException e) { ... }
     * </pre>
     */
    public static DomainWriter
    makeWriter(File file) throws IOException {
	// Factory method based on file type.
	if (file.getParentFile() == null) {
	    File libDir = DomainParser.getLibraryDirectory();
	    Debug.noteln("Resolving " + file + " against " + libDir);
	    file = new File(libDir, file.getPath());
	}
	String name = file.getName();
	if (name.endsWith(".xml"))
	    return new XMLTF_Writer(file);
	else
	    throw new IOException("File " + file.getName()
				  + " does not have type .xml");
    }

    /**
     * Writes a description of a domain to a file selected by the user,
     * conducting all necessary dialogs along the way.  It repeatedly
     * asks the user to select a file until either the domain description
     * has been successfully written or the user decices to cancel the
     * operation.  The user is informed of any exceptions thrown while
     * attempting to write, and "success" means that no exceptions were
     * thrown.  If the description has been written to a file, a
     * corresponding File (abstract pathname) is returned; otherwise,
     * the result is <code>null</code>. <p>
     *
     * The output syntax is a function of the file's type as
     * understood by <code>makeWriter(File)</code>.
     *
     * @see #makeWriter(File)
     *
     * @param parentComponent determines the Frame in which dialogs
     *        are displayed.
     * @param domain the Domain to save
     * @return a File if the domain has been written to a file;
     *         otherwise <code>null</code>
     */
    public static File saveDomain(Component parentComponent, Domain domain) {
	File libDir = DomainParser.getLibraryDirectory();
	// Loop until either the domain has successfully been written
	// to a file or else the user has decided to "Cancel" rather
	// that select a(nother) file.
	while (true) {
	    File domainFile = chooseFileToWrite(parentComponent, libDir);
	    if (domainFile == null)
		return null;		// user cancels
	    File result = saveDomain(parentComponent, domain, domainFile);
	    if (result != null)
		return result;		// success!
	}
    }

    private static File saveDomain(Component frame,
				   Domain domain,
				   File domainName) {
	// See if file already exists.
	if (domainName.exists()) {
	    switch(JOptionPane.showConfirmDialog(frame,
		       "Overwrite " + Util.quote(domainName.getPath()),
		       "Confirm",
		       JOptionPane.YES_NO_OPTION)) {
	    case JOptionPane.YES_OPTION:
		// Continue
		break;
	    case JOptionPane.NO_OPTION:
		// Return now and don't save.
		return null;
	    }
	}
	// Write a domain description if we can.
	Debug.noteln("Writing domain to", domainName);
	try {
	    DomainWriter.makeWriter(domainName).writeDomain(domain);
	    return domainName;
	}
	catch (IOException e) {
	    JOptionPane.showMessageDialog(frame,
                new Object[] {
                    "Problem saving " + Util.quote(domainName.getPath()),
		    e.getMessage()},
		"Trouble saving domain",
		JOptionPane.ERROR_MESSAGE);
	    return null;
	}
    }

    private static File chooseFileToWrite(Component frame, File directory) {
	JFileChooser chooser = new JFileChooser(directory);
	chooser.setFileFilter(new DomainFileFilter());
	int option = chooser.showSaveDialog(frame);
	if (option == JFileChooser.APPROVE_OPTION)
	    return chooser.getSelectedFile();
	else
	    return null;
    }

}
