/*
 * @(#)PDFButtonBorder.java  2.0  2008-05-10
 *
 * Copyright (c) 2005-2008 Werner Randelshofer, Immensee, Switzerland.
 * All rights reserved.
 *
 * You may not use, copy or modify this file, except in compliance with the
 * license agreement you entered into with Werner Randelshofer.
 * For details see accompanying license terms.
 */
package ch.randelshofer.pdf.plaf;

import org.monte.media.image.Images;
import ch.randelshofer.gui.border.OverlayBorder;
import ch.randelshofer.gui.border.FocusBorder;
import ch.randelshofer.gui.border.AnimatedBorder;
import java.awt.Component;
import java.awt.Graphics;
import java.awt.Insets;
import java.awt.image.BufferedImage;

import java.net.URL;
import javax.swing.AbstractButton;
import javax.swing.JComponent;
import javax.swing.JToolBar;
import javax.swing.UIManager;
import javax.swing.border.Border;
import javax.swing.border.CompoundBorder;
import javax.swing.border.EmptyBorder;
import javax.swing.plaf.UIResource;


/**
 * PDFButtonBorder.
 * This border uses client properties and font sizes of a JComponent to
 * determine which style the border shall have.
 * For some styles, the JComponent should honour size constrictions.
 * <p>
 * The following values of the client property <code>Quaqua.Button.style</code>
 * are supported:
 * <ul>
 * <li><code>push</code> Rounded push button. Maximum height of the JComponent
 * shall be constrained to its preferred height.</li>
 * <li><code>square</code> Square button. No size constraints.</li>
 * <li><code>placard</code> or <code>gradient</code> Placard button. No size constraints.</li>
 * <li><code>colorWell</code> Square button with color area in the center.
 * No size constraints.</li>
 * <li><code>bevel</code> Rounded Bevel button. No size constraints.</li>
 * <li><code>toggle</code> or <code>segmented</code> Toggle button. Maximum height of the JComponent
 * shall be constrained to its preferred height.</li>
 * <li><code>toggleWest</code> West Toggle button. Maximum height of the JComponent
 * shall be constrained to its preferred height.</li>
 * <li><code>toggleEast</code> East Toggle button. Maximum height of the JComponent
 * shall be constrained to its preferred height.</li>
 * <li><code>toggleCenter</code> Center Toggle button. Maximum height of the JComponent
 * shall be constrained to its preferred height.</li>
 * <li><code>toolBar</code> ToolBar button. No size constraints.</li>
 * <li><code>toolBarTab</code> ToolBar Tab button. No size constraints.</li>
 * <li><code>toolBarRollover</code> ToolBar button with rollover effect. No size constraints.</li>
 * </ul>
 * If the <code>Quaqua.Button.style</code> property is missing, then the
 * following values of the client property <code>JButton.buttonType</code>
 * are supported:
 * <ul>
 * <li><code>text</code> Rounded push button. Maximum height of the JComponent
 * shall be constrained to its preferred height.</li>
 * <li><code>toolBar</code> Square button. No size constraints.</li>
 * <li><code>icon</code> Rounded Bevel button. No size constraints.</li>
 * </ul>
 *
 * @author  Werner Randelshofer
 * @version 2.0 2008-05-10 Added support for "JButton.buttonType"="gradient",
 * and "JButton.segmentPosition"="first","middle" and "last" for compatibility 
 * with AquaLookAndFeel.
 * <br>1.5.1 2008-03-17 Slightly reduced margins of rounded push button.
 * <br>1.5 2007-12-18 Fixed visual margins. 
 * <br>1.4 2006-12-24 by Karl von Randow: Use Images class to create artwork.
 * <br>1.2 2005-01-04 "placard" style added. Tweaked visual margin of
 * regular push button and of small push button.
 * <br>1.1 2005-09-06 Create all borders lazily.
 * <br>1.1 2005-09-04 "help" style added.
 * <br>1.0.2 2005-05-06 Fixed "toolbar" property.
 * <br>1.0  29 March 2005  Created.
 */
public class PDFButtonBorder implements Border, UIResource {
    // Shared borders

    private static Border regularPushButtonBorder;
    private static Border smallPushButtonBorder;
    private static Border squareBorder;
    private static Border placardBorder;
    private static Border colorWellBorder;
    private static Border bevelBorder;
    private static Border toolBarBorder;
    private static Border toolBarRolloverBorder;
    private static Border toolBarTabBorder;
    private static Border toggleWestBorder;
    private static Border toggleEastBorder;
    private static Border toggleCenterBorder;
    private static Border toggleBorder;
    private static Border helpBorder;
    private static Border tableHeaderBorder;
    /**
     * The default client property value to be used, when no client property
     * has been specified for the JComponent.
     */
    private String defaultStyle;

