/* Copyright 2000 by Basil Vandegriend.  All rights reserved. */

package com.basilv.core.richui;

import java.awt.Font;
import java.awt.event.ActionEvent;

import javax.swing.*;
import javax.swing.border.Border;

import com.basilv.core.Assert;

/**
 * A collection of useful constants and functions for working with a Swing GUI.
 */
public class GuiUtilities
{

	public static void initializeGui() {
		try {
			UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
		} catch (Exception e) {
			throw new RuntimeException("Error initializing GUI look & feel.", e);
		}
		ExceptionHandler.registerExceptionHandler();
	}
	
	public static final int STANDARD_SPACING = 5;
	
	/**
     * Create an empty border with standard spacing (5 pixels on each side).
     * @return the created border.
     */
    public static Border createEmptySpacingBorder() {
        return BorderFactory.createEmptyBorder(
            STANDARD_SPACING, STANDARD_SPACING, STANDARD_SPACING, STANDARD_SPACING);
    }

	
	/**
	 * Create and return a non-editable text field.
	 * 
	 * @param numColumns  The number of columns wide to make the field.
	 *        Approximately equals the number of characters that can be
	 *        displayed. Should be >= 0.
	 * @return the created textfield. Will not be null.
	 */
	public static JTextField createNonEditableTextField(int numColumns) {
		Assert.greaterThanZero("numColumns", numColumns);

		JTextField field = new JTextField(numColumns) {
			private static final long serialVersionUID = 1L;

			@Override
			public boolean isFocusable() {
				return isEditable();
			}
		};
		field.setEditable(false);
		field.setMaximumSize(field.getPreferredSize());

		return field;
	}

	/**
	 * Create and return a fixed size text field that looks like a label.
	 * 
	 * @param numColumns  The number of columns wide to make the field.
	 *        Approximately equals the number of characters that can be
	 *        displayed. Should be >= 0.
	 * @return the created textfield. Will not be null.
	 */
	public static JTextField createLabelField(int numColumns) {
		Assert.greaterThanZero("numColumns", numColumns);

		JTextField field = createNonEditableTextField(numColumns);
		field.setBorder(BorderFactory.createEmptyBorder());

		// TODO: Can we actually use a JLabel but have a different layout manager for it?

		// Set text to look like a label
		JLabel label = new JLabel();
		field.setFont(label.getFont());
		field.setForeground(label.getForeground());
		field.setBackground(label.getBackground());
		return field;
	}

	/**
	 * Create and return a fixed size text field that looks like a label and
	 * that contains the specified text.
	 * 
	 * @param numColumns  The number of columns wide to make the field.
	 *        Approximately equals the number of characters that can be
	 *        displayed. Should be >= 0.
	 * @param labelText  The text to display.
	 * @return the created textfield. Will not be null.
	 */
	public static JTextField createLabelField(int numColumns, String labelText) {
		Assert.greaterThanZero("numColumns", numColumns);

		JTextField field = createLabelField(numColumns);
		field.setText(labelText);
		return field;
	}

    /**
     * Create a label with the specified name and mnemonic.
     * @param labelName  Cannot be null or empty.
     * @param labelMnemonic  Should be a member of KeyEvent.VK_?
     * @return the created label.
     */
    public static JLabel createLabelWithMnemonic(String labelName, int labelMnemonic) {
        assert labelName != null && labelName.length() > 0;

        JLabel label = new JLabel(labelName);
        label.setDisplayedMnemonic(labelMnemonic);
        return label;
    }

    /**
     * Add a label and associated component to a panel.
     * @param panel  Cannot be null.
     * @param label  Cannot be null.
     * @param component  Cannot be null.
     */
    public static void addLabelComponentPair(JPanel panel, JLabel label, JComponent component) {
        assert panel != null;
        assert label != null;
        assert component != null;
        
        label.setLabelFor(component);
        panel.add(label);
        panel.add(component);
    }

    /**
     * Create a header label that uses a larger, bold font.
     * @param labelText Cannot be null or empty.
     * @return the created label.
     */
    public static JLabel createHeaderLabel(String labelText) {
        assert labelText != null && labelText.length() > 0;
        
        final float fontSizeIncrease = 4;
        JLabel label = new JLabel(labelText);
        Font font = label.getFont();
        Font newFont = font.deriveFont(Font.BOLD, font.getSize2D() + fontSizeIncrease);
        label.setFont(newFont);
        
        return label;
    }

    /**
     * Set the accelerator key for the specified action.
     * @param action  Cannot be null
     * @param keyEvent  The key to trigger the action.
     * @param keyModifiers  The modifier for the key (i.e. control, alt). 
     */
    public static void setAcceleratorKey(Action action, int keyEvent, int keyModifiers) {
        assert action != null;
        
        action.putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(keyEvent,
                keyModifiers));
    }

    /**
     * Add the specified action to the specified menu. If the specified mnemonic
     * is not null, make it the mnemonic for the created menu item.
     * 
     * @param menu  Cannot be null
     * @param action  Cannot be null
     * @param mnemonic May be null
     * @return the menu item that was created. Will not be null.
     */
    public static JMenuItem addActionToMenu(JMenu menu, Action action, Character mnemonic) {
        assert menu != null;
        assert action != null;
        
        JMenuItem menuItem = menu.add(action);
        if (mnemonic != null) {
            menuItem.setMnemonic(mnemonic.charValue());
        }
        return menuItem;
    }

	
	/**
	 * Create and return an action named 'Exit' that will exit the application.
	 */
	public static Action getExitApplicationAction() {
		return new ExitAction();
	}

	private static class ExitAction extends AbstractAction
	{
		private static final long serialVersionUID = 1L;
	
		public ExitAction() {
			super("Exit");
		}
	
		public void actionPerformed(ActionEvent e) {
			System.exit(0);
		}
	}

	
} // class GuiUtilities
