 /*
* Copyright 2003-2011 Tufts University  Licensed under the
 * Educational Community License, Version 2.0 (the "License"); you may
 * not use this file except in compliance with the License. You may
 * obtain a copy of the License at
 * 
 * http://www.osedu.org/licenses/ECL-2.0
 * 
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an "AS IS"
 * BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */


package tufts.vue.gui;

import tufts.vue.DEBUG;

import java.awt.Component;
import java.awt.Dimension;
import java.awt.Color;
import java.awt.Insets;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GradientPaint;
import java.awt.RenderingHints;
import java.awt.AlphaComposite;
import javax.swing.Icon;
import javax.swing.AbstractButton;
import javax.swing.border.*;

/**
 * VueButtonIcon
 *
 * Icon class to support the various states of VUE buttons (rollover, pressed, disabled, etc).
 * Takes a raw icon which is used for painting the body of all the button states, but adjust borders,
 * transparency, etc, based on button state.  Can also handle a null raw icon for just drawing borders.
 * Can install, via installGenerated, a set of icons into any AbstractButton using our default VUE GUI scheme.
 *
 * @version $Revision: 1.16 $ / $Date: 2010-02-03 19:15:46 $ / $Author: mike $
 * @author Scott Fraize
 */

public class VueButtonIcon implements Icon
{
    public static final int UP = 0;        // unselected/default
    public static final int PRESSED = 1;   // only while being held down by a mouse press
    public static final int SELECTED = 2;  // selected (for toggle buttons)
    public static final int DISABLED = 3;  // disabled
    public static final int ROLLOVER = 4;  // rollover
    public static final int MENU = 5;              // sub-menus: default (palette menu)
    public static final int MENU_SELECTED = 6;     // sub-menus: rollover (palette menu)
            
    public static void installGenerated(AbstractButton b, Icon raw, Dimension s) {
    	installGenerated(b,raw,raw,s);
    }
    
    public static void installGenerated(AbstractButton b, Icon raw, Icon selected,Dimension s) {
        installGenerated(b,raw,selected,raw,s);
    }
    
    public static void installGenerated(AbstractButton b, Icon raw, Icon selected,Icon rollover,Dimension s) {
        if (DEBUG.INIT||DEBUG.TOOL) System.out.println(b + " generating button states from " + raw);
        if (s == null)
            s = new Dimension(0,0);

        if (GUI.isMacAqua() && b instanceof MenuButton) {
            b.setIcon(raw);
        } else {

            b.setIcon(new VueButtonIcon(raw, UP, s));
            b.setPressedIcon(new VueButtonIcon(raw, PRESSED, s));
            b.setSelectedIcon(new VueButtonIcon(selected, SELECTED, s));
            b.setDisabledIcon(new VueButtonIcon(raw, DISABLED, s));
            b.setRolloverIcon(new VueButtonIcon(rollover, ROLLOVER, s));
        }
    }