    /** Creates a new instance. */
    public PDFButtonBorder(String defaultStyle) {
        this.defaultStyle = defaultStyle;
    }

    public Border getBorder(JComponent c) {
        Border b = null;
        String style = getStyle(c);

        // Explicitly chosen styles
                b = getRegularPushButtonBorder();
        return b;
    }

    private Border getRegularPushButtonBorder() {
        if (regularPushButtonBorder == null) {
            Insets borderInsets = new Insets(3, 8, 3, 8);
            String location = "images/Button.default.png";
        URL resource = PDFButtonBorder.class.getResource(location);
        if (resource == null) {
            System.err.println("Warning: PDFButtonBorder couldn't find resource for "+location);
            return null;
        }
            BufferedImage[] imageFrames = Images.split(
                    Images.createImage(resource),
                    12, true);
            Border[] borderFrames = new Border[12];
            for (int i = 0; i < 12; i++) {
                borderFrames[i] = PDFBorderFactory.create(
                        imageFrames[i],
                        new Insets(11, 13, 13, 13),
                        borderInsets,
                        true);
            }
            location = "images/Button.borders.png";
         resource = PDFButtonBorder.class.getResource(location);
        if (resource == null) {
            System.err.println("Warning: PDFButtonBorder couldn't find resource for "+location);
            return null;
        }
            PDFButtonStateBorder buttonStateBorder = new PDFButtonStateBorder(
                    Images.split(
                    Images.createImage(resource),
                    10, true),
                    new Insets(11, 13, 13, 13),
                    borderInsets,
                    true);

            buttonStateBorder.setBorder(
                    PDFButtonStateBorder.DEFAULT,
                    new AnimatedBorder(borderFrames, 100));

            location = "images/Button.focusRing.png";
         resource = PDFButtonBorder.class.getResource(location);
        if (resource == null) {
            System.err.println("Warning: PDFButtonBorder couldn't find resource for "+location);
            return null;
        }
            regularPushButtonBorder = new CompoundBorder(
                    //new VisualMargin(2, 3, 2, 3),
                    new VisualMargin(0, 0, 0, 0),
                    new CompoundBorder(
                    new EmptyBorder(-2, -4, -2, -4),
                    new OverlayBorder(
                    buttonStateBorder,
                    new FocusBorder(
                    PDFBorderFactory.create(
                    Images.createImage(resource),
                    new Insets(12, 13, 12, 13),
                    borderInsets,
                    false)))));
        }
        return regularPushButtonBorder;
    }

    /**
     * Returns the default button margin for the specified component.
     *
     * FIXME: We should not create a new Insets instance on each method call.
     */
    public Insets getDefaultMargin(JComponent c) {
        Insets margin = null;
        String style = getStyle(c);
        boolean isSmall = PDFUtilities.isSmallSizeVariant(c);

        // Explicitly chosen styles
        if (style.equals("text") || style.equals("push")) {
            if (isSmall) {
                margin = new Insets(1, 3, 1, 3);
            } else {
                margin = new Insets(1, 6, 2, 6);
            }
        } else if (style.equals("toolBar")) {
            margin = new Insets(0, 0, 0, 0);
        } else if (style.equals("toolBarRollover")) {
            margin = new Insets(0, 0, 0, 0);
        } else if (style.equals("toolBarTab")) {
            margin = new Insets(0, 0, 0, 0);
        } else if (style.equals("square")) {
            if (isSmall) {
                margin = new Insets(1, 6, 1, 6);
            } else {
                margin = new Insets(1, 6, 2, 6);
            }
        } else if (style.equals("placard") || style.equals("gradient")) {
            if (isSmall) {
                margin = new Insets(1, 6, 1, 6);
            } else {
                margin = new Insets(1, 6, 2, 6);
            }
        } else if (style.equals("colorWell")) {
            if (isSmall) {
                margin = new Insets(1, 6, 1, 6);
            } else {
                margin = new Insets(1, 6, 2, 6);
            }
        } else if (style.equals("icon") || style.equals("bevel")) {
            if (isSmall) {
                margin = new Insets(1, 6, 1, 6);
            } else {
                margin = new Insets(1, 6, 2, 6);
            }
        } else if (style.equals("toggle")) {
            if (isSmall) {
                margin = new Insets(1, 5, 1, 5);
            } else {
                margin = new Insets(1, 5, 2, 5);
            }
        } else if (style.equals("toggleEast")) {
            if (isSmall) {
                margin = new Insets(1, 5, 1, 5);
            } else {
                margin = new Insets(1, 5, 2, 5);
            }
        } else if (style.equals("toggleCenter")) {
            if (isSmall) {
                margin = new Insets(1, 5, 1, 5);
            } else {
                margin = new Insets(1, 5, 2, 5);
            }
        } else if (style.equals("toggleWest")) {
            if (isSmall) {
                margin = new Insets(1, 5, 1, 5);
            } else {
                margin = new Insets(1, 5, 2, 5);
            }
        } else if (style.equals("help")) {
            margin = new Insets(0, 0, 0, 0);

        // Implicit styles
        } else if (c.getParent() instanceof JToolBar) {
            margin = new Insets(0, 0, 0, 0);
        } else {
            if (isSmall) {
                margin = new Insets(1, 4, 1, 4);
            } else {
                margin = new Insets(1, 8, 2, 8);
            }
        }
        return margin;
    }

