/*
 * @(#)DialogAction.java    2.0  21 February 2002
 *
 * Copyright 2004
 * College of Computer and Information Science
 * Northeastern University
 * Boston, MA  02115
 *
 * The Java Power Tools software may be used for educational
 * purposes as long as this copyright notice is retained intact
 * at the top of all source files.
 *
 * To discuss possible commercial use of this software, 
 * contact Richard Rasala at Northeastern University, 
 * College of Computer and Information Science,
 * 617-373-2462 or rasala@ccs.neu.edu.
 *
 * The Java Power Tools software has been designed and built
 * in collaboration with Viera Proulx and Jeff Raab.
 *
 * Should this software be modified, the words "Modified from 
 * Original" must be included as a comment below this notice.
 *
 * All publication rights are retained.  This software or its 
 * documentation may not be published in any media either
 * in whole or in part without explicit permission.
 *
 * This software was created with support from Northeastern 
 * University and from NSF grant DUE-9950829.
 */

package edu.neu.ccs.gui;

import edu.neu.ccs.*;
import edu.neu.ccs.gui.*;
import edu.neu.ccs.util.*;
import edu.neu.ccs.console.*;

import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
import java.beans.*;

/**
 * <P>Encapsulates an <CODE>Action</CODE> that will be performed
 * for a <CODE>GeneralDialog</CODE>.</P>
 *
 * @author  Richard Rasala
 * @version 2.2
 * @since   2.0
 */