    private static final Color sButtonColor = new Color(222,222,222);
    private static final Color sOverColor = Color.gray;
    private static final Color sDownColor = sOverColor;
    private static final AbstractBorder sRolloverBorder = new EtchedBorder();
    //private static final AbstractBorder sRolloverBorder = new EtchedBorder(EtchedBorder.RAISED);
    //private static final AbstractBorder sRolloverBorder = new BevelBorder(BevelBorder.LOWERED);
    //private static final AbstractBorder sRolloverBorder = new SoftBevelBorder(BevelBorder.LOWERED);
    private static final AlphaComposite DisabledAlpha = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.4f);
    
    protected final int width;
    protected final int height;

    private static Insets insets = new Insets(0,0,0,0);
    private Color mColor = sButtonColor;

    private final int mType;
    private final Icon mRawIcon;
    private final boolean isPressIcon;
    protected boolean isRadioButton = false; // if in an exclusive button-group

    // OffsetWhenDown: nudge the icon when in the down state.
    // Set to true of "up" state appears as a button -- can
    // turn on otherwise but will need to adjust whole button so
    // icon stays centered.
    private final static boolean OffsetWhenDown = false;
    private final static boolean debug = false;

    public VueButtonIcon(Icon rawIcon, int t, int width, int height)
    {
    	
        this.mRawIcon = rawIcon;
        this.mType = t;
        if (rawIcon != null) {
            if (tufts.Util.isMacLeopard()) {
                // TODO: not sure why we need this -- may have to do with clipping
                this.width = width <= 0 ? rawIcon.getIconWidth() + 6 : width;
                this.height = height <= 0 ? rawIcon.getIconHeight() + 6 : height;
            } else {
                this.width = width <= 0 ? rawIcon.getIconWidth() + 4 : width;
                this.height = height <= 0 ? rawIcon.getIconHeight() + 4 : height;
            }
        } else {
            this.width = width;
            this.height = height;
        }
        this.isPressIcon = (t == PRESSED || t == SELECTED || t == MENU_SELECTED);
        
        if (isPressIcon)
            mColor = Color.lightGray;
        if (debug) {
            if (t == MENU) mColor = Color.pink;
            if (t == MENU_SELECTED) mColor = Color.magenta;
            if (t == ROLLOVER) mColor = Color.green;
        }
        //if (t >= MENU) insets = new Insets(-1,-1,1,1);
    }
            
    public VueButtonIcon(Icon rawIcon, int t, Dimension size) {
        this(rawIcon, t, size.width, size.height);
    }
    public VueButtonIcon(Icon rawIcon, int t) {
        this(rawIcon, t, 0, 0);
    }
        
    public int getIconWidth() { return width; }
    public int getIconHeight() { return height; }

    private final static String _types[] = {"Up","Pressed","Selected","Disabled","Rollover","Menu","Menu Selected"};
	
    /**
     * paint the entire button as an icon plus it's visible icon graphic
     */
    public void paintIcon(Component c, Graphics g, int x, int y) {
        if (debug||DEBUG.PAINT) System.out.println("painting "
                                                   + mRawIcon + " type " + mType + "(" + _types[mType]
                                                   + ") on " + c + " bg=" + c.getBackground());
        Graphics2D g2 = (Graphics2D) g;
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        //   if (mType >= MENU) {
            // the drop-down menus have GC bugs on the PC such
            // that we need to be sure to paint something in
            // the entire region, or we appear to get another
            // version of the icon painted *under* us.
            g2.setColor(c.getBackground());
            g2.fillRect(0,0, width,height);
//        }

        if (debug) {
            g2.setColor(Color.red);
            g2.fillRect(0,0, 99,99);
        }
        x += insets.top;
        y += insets.left;
        //if (VueUtil.isMacPlatform()) { x += 2; y += 2; }// try now that attend to x/y above
        g2.translate(x,y);

        int w = width - (insets.left + insets.right);
        int h = height - (insets.top + insets.bottom);
                
        float gw = width;
        //GradientPaint gradient = new GradientPaint(gw/2,0,Color.white,gw/2,h/2,mColor,true);
        //GradientPaint gradient = new GradientPaint(gw/6,0,Color.white,gw/2,h/2,mColor,true);
        GradientPaint gradient = new GradientPaint(gw/6,0,Color.white,gw*.33f,h/2,mColor,true);
        // Set gradient for the whole button.

        if (isPressIcon) {
            // Draw the 3d button border -- raised/lowered depending on down state                
            g2.setColor(c.getBackground());
            g2.draw3DRect(0,0, w-1,h-1, !isPressIcon);
        } else if (mType == ROLLOVER) {
            // Draw an etched rollover border:

            if (DEBUG.BOXES) {
                g.setColor(Color.green);
                g.fillRect(0,0, w, h);
            }

            if (true) {
                if (tufts.Util.isMacLeopard()) {
                    // TODO: not sure why we need this -- may have to do with clipping
                    sRolloverBorder.paintBorder(c, g, 0, 2, w, h-2);
                } else {
                    sRolloverBorder.paintBorder(c, g, 0, 0, w, h);
                }
            } else {
                // experiment in separate border for drop-down portions
                sRolloverBorder.paintBorder(c, g, 0, 0, w-8, h);
                sRolloverBorder.paintBorder(c, g, w-7, 0, 7, h);
            }
            
            // this make it look like button-pressed:
            //g2.draw3DRect(0,0, w-1,h-1, false);
        }

        // now fill the icon, but don't fill if we're just holding the mouse down on a grouped toggle button
        // (so the group won't ever show two buttons in the fully selected state at once)
        if (isPressIcon && (!isRadioButton || mType != PRESSED)) {
            g2.setPaint(gradient);
            g2.fillRect(1,1, w-2,h-2);
        }
        else
            g2.setColor(mColor);
        //g2.fillRect(1,1, w-2,h-2);
        // skipping the fill here creates the flush-look
                
        // if we're down, nudge icon
        if (OffsetWhenDown && isPressIcon)
            g2.translate(1,1);

        // now draw the actual graphic in the center
        if (mRawIcon != null) {
            int ix = (w - mRawIcon.getIconWidth()) / 2;
            int iy = (h - mRawIcon.getIconHeight()) / 2;
            if (mType == DISABLED)
                g2.setComposite(DisabledAlpha);
            if (DEBUG.BOXES) {
                g2.setColor(Color.red);
                g2.fillRect(ix, iy, mRawIcon.getIconWidth(), mRawIcon.getIconHeight());
            }
            drawGraphic(c, g2, ix, iy);
        }
    }

    // can be overriden to do anything really fancy
    void drawGraphic(Component c, Graphics2D g, int x, int y)
    {
        mRawIcon.paintIcon(c, g, x, y);
    }

    public String toString()
    {
        return "VueButtonIcon[" + mType + " " + mRawIcon + "]";
    }
}
    
