package org.jcon.ui;

import org.jcon.ba.lib.LocaleMgr;
import java.awt.BorderLayout;
import java.awt.Toolkit;
import java.awt.event.*;
import javax.swing.JPanel;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;

/**
 * Provides a reusable window that presents a message and
 * choice buttons to the user. A modal dialog is used.
 * Excessively long lines are automatically wrapped if they
 * exceed 100 characters.
 * <p>
 * Since a thread is used to set the dialog to visible,
 * when the client calls ask() it will not block. If
 * askBlock() is used, it will block. Blocking is useful
 * for simple situations, but should be avoided where the
 * delay or blocked awt thread could cause problems.
 * <p>
 * The client may implement ActionListener, which has: <br>
 * <code> public void actionPerformed(ActionEvent evt) </code> <br>
 * if user response notification is desired.
 *
 * @author Jack Harich
 */
public class MessageBox implements Runnable,
    ActionListener, WindowListener, KeyListener {

//---------- Private Fields ------------------------------
private ActionListener listener;
private JDialog   dialog;
private String    closeWindowCommand = "CloseRequested";
private String    title;
private JFrame    frame;
private JPanel    buttonPanel = new JPanel();
private JPanel    imageCanvas;
private boolean   closeWindowAllowed = true;
private String    command;
private LocaleMgr localeMgr;

//---------- Initialization ------------------------------
/**
 * This convenience constructor is used to delare the
 * listener that will be notified when a button is clicked.
 * The listener must implement ActionListener.
 */
public MessageBox(ActionListener listener) {
    this();
    this.listener = listener;
}
/**
 * This constructor is used for no listener, such as for
 * a simple okay dialog or when askBlock() is used.
 */
public MessageBox() {
}
// Unit test. Shows only simple features.
public static void main(String args[]) {
    MessageBox box = new MessageBox();
    box.setTitle("Test MessageBox");
    box.useImageCanvas("LightBulb.gif");
    box.askYesNo("Tell me now.\nDo you like Java?");
}
//---------- Runnable Implementation ---------------------
/**
 * This prevents the caller from blocking on ask(), which
 * if this class is used on an awt event thread would
 * cause a deadlock in Java 1.0.
 */
public void run() {
    dialog.setVisible(true);
}
//---------- ActionListener Implementation ---------------
public void actionPerformed(ActionEvent evt) {
    command = evt.getActionCommand().intern();
    dialog.setVisible(false);
    dialog.dispose();
    frame = null;

    if (listener != null) listener.actionPerformed(evt);
}
//---------- WindowListener Implementatons ---------------
public void windowClosing(WindowEvent evt) {
    // User clicked on X or chose Close selection
    if (closeWindowAllowed) fireCloseRequested();
}
public void windowClosed(WindowEvent evt) { }
public void windowDeiconified(WindowEvent evt) { }
public void windowIconified(WindowEvent evt) { }
public void windowOpened(WindowEvent evt) { }
public void windowActivated(WindowEvent evt) {  }
public void windowDeactivated(WindowEvent evt) { }

//---------- KeyListener Implementation ------------------
public void keyTyped(KeyEvent evt) { }
public void keyPressed(KeyEvent evt) {
    if (evt.getKeyCode() == KeyEvent.VK_ESCAPE) {
        fireCloseRequested();
    }
}
public void keyReleased(KeyEvent evt) { }

private void fireCloseRequested() {
    ActionEvent event = new ActionEvent(this,
        ActionEvent.ACTION_PERFORMED, closeWindowCommand);
    actionPerformed(event);
}
//---------- Properties ----------------------------------
/**
* This property is used to localize button labels and such
* that are not user controlled. If not supplied or null
* then the default locale is used. All other text must be
* localized by the user of this class.
*/
public void setLocaleMgr(LocaleMgr localeMgr) {
	this.localeMgr = localeMgr;
}
/**
 * This set the listener to be notified of button clicks
 * and WindowClosing events.
 */
public void setActionListener(ActionListener listener) {
    this.listener = listener;
}
public void setTitle(String title) {
    this.title = title;
}
/**
 * If a Frame is provided then it is used to instantiate
 * the modal Dialog. Otherwise the last active Frame is used.
 * Providing a Frame will have the effect of putting the
 * focus back on that Frame when the MessageBox is closed
 * or a button is clicked.
 */
public void setFrame(JFrame frame) { // Optional
    this.frame = frame;
}
/**
 * Sets the ActionCommand used in the ActionEvent when the
 * user attempts to close the window. The window may be
 * closed by clicking on "X", choosing Close from the
 * window menu, or pressing the Escape key. The default
 * command is "CloseRequested", which is just what a Close
 * choice button would probably have as a command.
 */
public void setCloseWindowCommand(String command) {
    closeWindowCommand = command;
}
/**
 * If set to false there will be no reaction when the user
 * tries to close the window directly. This is useful to
 * force a choice. The default is true.
 */
public void setCloseWindowAllowed(boolean closeWindowAllowed) {
    this.closeWindowAllowed = closeWindowAllowed;
}
/**
 * This is handy for providing a small image that will be
 * displayed to the left of the message.
 */
//---------- Public Methods ------------------------------
public void useImageCanvas(JPanel imageCanvas) {
    this.imageCanvas = imageCanvas;
}
/**
 * This loads the image from the specified fileName,
 * which must be in the same directory as this class.
 * For example fileName might be "LightBulb.gif".
 */
public void useImageCanvas(String fileName) {
    try {
        JImageCanvas imageCanvas = new JImageCanvas(MessageBox.class, fileName);
        useImageCanvas(imageCanvas);
    } catch(Exception ex) {
        print(".helpfulHint() - Cannot load image " + fileName);
        ex.printStackTrace();
    }
}
/**
 * The label will be used for the button and the
 * command will be returned to the listener.
 */
public void addChoice(String label, String command) {
    JButton button = new JButton(label);
    button.setActionCommand(command);
    button.addActionListener(this);
    button.addKeyListener(this);
    buttonPanel.add(button);
}
/**
 * A convenience method that assumes the command is the
 * same as the label.
 */
public void addChoice(String label) {
    addChoice(label, label);
}
/**
 * One of the "ask" methods must be the last call when
 * using a MessageBox. This is the simplest "ask" method.
 * It presents the provided message.
 */
public void ask(String message) {
    prepareDialog(message);
    // Start a new thread to show the dialog
    Thread thread = new Thread(this);
    thread.start();
}
/**
 * Same as ask() except adds an "Okay" button.
 */
public void askOkay(String message) {
    addChoice(getText("Okay"));
    ask(message);
}
/**
 * Same as ask() except adds "Yes" and "No"
 * buttons and sets CloseWindowCommand to "No";
 */
public void askYesNo(String message) {
    addChoice(getText("Yes"));
    addChoice(getText("No"));
    setCloseWindowCommand(getText("No"));
    ask(message);
}
/**
 * Asks the message and blocks until the user clicks a
 * button or closes the window (if permitted). After that
 * the interned action command is returned and the dialog
 * is disposed. Same as ask() but blocks.
 */
public String askBlock(String message) {
    prepareDialog(message);
    // The following line blocks until the dialog is not visible
    dialog.setVisible(true);
    // Now return the action command
    return command;
}
/**
 * Same as askOkay() but blocks.
 */
public String askOkayBlock(String message) {
    addChoice(getText("Okay"));
    return askBlock(message);
}
/**
 * Same as askYesNo() but blocks.
 */
public String askYesNoBlock(String message) {
    addChoice(getText("Yes"));
    addChoice(getText("No"));
    setCloseWindowCommand("No");
    return askBlock(message);
}
//---------- Private Methods -----------------------------
private String getText(String textName) {
	if (localeMgr == null) {
		return textName;
	} else {
		return localeMgr.getString("UtilitiesJCON",
			textName + "Text");
	}
}
// Prepares but does not show the dialog
private void prepareDialog(String message) {
    if (frame == null) {
        dialog = WindowMgr.createDialog(true);
    } else {
        dialog = new JDialog(frame, true);
    }
    dialog.addWindowListener(this);
    dialog.addKeyListener(this);
    dialog.setTitle(title);
    dialog.getContentPane().setLayout(new BorderLayout(5, 5));

    JPanel messagePanel =
        VisualLib.createMultiLinePanel(message);

    if (imageCanvas == null) {
        dialog.getContentPane().add("Center", messagePanel);
    } else {
        JPanel centerPanel = new JPanel();
        centerPanel.add(imageCanvas);
        centerPanel.add(messagePanel);
        dialog.getContentPane().add("Center", centerPanel);
    }
    dialog.getContentPane().add("South", buttonPanel);
    dialog.pack();
    WindowLib.enforceMinimumSize(dialog, 200, 100);
    WindowLib.centerOverActiveWindow(dialog);
    Toolkit.getDefaultToolkit().beep();
}
//--- Std
private static void print(String text) {
    System.out.println("MessageBox" + text);
}

} // End class
