/*
 * @(#)JideButton.java
 *
 * Copyright 2002-2003 JIDE Software Inc. All rights reserved.
 */
package com.jidesoft.swing;

import com.jidesoft.plaf.LookAndFeelFactory;
import com.jidesoft.plaf.UIDefaultsLookup;
import com.jidesoft.plaf.basic.ThemePainter;
import com.jidesoft.utils.ColorUtils;

import javax.swing.*;
import java.awt.*;

/**
 * JideButton is a replacement for JButton when it is used on toolbar (or command bar in the case of JIDE Action
 * Framework).
 */
public class JideButton extends JButton implements Alignable, ButtonStyle, ComponentStateSupport, AlignmentSupport {

    private static final String uiClassID = "JideButtonUI";

    /**
     * Bound property name for always show hyperlink property.
     */
    public static final String PROPERTY_ALWAYS_SHOW_HYPERLINK = "alwaysShowHyperlink";

    private boolean _alwaysShowHyperlink = false;

    private int _buttonStyle = TOOLBAR_STYLE;
    private Cursor _savedCursor;

    /**
     * By default, if a JideButton is added to a popup menu, clicking on the button will dismiss the popup menu. However
     * if you change the default behavior, you can use this client property and set it to Boolean.FALSE.
     */
    public static final String CLIENT_PROPERTY_HIDE_POPUPMENU = "JideButton.hidePopupMenu";

    /**
     * Creates a button with no set text or icon.
     */
    public JideButton() {
        this(null, null);
    }

    /**
     * Creates a button with an icon.
     *
     * @param icon the Icon image to display on the button
     */
    public JideButton(Icon icon) {
        this(null, icon);
    }

    /**
     * Creates a button with text.
     *
     * @param text the text of the button
     */
    public JideButton(String text) {
        this(text, null);
    }

    /**
     * Creates a button where properties are taken from the <code>Action</code> supplied.
     *
     * @param a the <code>Action</code> used to specify the new button
     * @since 1.3
     */
    public JideButton(Action a) {
        this();
        setAction(a);
    }

    /**
     * Creates a button with initial text and an icon.
     *
     * @param text the text of the button
     * @param icon the Icon image to display on the button
     */
    public JideButton(String text, Icon icon) {
        // Create the model
        setModel(new DefaultButtonModel());
        // initialize
        init(text, icon);
        setRolloverEnabled(true);
        setFocusable(true);
        setRequestFocusEnabled(false);
    }

    /**
     * Resets the UI property to a value from the current look and feel.
     *
     * @see JComponent#updateUI
     */
    @Override
    public void updateUI() {
        if (UIDefaultsLookup.get(uiClassID) == null) {
            LookAndFeelFactory.installJideExtension();
        }
        setUI(UIManager.getUI(this));
    }


    /**
     * Returns a string that specifies the name of the L&F class that renders this component.
     *
     * @return the string "ButtonUI"
     *
     * @see JComponent#getUIClassID
     * @see UIDefaults#getUI
     */
    @Override
    public String getUIClassID() {
        return uiClassID;
    }

    private int _orientation;

    /**
     * The button orientation.
     *
     * @return the orientation.
     */
    public int getOrientation() {
        return _orientation;
    }

    public void setOrientation(int orientation) {
        if (_orientation != orientation) {
            int old = _orientation;
            _orientation = orientation;
            firePropertyChange(PROPERTY_ORIENTATION, old, orientation);
        }
    }

    /**
     * return true if it supports vertical orientation.
     *
     * @return true if it supports vertical orientation
     */
    public boolean supportVerticalOrientation() {
        return true;
    }

    /**
     * return true if it supports horizontal orientation.
     *
     * @return true if it supports horizontal orientation
     */
    public boolean supportHorizontalOrientation() {
        return true;
    }

    /**
     * Gets the button style.
     *
     * @return the button style.
     */
    public int getButtonStyle() {
        return _buttonStyle;
    }