public class DialogAction 
    extends ActionWrapper 
    implements ConsoleAware 
{
    /** The choice to keep the dialog open after the action. */
    public static final Object KEEP_OPEN = new Object();

    /** The choice to auto close the dialog after the action. */
    public static final Object AUTO_CLOSE = new Object();

    /** The choice to both close the dialog and mark the dialog as cancelled. */
    public static final Object SET_CANCEL = new Object();

    /** The dialog to act on. */
    protected GeneralDialog dialog = null;
    
    /** The last action to perform. */
    protected Object finish = AUTO_CLOSE;
    
    //////////////////
    // Constructors //
    //////////////////
    
    /**
     * <P>Constructs a <CODE>DialogAction</CODE> object using
     * a <CODE>GeneralDialog</CODE> object,
     * an action that must be performed for that dialog,
     * and an object that determines what will happen to
     * the dialog when the action is done.</P>
     *
     * <P>The finish parameter must be one of the following values:</P>
     *
     * <UL>
     *   <LI> DialogAction.KEEP_OPEN
     *   <LI> DialogAction.AUTO_CLOSE
     *   <LI> DialogAction.SET_CANCEL
     * </UL>
     *
     * <P>If the parameter is not one of these values,
     * the finish is set to <CODE>DialogAction.AUTO_CLOSE</CODE>.</P>
     *
     * <P>A <CODE>NullPointerException</CODE> will be thrown if the
     * <CODE>GeneralDialog</CODE> object is <CODE>null</CODE>.</P>
     *
     * @param dialog the dialog to act on
     * @param action the action to perform
     * @param finish what to do to the dialog when the action is done
     * @see #DialogAction(GeneralDialog, String, Object)
     * @see #DialogAction(GeneralDialog, String, Icon, Object)
     * @see #DialogAction(GeneralDialog, Object[])
     */
    public DialogAction
        (GeneralDialog dialog, Action action, Object finish)
    {
		if (dialog == null)
            throw new NullPointerException
                ("Null dialog passed to a DialogAction constructor");
        
        this.dialog = dialog;
        
        setAction(action);
        setFinish(finish);
    }

    /**
     * <P>Constructs a <CODE>DialogAction</CODE> object using
     * a <CODE>GeneralDialog</CODE> object,
     * a name to define a trivial action,
     * and an object that determines what will happen to
     * the dialog when the action is done.</P>
     *
     * <P>The finish parameter must be one of the following values:</P>
     *
     * <UL>
     *   <LI> DialogAction.KEEP_OPEN
     *   <LI> DialogAction.AUTO_CLOSE
     *   <LI> DialogAction.SET_CANCEL
     * </UL>
     *
     * <P>If the parameter is not one of these values,
     * the finish is set to <CODE>DialogAction.AUTO_CLOSE</CODE>.</P>
     *
     * <P>A <CODE>NullPointerException</CODE> will be thrown if the
     * <CODE>GeneralDialog</CODE> object is <CODE>null</CODE>.</P>
     *
     * @param dialog the dialog to act on
     * @param name the name for a trivial action
     * @param finish what to do to the dialog when the action is done
     * @see #DialogAction(GeneralDialog, Action, Object)
     * @see #DialogAction(GeneralDialog, String, Icon, Object)
     * @see #DialogAction(GeneralDialog, Object[])
     */
    public DialogAction
        (GeneralDialog dialog, String name, Object finish)
    {
        this(dialog, makeTrivialAction(name), finish);
    }
    
    /**
     * <P>Constructs a <CODE>DialogAction</CODE> object using
     * a <CODE>GeneralDialog</CODE> object,
     * a name and icon to define a trivial action,
     * and an object that determines what will happen to
     * the dialog when the action is done.</P>
     *
     * <P>The finish parameter must be one of the following values:</P>
     *
     * <UL>
     *   <LI> DialogAction.KEEP_OPEN
     *   <LI> DialogAction.AUTO_CLOSE
     *   <LI> DialogAction.SET_CANCEL
     * </UL>
     *
     * <P>If the parameter is not one of these values,
     * the finish is set to <CODE>DialogAction.AUTO_CLOSE</CODE>.</P>
     *
     * <P>A <CODE>NullPointerException</CODE> will be thrown if the
     * <CODE>GeneralDialog</CODE> object is <CODE>null</CODE>.</P>
     *
     * @param dialog the dialog to act on
     * @param name the name for a trivial action
     * @param icon the icon for a trivial action
     * @param finish what to do to the dialog when the action is done
     * @see #DialogAction(GeneralDialog, Action, Object)
     * @see #DialogAction(GeneralDialog, String, Object)
     * @see #DialogAction(GeneralDialog, Object[])
     */
    public DialogAction
        (GeneralDialog dialog, String name, Icon icon, Object finish)
    {
        this(dialog, makeTrivialAction(name, icon), finish);
    }
    
    /**
     * <P>Constructs a <CODE>DialogAction</CODE> object using
     * a <CODE>GeneralDialog</CODE> object and
     * an action data array to set the action and finish.</P>
     *
     * <P>The parameter <CODE>actionData</CODE> is an <CODE>Object[]</CODE>
     * array that specifies the action and finish in one of the following
     * ways:</P>
     * 
     * <UL>
     *   <LI> An array { action }             with an action.
     *   <LI> An array { name }               with an action name.
     *   <LI> An array { name, icon }         with an action name and icon.
     *   <LI> An array { action, option }
     *   <LI> An array { name, option }
     *   <LI> An array { name, icon, option }
     * </UL>
     *
     * <P>The option parameter must be one of the following values
     * that will determine what is done to the dialog 
     * when the action is finished:</P>
     *
     * <UL>
     *   <LI> DialogAction.KEEP_OPEN
     *   <LI> DialogAction.AUTO_CLOSE
     *   <LI> DialogAction.SET_CANCEL
     * </UL>
     *
     * <P>The default if the option is not specified 
     * is DialogAction.AUTO_CLOSE.</P>
     *
     * <P>A <CODE>NullPointerException</CODE> will be thrown if the
     * <CODE>GeneralDialog</CODE> object is <CODE>null</CODE>.</P>
     *
     * @param dialog the dialog to act on
     * @param actionData the array used to generate the dialog actions
     * @see #DialogAction(GeneralDialog, Action, Object)
     * @see #DialogAction(GeneralDialog, String, Object)
     * @see #DialogAction(GeneralDialog, String, Icon, Object)
     */
    public DialogAction
        (GeneralDialog dialog, Object[] actionData)
    {
		if (dialog == null)
            throw new NullPointerException
                ("Null dialog passed to a DialogAction constructor");
        
        this.dialog = dialog;
        
        setActionAndFinish(actionData);
    }
    
    ////////////////////
    // ActionListener //
    ////////////////////
    
    /**
     * Invokes the <CODE>actionPerformed</CODE> method of the
     * encapsulated action and then performs the action
     * specified as the finish action.
     *
     * @param event the event that invoked this action
     */
    public void actionPerformed(ActionEvent event) {
        // assume by default that the dialog will not be cancelled
        dialog.setCancelled(false);
        
        // extract the action name for the dialog response
        String name = getName();
        
        try {
            // perform the encapsulated action
            if (action != null)
                action.actionPerformed(event);
        }
        catch (Exception exception) {
            // extract the exception message
            String message = exception.getMessage();
            
            if (message == null)
                message = "";
            
            // amplify the exception message
            message = "\nDialogAction Error: " + message + 
                      "\nIn Action: " + name;
            
            // print error information to the error stream
            console.err.println();
            console.err.println(message);
            console.err.println();
            exception.printStackTrace(console.err);
            console.err.println();
            
            // make sure the dialog will close
            if (finish == KEEP_OPEN)
                finish = AUTO_CLOSE;
            
            // throw the amplified exception
            throw new RuntimeException(message);
        }
        finally {
            // if the dialog should remain open then return immediately
            if (finish == KEEP_OPEN)
                return;
            
            // if the action is a cancel action then inform the dialog 
            if (finish == SET_CANCEL)
                dialog.setCancelled(true);
            
            // return the action name as the dialog response and then
            // close the dialog
            dialog.setResponseAndClose(name);
        }
    }
    
    ////////////////
    // Public API //
    ////////////////
    
    /**
     * Returns the choice of what to do to the dialog 
     * when the action is done.
     *
     * @see #setFinish(Object)
     * @see #setActionAndFinish(Object[])
     */
    public Object getFinish() {
        return finish;
    }
    
    /**
     * <P>Sets the choice of what to do to the dialog 
     * when the action is done to the given parameter.</P>
     *
     * <P>The finish parameter must be one of the following values:</P>
     *
     * <UL>
     *   <LI> DialogAction.KEEP_OPEN
     *   <LI> DialogAction.AUTO_CLOSE
     *   <LI> DialogAction.SET_CANCEL
     * </UL>
     *
     * <P>If the finish parameter is not one of these values,
     * this method does nothing.</P>
     *
     * @param finish what to do to the dialog when the action is done
     * @see #getFinish()
     * @see #setActionAndFinish(Object[])
     */
    public void setFinish(Object finish) {
        if ((finish == KEEP_OPEN)  || 
            (finish == AUTO_CLOSE) || 
            (finish == SET_CANCEL)) 
        {
            this.finish = finish;
        }
        else {
            this.finish = AUTO_CLOSE;
        }
    }
    
    /*
     * <P>Sets the DialogAction action and finish 
     * using the given action data.</P>
     *
     * <P>The parameter <CODE>actionData</CODE> is an <CODE>Object[]</CODE>
     * array that specifies the action and finish in one of the following
     * ways:</P>
     * 
     * <UL>
     *   <LI> An array { action }             with an action.
     *   <LI> An array { name }               with an action name.
     *   <LI> An array { name, icon }         with an action name and icon.
     *   <LI> An array { action, option }
     *   <LI> An array { name, option }
     *   <LI> An array { name, icon, option }
     * </UL>
     *
     * <P>The option parameter should be one of the following values that will
     * determine what is done to the dialog when the action is finished:</P>
     *
     * <UL>
     *   <LI> DialogAction.KEEP_OPEN
     *   <LI> DialogAction.AUTO_CLOSE
     *   <LI> DialogAction.SET_CANCEL
     * </UL>
     *
     * <P>The default if the option is not specified 
     * is DialogAction.AUTO_CLOSE.</P>
     *
     * @param actionData the array used to generate the dialog actions
     * @see #getAction()
     * @see #setAction(Action)
     * @see #getFinish()
     * @see #setFinish(Object)
     */
    public void setActionAndFinish(Object[] actionData)
    {
        Action action = null;
        String name   = null;
        Icon   icon   = null;
        Object finish = AUTO_CLOSE;
        
        if (actionData != null) {
            int length = actionData.length;
            int index = 0;
            
            while (index < length) {
                if (actionData[index] instanceof Action) {
                    action = (Action) actionData[index];
                    index++;
                }
                else
                if (actionData[index] instanceof String) {
                    name = (String) actionData[index];
                    index++;
                    
                    if ((index < length) && (actionData[index] instanceof Icon)) {
                        icon = (Icon) actionData[index];
                        index++;
                        
                        action = makeTrivialAction(name, icon);
                    }
                    else {
                        action = makeTrivialAction(name);
                    }
                }
                else
                if (actionData[index] == KEEP_OPEN) {
                    finish = KEEP_OPEN;
                    break; // break since this should be the last parameter
                }
                else
                if (actionData[index] == AUTO_CLOSE) {
                    finish = AUTO_CLOSE;
                    break; // break since this should be the last parameter
                }
                else
                if (actionData[index] == SET_CANCEL) {
                    finish = SET_CANCEL;
                    break; // break since this should be the last parameter
                }
                else {
                    break; // break since this parameter is unexpected
                }
            }
        }
        
        setAction(action);
        setFinish(finish);
    }
    
    ////////////////////
    // Static Methods //
    ////////////////////
    
    /*
     * <P>Makes a DialogAction object using the given dialog
     * and given action data.</P>
     *
     * <P>The parameter <CODE>actionData</CODE> is an <CODE>Object[]</CODE>
     * array that specifies each <CODE>Action</CODE> in one of the following
     * ways:</P>
     * 
     * <UL>
     *   <LI> An array { action }             with an action.
     *   <LI> An array { name }               with an action name.
     *   <LI> An array { name, icon }         with an action name and icon.
     *   <LI> An array { action, option }
     *   <LI> An array { name, option }
     *   <LI> An array { name, icon, option }
     * </UL>
     *
     * <P>The option parameter should be one of the following values 
     * that will determine what is done to the dialog 
     * when the action is finished:</P>
     *
     * <UL>
     *   <LI> DialogAction.KEEP_OPEN
     *   <LI> DialogAction.AUTO_CLOSE
     *   <LI> DialogAction.SET_CANCEL
     * </UL>
     *
     * <P>The default if the option is not specified 
     * is DialogAction.AUTO_CLOSE.</P>
     *
     * <P>A <CODE>NullPointerException</CODE> will be thrown if the
     * <CODE>GeneralDialog</CODE> object is <CODE>null</CODE>.</P>
     *
     * @param dialog the dialog to act on
     * @param actionData the array used to generate the dialog actions
     * @see #makeDialogActions(GeneralDialog, Object[][])
     */
    public static DialogAction makeDialogAction
        (GeneralDialog dialog, Object[] actionData)
    {
		if (dialog == null)
            throw new NullPointerException
                ("Null dialog passed to makeDialogAction");
        
		return new DialogAction(dialog, actionData);
    }
    
    /*
     * <P>Makes an array of DialogAction objects using the given dialog
     * and given action data array.</P>
     *
     * <P>The parameter <CODE>actionData</CODE> is an <CODE>Object[][]</CODE>
     * array that specifies each <CODE>Action</CODE> in one of the following
     * ways:</P>
     * 
     * <UL>
     *   <LI> A subarray { action }             with an action.
     *   <LI> A subarray { name }               with an action name.
     *   <LI> A subarray { name, icon }         with an action name and icon.
     *   <LI> A subarray { action, option }
     *   <LI> A subarray { name, option }
     *   <LI> A subarray { name, icon, option }
     * </UL>
     *
     * <P>The option parameter should be one of the following values 
     * that will determine what is done to the dialog 
     * when the action is finished:</P>
     *
     * <UL>
     *   <LI> DialogAction.KEEP_OPEN
     *   <LI> DialogAction.AUTO_CLOSE
     *   <LI> DialogAction.SET_CANCEL
     * </UL>
     *
     * <P>The default if the option is not specified 
     * is DialogAction.AUTO_CLOSE.</P>
     *
     * <P>All actions specified by the parameter <CODE>actionData</CODE> 
     * will be encapsulated as <CODE>DialogAction</CODE> objects.</P>
     *
     * <P>A <CODE>NullPointerException</CODE> will be thrown if the
     * <CODE>GeneralDialog</CODE> object is <CODE>null</CODE>.</P>
     *
     * @param dialog the dialog to act on
     * @param actionData the array used to generate the dialog actions
     * @see #makeDialogAction(GeneralDialog, Object[])
     */
    public static DialogAction[] makeDialogActions
        (GeneralDialog dialog, Object[][] actionData)
    {
		if (dialog == null)
            throw new NullPointerException
                ("Null dialog passed to makeDialogActions");
        
        if (actionData == null)
            return null;
        
        int length = actionData.length;
        
        DialogAction[] actionList = new DialogAction[length];
        
        for (int i = 0; i < length; i++)
            actionList[i] = makeDialogAction(dialog, actionData[i]);
        
        return actionList;
    }
    
    /**
     * Returns an action with the given name that does no work.
     *
     * @param name the name of the trivial action
     * @see #makeTrivialAction(String, Icon)
     */
    public static Action makeTrivialAction(String name) {
        return new SimpleAction(name) {
            public void perform() { }
        };
    }
    
    /**
     * Returns an action with the given name and icon that does no work.
     *
     * @param name the name of the trivial action
     * @param icon the icon of the trivial action
     * @see #makeTrivialAction(String)
     */
    public static Action makeTrivialAction(String name, Icon icon) {
        return new SimpleAction(name, icon) {
            public void perform() { }
        };
    }   
}
