/*
 * Copyright (c) 2005-2010 Flamingo Kirill Grouchnikov. All Rights Reserved.
 *
 * Redistribution and use in source and binary forms, with or without 
 * modification, are permitted provided that the following conditions are met:
 * 
 *  o Redistributions of source code must retain the above copyright notice, 
 *    this list of conditions and the following disclaimer. 
 *     
 *  o Redistributions in binary form must reproduce the above copyright notice, 
 *    this list of conditions and the following disclaimer in the documentation 
 *    and/or other materials provided with the distribution. 
 *     
 *  o Neither the name of Flamingo Kirill Grouchnikov nor the names of 
 *    its contributors may be used to endorse or promote products derived 
 *    from this software without specific prior written permission. 
 *     
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 
 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
 */
package org.pushingpixels.flamingo.internal.ui.ribbon;

import java.awt.*;
import java.awt.image.BufferedImage;

import javax.swing.*;
import javax.swing.border.Border;
import javax.swing.plaf.*;
import javax.swing.plaf.basic.BasicGraphicsUtils;

import org.pushingpixels.flamingo.api.common.popup.PopupPanelManager;
import org.pushingpixels.flamingo.api.common.popup.PopupPanelManager.PopupEvent;
import org.pushingpixels.flamingo.api.ribbon.JRibbon;
import org.pushingpixels.flamingo.api.ribbon.RibbonContextualTaskGroup;
import org.pushingpixels.flamingo.internal.ui.common.BasicCommandToggleButtonUI;
import org.pushingpixels.flamingo.internal.utils.*;

/**
 * Basic UI for toggle button of ribbon tasks {@link JRibbonTaskToggleButton}.
 * 
 * @author Kirill Grouchnikov
 */
public class BasicRibbonTaskToggleButtonUI extends BasicCommandToggleButtonUI {
	protected PopupPanelManager.PopupListener popupListener;

	/*
	 * (non-Javadoc)
	 * 
	 * @see javax.swing.plaf.ComponentUI#createUI(javax.swing.JComponent)
	 */
	public static ComponentUI createUI(JComponent c) {
		return new BasicRibbonTaskToggleButtonUI();
	}

	@Override
	protected void installDefaults() {
		super.installDefaults();
		Font f = this.commandButton.getFont();
		if (f == null || f instanceof UIResource) {
			this.commandButton.setFont(FlamingoUtilities.getFont(null,
					"Ribbon.font", "Button.font", "Panel.font"));
		}

		Border border = this.commandButton.getBorder();
		if (border == null || border instanceof UIResource) {
			Border toInstall = UIManager
					.getBorder("RibbonTaskToggleButton.border");
			if (toInstall == null)
				toInstall = new BorderUIResource.EmptyBorderUIResource(1, 12,
						1, 12);
			this.commandButton.setBorder(toInstall);
		}

		this.commandButton.setFlat(true);
		this.commandButton.setOpaque(false);
	}

	@Override
	protected void installListeners() {
		super.installListeners();

		this.popupListener = new PopupPanelManager.PopupListener() {
			@Override
			public void popupShown(PopupEvent event) {
				if (event.getSource() == commandButton) {
					commandButton.getActionModel()
							.setSelected(isTaskSelected());
				}
			}

			@Override
			public void popupHidden(PopupEvent event) {
				if (event.getSource() == commandButton) {
					commandButton.getActionModel()
							.setSelected(isTaskSelected());
				}
			}

			private boolean isTaskSelected() {
				JRibbon ribbon = (JRibbon) SwingUtilities.getAncestorOfClass(
						JRibbon.class, commandButton);
				if (ribbon == null)
					return false;

				return ribbon.getSelectedTask() == ((JRibbonTaskToggleButton) commandButton)
						.getRibbonTask();
			}
		};
		PopupPanelManager.defaultManager().addPopupListener(this.popupListener);
	}

	@Override
	protected void uninstallListeners() {
		PopupPanelManager.defaultManager().removePopupListener(
				this.popupListener);
		this.popupListener = null;

		super.uninstallListeners();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see javax.swing.plaf.ComponentUI#update(java.awt.Graphics,
	 * javax.swing.JComponent)
	 */
	@Override
	public void update(Graphics g, JComponent c) {
		Graphics2D g2d = (Graphics2D) g.create();
		RenderingUtils.installDesktopHints(g2d);
		this.layoutInfo = this.layoutManager.getLayoutInfo(this.commandButton,
				g);
		this.paintButtonBackground(g2d, new Rectangle(0, 0, c.getWidth(), c
				.getHeight() + 10));
		this.paintText(g2d);
		g2d.dispose();
	}

	protected void paintText(Graphics g) {
		FontMetrics fm = g.getFontMetrics();
		String toPaint = this.commandButton.getText();

		// compute the insets
		int fullInsets = this.commandButton.getInsets().left;
		int pw = this.getPreferredSize(this.commandButton).width;
		int mw = this.getMinimumSize(this.commandButton).width;
		int w = this.commandButton.getWidth();
		int h = this.commandButton.getHeight();
		int insets = fullInsets - (pw - w) * (fullInsets - 2) / (pw - mw);

		// and the text rectangle
		Rectangle textRect = new Rectangle(insets,
				1 + (h - fm.getHeight()) / 2, w - 2 * insets, fm.getHeight());

		// show the first characters that fit into the available text rectangle
		while (true) {
			if (toPaint.length() == 0)
				break;
			int strWidth = fm.stringWidth(toPaint);
			if (strWidth <= textRect.width)
				break;
			toPaint = toPaint.substring(0, toPaint.length() - 1);
		}
		BasicGraphicsUtils.drawString(g, toPaint, -1, textRect.x, textRect.y
				+ fm.getAscent());
	}