    /**
     * Sets the button style.
     *
     * @param buttonStyle one of the following values: {@link #TOOLBAR_STYLE} (default), {@link #TOOLBOX_STYLE}, {@link
     *                    #FLAT_STYLE} and {@link #HYPERLINK_STYLE}.
     */
    public void setButtonStyle(int buttonStyle) {
        if (buttonStyle < 0 || buttonStyle > HYPERLINK_STYLE) {
            throw new IllegalArgumentException("Only TOOLBAR_STYLE, TOOLBOX_STYLE, FLAT_STYLE and HYPERLINK_STYLE are supported");
        }
        if (buttonStyle == _buttonStyle)
            return;

        int oldStyle = _buttonStyle;
        _buttonStyle = buttonStyle;

        configureCursor();
        firePropertyChange(BUTTON_STYLE_PROPERTY, oldStyle, _buttonStyle);
    }

    private void configureCursor() {
        if (getButtonStyle() == HYPERLINK_STYLE
                && isRolloverEnabled()
                && ((getText() != null && getText().length() > 0) || getIcon() != null)) {
            _savedCursor = getCursor();
            setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
        }
        else {
            if (_savedCursor != null) {
                setCursor(_savedCursor);
                _savedCursor = null;
            }
        }
    }

    @Override
    public void setRolloverEnabled(boolean b) {
        super.setRolloverEnabled(b);
        configureCursor();
    }

    @Override
    public void setText(String text) {
        super.setText(text);
        configureCursor();
    }

    @Override
    public void setIcon(Icon defaultIcon) {
        super.setIcon(defaultIcon);
        configureCursor();
    }

    /**
     * Checks the alwaysShowHyperlink property value.
     *
     * @return true if the hyperlink is always visible. False if the hyperlink will be visible only when mouse rolls
     *         over.
     */
    public boolean isAlwaysShowHyperlink() {
        return _alwaysShowHyperlink;
    }

    /**
     * Sets the property if hyperlink (the underline) should be visible all the time. By default the hyperlink is
     * visible when mouse is over the button. If set to true, the hyperlink will always be visible.
     * <p/>
     * Please notes, this is an option only available when button style is set to HYPERLINK_STYLE.
     *
     * @param alwaysShowHyperlink a boolean value. True means the button will always show hyperlink. False means it will
     *                            show hyperlink only when mouse is over the button.
     */
    public void setAlwaysShowHyperlink(boolean alwaysShowHyperlink) {
        if (_alwaysShowHyperlink != alwaysShowHyperlink) {
            boolean old = _alwaysShowHyperlink;
            _alwaysShowHyperlink = alwaysShowHyperlink;
            firePropertyChange(PROPERTY_ALWAYS_SHOW_HYPERLINK, old, alwaysShowHyperlink);
        }
    }

    private Color _defaultForeground;
    private Color _rolloverBackground;
    private Color _selectedBackground;
    private Color _pressedBackground;
    private Color _rolloverForeground;
    private Color _selectedForeground;
    private Color _pressedForeground;

    public Color getDefaultForeground() {
        return _defaultForeground;
    }

    public void setDefaultForeground(Color defaultForeground) {
        _defaultForeground = defaultForeground;
    }

    private Color getRolloverBackground() {
        return _rolloverBackground;
    }

    private void setRolloverBackground(Color rolloverBackground) {
        _rolloverBackground = rolloverBackground;
    }

    private Color getSelectedBackground() {
        return _selectedBackground;
    }

    private void setSelectedBackground(Color selectedBackground) {
        _selectedBackground = selectedBackground;
    }

    private Color getPressedBackground() {
        return _pressedBackground;
    }

    private void setPressedBackground(Color pressedBackground) {
        _pressedBackground = pressedBackground;
    }

    private Color getRolloverForeground() {
        return _rolloverForeground;
    }

    private void setRolloverForeground(Color rolloverForeground) {
        _rolloverForeground = rolloverForeground;
    }

    private Color getSelectedForeground() {
        return _selectedForeground;
    }

    private void setSelectedForeground(Color selectedForeground) {
        _selectedForeground = selectedForeground;
    }

    private Color getPressedForeground() {
        return _pressedForeground;
    }

    private void setPressedForeground(Color pressedForeground) {
        _pressedForeground = pressedForeground;
    }