    public boolean isFixedHeight(JComponent c) {
        String style = getStyle(c).toLowerCase();
        return style.equals("text") || style.equals("push") || style.startsWith("toggle");
    }

    protected String getStyle(JComponent c) {
        String style = (String) c.getClientProperty("Quaqua.Button.style");
        if (style == null) {
            style = (String) c.getClientProperty("JButton.buttonType");
        }
        if (style == null || style.equals("segmented") || style.equals("toggle")) {
            String segmentPosition = (String) c.getClientProperty("JButton.segmentPosition");
            if (segmentPosition != null) {
                if (segmentPosition.equals("first")) {
                    style = "toggleWest";
                } else if (segmentPosition.equals("middle")) {
                    style = "toggleCenter";
                } else if (segmentPosition.equals("last")) {
                    style = "toggleEast";
                }
            }
        }
        if (style == null) {
            style = defaultStyle;
        }
        return style;
    }

    /**
     * Returns true, if this border has a visual cue for the pressed
     * state of the button.
     * If the border has no visual cue, then the ButtonUI has to provide
     * it by some other means.
     */
    public boolean hasPressedCue(JComponent c) {
        Border b = getBorder(c);
        return b != toolBarBorder;
    }

    public Insets getVisualMargin(Component c) {
        Border b = getBorder((JComponent) c);
        Insets visualMargin = new Insets(0, 0, 0, 0);

        if (b instanceof VisualMargin) {
            visualMargin = ((VisualMargin) b).getVisualMargin(c);
        } else if (b instanceof CompoundBorder) {
            b = ((CompoundBorder) b).getOutsideBorder();
            if (b instanceof VisualMargin) {
                visualMargin = ((VisualMargin) b).getVisualMargin(c);
            }
        }
        return visualMargin;
    }

    /**
     * Returns true, if this border has a visual cue for the disabled
     * state of the button.
     * If the border has no visual cue, then the ButtonUI has to provide
     * it by some other means.
     * /
     * public boolean hasDisabledCue(JComponent c) {
     * return false;
     * }*/
    public Insets getBorderInsets(Component c) {
        if (c instanceof JComponent) {
            JComponent jc = (JComponent) c;
            Insets insets = (Insets) jc.getClientProperty("Quaqua.Border.insets");
            if (insets != null) {
                return (Insets) insets.clone();
            }
        }

        boolean isBorderPainted = true;
        if (c instanceof AbstractButton) {
            isBorderPainted = ((AbstractButton) c).isBorderPainted();
        }
        Insets insets;
        if (!isBorderPainted) {
            insets = (Insets) UIManager.getInsets("Component.visualMargin").clone();
        } else {
            insets = getBorder((JComponent) c).getBorderInsets(c);
            if (c instanceof AbstractButton) {
                AbstractButton b = (AbstractButton) c;
                Insets margin = b.getMargin();
                if (margin == null || (margin instanceof UIResource)) {
                    margin = getDefaultMargin((JComponent) c);
                }
                if (margin != null) {
                    insets.top += margin.top;
                    insets.left += margin.left;
                    insets.bottom += margin.bottom;
                    insets.right += margin.right;
                }
            }
        }
        return insets;
    }

    public boolean isBorderOpaque() {
        return false;
    }

    public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) {
        getBorder((JComponent) c).paintBorder(c, g, x, y, width, height);
    }
}