	/**
	 * Paints the button background.
	 * 
	 * @param graphics
	 *            Graphics context.
	 * @param toFill
	 *            Rectangle to fill.
	 * @param button
	 *            The button itself.
	 */
	@Override
	protected void paintButtonBackground(Graphics graphics, Rectangle toFill) {
		JRibbon ribbon = (JRibbon) SwingUtilities.getAncestorOfClass(
				JRibbon.class, this.commandButton);

		this.buttonRendererPane.setBounds(toFill.x, toFill.y, toFill.width,
				toFill.height);
		ButtonModel model = this.rendererButton.getModel();
		model.setEnabled(this.commandButton.isEnabled());
		model.setSelected(false);
		// System.out.println(toggleTabButton.getText() + ":"
		// + toggleTabButton.isSelected());

		// selected task toggle button should not have any background if
		// the ribbon is minimized and it is not shown in a popup
		boolean displayAsSelected = this.commandButton.getActionModel()
				.isSelected();
		model.setRollover(displayAsSelected
				|| this.commandButton.getActionModel().isRollover());
		model.setPressed(false);
		if (model.isRollover()) {
			Graphics2D g2d = (Graphics2D) graphics.create();
			// partial translucency if it is not selected
			if (!this.commandButton.getActionModel().isSelected()) {
				g2d.setComposite(AlphaComposite.SrcOver.derive(0.4f));
			}
			g2d.translate(toFill.x, toFill.y);

			Color contextualGroupHueColor = ((JRibbonTaskToggleButton) this.commandButton)
					.getContextualGroupHueColor();
			boolean isContextualTask = (contextualGroupHueColor != null);
			if (!isContextualTask) {
				Shape clip = g2d.getClip();
				g2d.clip(FlamingoUtilities.getRibbonTaskToggleButtonOutline(
						toFill.width, toFill.height, 2));
				this.buttonRendererPane.paintComponent(g2d,
						this.rendererButton, this.commandButton, toFill.x
								- toFill.width / 2, toFill.y - toFill.height
								/ 2, 2 * toFill.width, 2 * toFill.height, true);
				g2d.setColor(FlamingoUtilities.getBorderColor().darker());
				g2d.setClip(clip);
				g2d.draw(FlamingoUtilities.getRibbonTaskToggleButtonOutline(
						toFill.width, toFill.height + 1, 2));
			} else {
				// draw to an offscreen image, colorize and draw the colorized
				// image
				BufferedImage offscreen = FlamingoUtilities.getBlankImage(
						toFill.width, toFill.height);
				Graphics2D offscreenGraphics = offscreen.createGraphics();
				Shape clip = g2d.getClip();
				offscreenGraphics.clip(FlamingoUtilities
						.getRibbonTaskToggleButtonOutline(toFill.width,
								toFill.height, 2));
				this.buttonRendererPane.paintComponent(offscreenGraphics,
						this.rendererButton, this.commandButton, toFill.x
								- toFill.width / 2, toFill.y - toFill.height
								/ 2, 2 * toFill.width, 2 * toFill.height, true);
				offscreenGraphics.setColor(FlamingoUtilities.getBorderColor()
						.darker());
				offscreenGraphics.setClip(clip);
				offscreenGraphics.draw(FlamingoUtilities
						.getRibbonTaskToggleButtonOutline(toFill.width,
								toFill.height + 1, 2));
				offscreenGraphics.dispose();

				ColorShiftFilter filter = new ColorShiftFilter(
						contextualGroupHueColor,
						RibbonContextualTaskGroup.HUE_ALPHA);
				BufferedImage colorized = filter.filter(offscreen, null);
				g2d.drawImage(colorized, 0, 0, null);
			}
			g2d.dispose();
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * javax.swing.plaf.basic.BasicButtonUI#getPreferredSize(javax.swing.JComponent
	 * )
	 */
	@Override
	public Dimension getPreferredSize(JComponent c) {
		JRibbonTaskToggleButton b = (JRibbonTaskToggleButton) c;

		Icon icon = b.getIcon();
		String text = b.getText();

		Font font = b.getFont();
		FontMetrics fm = b.getFontMetrics(font);

		Rectangle iconR = new Rectangle();
		Rectangle textR = new Rectangle();
		Rectangle viewR = new Rectangle(Short.MAX_VALUE, Short.MAX_VALUE);

		SwingUtilities.layoutCompoundLabel(b, fm, text, icon,
				SwingUtilities.CENTER, b.getHorizontalAlignment(),
				SwingUtilities.CENTER, SwingUtilities.CENTER, viewR, iconR,
				textR, (text == null ? 0 : 6));

		Rectangle r = iconR.union(textR);

		Insets insets = b.getInsets();
		r.width += insets.left + insets.right;
		r.height += insets.top + insets.bottom;

		return r.getSize();
	}

	@Override
	public Dimension getMinimumSize(JComponent c) {
		JRibbonTaskToggleButton b = (JRibbonTaskToggleButton) c;

		Icon icon = b.getIcon();
		String text = "Www";

		Font font = b.getFont();
		FontMetrics fm = b.getFontMetrics(font);

		Rectangle iconR = new Rectangle();
		Rectangle textR = new Rectangle();
		Rectangle viewR = new Rectangle(Short.MAX_VALUE, Short.MAX_VALUE);

		SwingUtilities.layoutCompoundLabel(b, fm, text, icon,
				SwingUtilities.CENTER, b.getHorizontalAlignment(),
				SwingUtilities.CENTER, SwingUtilities.CENTER, viewR, iconR,
				textR, (text == null ? 0 : 6));

		Rectangle r = iconR.union(textR);

		Insets insets = b.getInsets();
		r.width += 4;
		r.height += insets.top + insets.bottom;

		return r.getSize();
	}
}