    /**
     * Gets the background for different states. The states are defined in ThemePainter as constants. Not all states are
     * supported by all components. If the state is not supported or background is never set, it will return null.
     * <p/>
     * Please note, each L&F will have its own way to paint the different backgrounds. This method allows you to
     * customize it for each component to use a different background. So if you want the background to be used, don't
     * use a ColorUIResource because UIResource is considered as a setting set by the L&F and any L&F can choose to
     * ignore it.
     *
     * @param state the button state. Valid values are ThemePainter.STATE_DEFAULT, ThemePainter.STATE_ROLLOVER,
     *              ThemePainter.STATE_SELECTED and ThemePainter.STATE_PRESSED.
     * @return the background for different states.
     */
    public Color getBackgroundOfState(int state) {
        switch (state) {
            case ThemePainter.STATE_DEFAULT:
                return getBackground();
            case ThemePainter.STATE_ROLLOVER:
                return getRolloverBackground();
            case ThemePainter.STATE_SELECTED:
                return getSelectedBackground();
            case ThemePainter.STATE_DISABLE_SELECTED:
                Color background = getSelectedBackground();
                return background != null ? ColorUtils.toGrayscale(background) : background;
            case ThemePainter.STATE_PRESSED:
                return getPressedBackground();
        }
        return null;
    }

    /**
     * Sets the background for different states.  The states are defined in ThemePainter as constants. Not all states
     * are supported by all components. If the state is not supported or background is never set, it will return null.
     * <p/>
     * Please note, each L&F will have its own way to paint the different backgrounds. This method allows you to
     * customize it for each component to use a different background. So if you want the background to be used, don't
     * use a ColorUIResource because UIResource is considered as a setting set by the L&F and any L&F can choose to
     * ignore it.
     *
     * @param state the button state. Valid values are ThemePainter.STATE_DEFAULT, ThemePainter.STATE_ROLLOVER,
     *              ThemePainter.STATE_SELECTED and ThemePainter.STATE_PRESSED.
     * @param color the new background for the state.
     */
    public void setBackgroundOfState(int state, Color color) {
        switch (state) {
            case ThemePainter.STATE_DEFAULT:
                setBackground(color);
                break;
            case ThemePainter.STATE_ROLLOVER:
                setRolloverBackground(color);
                break;
            case ThemePainter.STATE_SELECTED:
                setSelectedBackground(color);
                break;
            case ThemePainter.STATE_PRESSED:
                setPressedBackground(color);
                break;
        }
    }

    /**
     * Gets the foreground for different states. The states are defined in ThemePainter as constants. Not all states are
     * supported by all components. If the state is not supported or foreground is never set, it will return null.
     * <p/>
     * Please note, each L&F will have its own way to paint the different foregrounds. This method allows you to
     * customize it for each component to use a different foreground. So if you want the foreground to be used, don't
     * use a ColorUIResource because UIResource is considered as a setting set by the L&F and any L&F can choose to
     * ignore it.
     *
     * @param state the button state. Valid values are ThemePainter.STATE_DEFAULT, ThemePainter.STATE_ROLLOVER,
     *              ThemePainter.STATE_SELECTED and ThemePainter.STATE_PRESSED.
     * @return the foreground for different states.
     */
    public Color getForegroundOfState(int state) {
        switch (state) {
            case ThemePainter.STATE_DEFAULT:
                return getDefaultForeground();
            case ThemePainter.STATE_ROLLOVER:
                return getRolloverForeground();
            case ThemePainter.STATE_SELECTED:
                return getSelectedForeground();
            case ThemePainter.STATE_PRESSED:
                return getPressedForeground();
        }
        return null;
    }


    /**
     * Sets the foreground for different states.  The states are defined in ThemePainter as constants. Not all states
     * are supported by all components. If the state is not supported or foreground is never set, it will return null.
     * <p/>
     * Please note, each L&F will have its own way to paint the different foregrounds. This method allows you to
     * customize it for each component to use a different foreground. So if you want the foreground to be used, don't
     * use a ColorUIResource because UIResource is considered as a setting set by the L&F and any L&F can choose to
     * ignore it.
     *
     * @param state the button state. Valid values are ThemePainter.STATE_DEFAULT, ThemePainter.STATE_ROLLOVER,
     *              ThemePainter.STATE_SELECTED and ThemePainter.STATE_PRESSED.
     * @param color the new foreground for the state.
     */
    public void setForegroundOfState(int state, Color color) {
        switch (state) {
            case ThemePainter.STATE_DEFAULT:
                setDefaultForeground(color);
                break;
            case ThemePainter.STATE_ROLLOVER:
                setRolloverForeground(color);
                break;
            case ThemePainter.STATE_SELECTED:
                setSelectedForeground(color);
                break;
            case ThemePainter.STATE_PRESSED:
                setPressedForeground(color);
                break;
        }
    }
}
