/*
 * Copyright 2005-2010 Kirill Grouchnikov, based on work by
 * Sun Microsystems, Inc. All rights reserved.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */
package org.pushingpixels.substance.swingx;

import java.awt.Color;
import java.awt.Component;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.Rectangle;
import java.awt.event.MouseEvent;
import java.util.Map;

import javax.swing.ButtonModel;
import javax.swing.DefaultButtonModel;
import javax.swing.Icon;
import javax.swing.JComponent;
import javax.swing.SwingConstants;
import javax.swing.border.Border;
import javax.swing.border.CompoundBorder;
import javax.swing.border.EmptyBorder;
import javax.swing.plaf.ComponentUI;

import org.jdesktop.swingx.JXTaskPane;
import org.jdesktop.swingx.icon.EmptyIcon;
import org.jdesktop.swingx.plaf.basic.BasicTaskPaneUI;
import org.pushingpixels.lafwidget.LafWidgetUtilities;
import org.pushingpixels.substance.api.ColorSchemeAssociationKind;
import org.pushingpixels.substance.api.ComponentState;
import org.pushingpixels.substance.api.DecorationAreaType;
import org.pushingpixels.substance.api.SubstanceColorScheme;
import org.pushingpixels.substance.api.SubstanceLookAndFeel;
import org.pushingpixels.substance.internal.animation.StateTransitionTracker;
import org.pushingpixels.substance.internal.animation.TransitionAwareUI;
import org.pushingpixels.substance.internal.painter.DecorationPainterUtils;
import org.pushingpixels.substance.internal.painter.HighlightPainterUtils;
import org.pushingpixels.substance.internal.utils.RolloverControlListener;
import org.pushingpixels.substance.internal.utils.SubstanceColorSchemeUtilities;
import org.pushingpixels.substance.internal.utils.SubstanceColorUtilities;
import org.pushingpixels.substance.internal.utils.SubstanceCoreUtilities;
import org.pushingpixels.substance.internal.utils.SubstanceImageCreator;
import org.pushingpixels.substance.internal.utils.SubstanceSizeUtils;
import org.pushingpixels.substance.internal.utils.SubstanceTextUtilities;
import org.pushingpixels.substance.internal.utils.icon.TransitionAwareIcon;

/**
 * Substance-consistent UI delegate for {@link JXTaskPane}.
 * 
 * @author Kirill Grouchnikov
 */
public class SubstanceTaskPaneUI extends BasicTaskPaneUI implements
		TransitionAwareUI {
	/**
	 * Background delegate.
	 */
	private SubstanceSwingxFillBackgroundDelegate bgDelegate;

	/**
	 * Listener for thumb transition animations.
	 */
	private RolloverControlListener substanceRolloverListener;

	/**
	 * Listener for transition animations.
	 */
	protected StateTransitionTracker stateTransitionTracker;

	/**
	 * Surrogate model for the fade effects on the title pane border.
	 */
	protected ButtonModel taskPaneModel;

	public static ComponentUI createUI(JComponent comp) {
		SubstanceCoreUtilities.testComponentCreationThreadingViolation(comp);
		return new SubstanceTaskPaneUI((JXTaskPane) comp);
	}

	/**
	 * Creates a new UI delegate.
	 */
	public SubstanceTaskPaneUI(JXTaskPane taskPane) {
		super();
		this.bgDelegate = new SubstanceSwingxFillBackgroundDelegate();

		this.taskPaneModel = new DefaultButtonModel();
		this.taskPaneModel.setArmed(false);
		this.taskPaneModel.setSelected(false);
		this.taskPaneModel.setPressed(false);
		this.taskPaneModel.setRollover(false);

		this.stateTransitionTracker = new StateTransitionTracker(taskPane,
				this.taskPaneModel);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.jdesktop.swingx.plaf.basic.BasicTaskPaneUI#installListeners()
	 */
	@Override
	protected void installListeners() {
		super.installListeners();

		this.substanceRolloverListener = new RolloverControlListener(this,
				this.taskPaneModel);
		this.group.addMouseListener(this.substanceRolloverListener);
		this.group.addMouseMotionListener(this.substanceRolloverListener);

		this.stateTransitionTracker.registerModelListeners();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.jdesktop.swingx.plaf.basic.BasicTaskPaneUI#uninstallListeners()
	 */
	@Override
	protected void uninstallListeners() {
		this.group.removeMouseListener(this.substanceRolloverListener);
		this.group.removeMouseMotionListener(this.substanceRolloverListener);
		this.substanceRolloverListener = null;

		this.stateTransitionTracker.unregisterModelListeners();

		super.uninstallListeners();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.jdesktop.swingx.plaf.basic.BasicTaskPaneUI#installDefaults()
	 */
	@Override
	protected void installDefaults() {
		SubstanceLookAndFeel.setDecorationType(this.group,
				DecorationAreaType.GENERAL);

		super.installDefaults();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.jdesktop.swingx.plaf.basic.BasicTaskPaneUI#uninstallUI(javax.swing
	 * .JComponent)
	 */
	@Override
	public void uninstallUI(JComponent c) {
		DecorationPainterUtils.clearDecorationType(this.group);
		super.uninstallUI(c);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.jdesktop.swingx.plaf.basic.BasicTaskPaneUI#createPaneBorder()
	 */
	@Override
	protected Border createPaneBorder() {
		return new SubstancePaneBorder();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.jdesktop.swingx.plaf.basic.BasicTaskPaneUI#createContentPaneBorder()
	 */
	@Override
	protected Border createContentPaneBorder() {
		SubstanceColorScheme colorScheme = SubstanceColorSchemeUtilities
				.getColorScheme(group, ColorSchemeAssociationKind.BORDER,
						ComponentState.ENABLED);
		// the code below is the same as for the separators
		final Color borderColor = colorScheme.isDark() ? colorScheme
				.getDarkColor() : SubstanceColorUtilities.getInterpolatedColor(
				colorScheme.getMidColor(), colorScheme.getDarkColor(), 0.4);
		Border outer = new Border() {
			@Override
			public void paintBorder(Component c, Graphics g, int x, int y,
					int width, int height) {
				Graphics2D g2d = (Graphics2D) g.create();

				g2d.setColor(borderColor);
				// left
				g2d.drawLine(x, y, x, y + height - 1);
				// right
				g2d.drawLine(x + width - 1, y, x + width - 1, y + height - 1);
				// bottom
				g2d.drawLine(x, y + height - 1, x + width - 1, y + height - 1);

				g2d.dispose();
			}

			@Override
			public boolean isBorderOpaque() {
				return true;
			}

			@Override
			public Insets getBorderInsets(Component c) {
				// don't paint at the top
				return new Insets(0, 1, 1, 1);
			}
		};
		return new CompoundBorder(outer, new EmptyBorder(1, 2, 2, 2));
	}

	/**
	 * Pane border for task pane.
	 * 
	 * @author Kirill Grouchnikov
	 */
	protected class SubstancePaneBorder extends PaneBorder {
		protected Icon expandedIcon;

		protected Icon collapsedIcon;

		/**
		 * Creates a new pane border.
		 */
		public SubstancePaneBorder() {
			this.borderColor = SubstanceColorSchemeUtilities.getColorScheme(
					group, ColorSchemeAssociationKind.BORDER,
					ComponentState.ENABLED).getMidColor();
			TransitionAwareIcon.ColorSchemeAssociationKindDelegate colorSchemeAssociationDelegate = new TransitionAwareIcon.ColorSchemeAssociationKindDelegate() {
				@Override
				public ColorSchemeAssociationKind getColorSchemeAssociationKind(
						ComponentState state) {
					if (!state.isDisabled()
							&& (state != ComponentState.ENABLED)) {
						// use HIGHLIGHT
						return ColorSchemeAssociationKind.HIGHLIGHT;
					}
					return ColorSchemeAssociationKind.MARK;
				}
			};
			this.expandedIcon = new TransitionAwareIcon(group,
					new TransitionAwareIcon.TransitionAwareUIDelegate() {
						@Override
						public TransitionAwareUI getTransitionAwareUI() {
							return (TransitionAwareUI) group.getUI();
						}
					}, new TransitionAwareIcon.Delegate() {
						public Icon getColorSchemeIcon(
								SubstanceColorScheme scheme) {
							return SubstanceImageCreator
									.getDoubleArrowIconDelta(SubstanceSizeUtils
											.getComponentFontSize(group), 0, 3,
											0, SwingConstants.NORTH, scheme);
						}
					}, colorSchemeAssociationDelegate,
					"substance.swingx.taskpane.expanded");
			this.collapsedIcon = new TransitionAwareIcon(group,
					new TransitionAwareIcon.TransitionAwareUIDelegate() {
						@Override
						public TransitionAwareUI getTransitionAwareUI() {
							return (TransitionAwareUI) group.getUI();
						}
					}, new TransitionAwareIcon.Delegate() {
						public Icon getColorSchemeIcon(
								SubstanceColorScheme scheme) {
							return SubstanceImageCreator
									.getDoubleArrowIconDelta(SubstanceSizeUtils
											.getComponentFontSize(group), 0, 3,
											0, SwingConstants.SOUTH, scheme);
						}
					}, colorSchemeAssociationDelegate,
					"substance.swingx.taskpane.collapsed");

			// since the label is never added to this component, we need
			// to explicitly mark it as DecorationAreaType.GENERAL
			SubstanceLookAndFeel.setDecorationType(this.label,
					DecorationAreaType.GENERAL);
			// and enforce the foreground color computed in #getPaintColor
			this.label.putClientProperty(
					SubstanceTextUtilities.ENFORCE_FG_COLOR, Boolean.TRUE);
		}

		/*
		 * (non-Javadoc)
		 * 
		 * @seeorg.jdesktop.swingx.plaf.basic.BasicTaskPaneUI.PaneBorder#
		 * paintTitleBackground(org.jdesktop.swingx.JXTaskPane,
		 * java.awt.Graphics)
		 */
		@Override
		protected void paintTitleBackground(JXTaskPane group, Graphics g) {
			Graphics2D graphics = (Graphics2D) g.create();

			StateTransitionTracker.ModelStateInfo modelStateInfo = stateTransitionTracker
					.getModelStateInfo();
			Map<ComponentState, StateTransitionTracker.StateContributionInfo> activeStates = modelStateInfo
					.getStateContributionMap();
			ComponentState currState = stateTransitionTracker
					.getModelStateInfo().getCurrModelState();

			SubstanceColorScheme baseFillScheme = SubstanceColorSchemeUtilities
					.getColorScheme(group,
							ColorSchemeAssociationKind.HIGHLIGHT, currState);
			SubstanceColorScheme baseBorderScheme = SubstanceColorSchemeUtilities
					.getColorScheme(group,
							ColorSchemeAssociationKind.HIGHLIGHT_BORDER,
							currState);

			HighlightPainterUtils
					.paintHighlight(graphics, null, group, new Rectangle(0, 0,
							group.getWidth(), getTitleHeight(group)), 0.5f,
							null, baseFillScheme, baseBorderScheme);

			for (Map.Entry<ComponentState, StateTransitionTracker.StateContributionInfo> stateEntry : activeStates
					.entrySet()) {
				ComponentState activeState = stateEntry.getKey();
				if (activeState == currState)
					continue;

				float contribution = stateEntry.getValue().getContribution();
				if (contribution == 0.0f)
					continue;

				SubstanceColorScheme fillScheme = SubstanceColorSchemeUtilities
						.getColorScheme(group,
								ColorSchemeAssociationKind.HIGHLIGHT,
								activeState);
				SubstanceColorScheme borderScheme = SubstanceColorSchemeUtilities
						.getColorScheme(group,
								ColorSchemeAssociationKind.HIGHLIGHT_BORDER,
								activeState);

				graphics.setComposite(LafWidgetUtilities.getAlphaComposite(
						group, contribution, g));
				HighlightPainterUtils.paintHighlight(graphics, null, group,
						new Rectangle(0, 0, group.getWidth(),
								getTitleHeight(group)), 0.5f, null, fillScheme,
						borderScheme);
			}
			graphics.dispose();
		}

		/*
		 * (non-Javadoc)
		 * 
		 * @see
		 * org.jdesktop.swingx.plaf.basic.BasicTaskPaneUI$PaneBorder#getPaintColor
		 * (org.jdesktop.swingx.JXTaskPane)
		 */
		@Override
		protected Color getPaintColor(JXTaskPane group) {
			StateTransitionTracker.ModelStateInfo modelStateInfo = stateTransitionTracker
					.getModelStateInfo();
			Map<ComponentState, StateTransitionTracker.StateContributionInfo> activeStates = modelStateInfo
					.getStateContributionMap();
			ComponentState currState = stateTransitionTracker
					.getModelStateInfo().getCurrModelState();

			if (currState.isDisabled() || (activeStates.size() == 1))
				return getColorSchemeForState(group, currState)
						.getForegroundColor();

			float aggrRed = 0;
			float aggrGreen = 0;
			float aggrBlue = 0;

			for (Map.Entry<ComponentState, StateTransitionTracker.StateContributionInfo> activeEntry : modelStateInfo
					.getStateContributionMap().entrySet()) {
				ComponentState activeState = activeEntry.getKey();
				SubstanceColorScheme scheme = getColorSchemeForState(group,
						activeState);
				Color schemeFg = scheme.getForegroundColor();
				float contribution = activeEntry.getValue().getContribution();
				aggrRed += schemeFg.getRed() * contribution;
				aggrGreen += schemeFg.getGreen() * contribution;
				aggrBlue += schemeFg.getBlue() * contribution;
			}
			return new Color((int) aggrRed, (int) aggrGreen, (int) aggrBlue);
		}

		private SubstanceColorScheme getColorSchemeForState(JXTaskPane group,
				ComponentState state) {
			ColorSchemeAssociationKind assocKind = ColorSchemeAssociationKind.FILL;
			if (!state.isDisabled() && (state != ComponentState.ENABLED)) {
				// use HIGHLIGHT
				assocKind = ColorSchemeAssociationKind.HIGHLIGHT;
			}
			SubstanceColorScheme currScheme = SubstanceColorSchemeUtilities
					.getColorScheme(group, assocKind, state);
			return currScheme;
		}

		/*
		 * (non-Javadoc)
		 * 
		 * @seeorg.jdesktop.swingx.plaf.basic.BasicTaskPaneUI$PaneBorder#
		 * paintExpandedControls(org.jdesktop.swingx.JXTaskPane,
		 * java.awt.Graphics, int, int, int, int)
		 */
		@Override
		protected void paintExpandedControls(JXTaskPane group, Graphics g,
				int x, int y, int width, int height) {
			Icon arrowIcon = group.isCollapsed() ? collapsedIcon : expandedIcon;
			int dx = (width - arrowIcon.getIconWidth()) / 2;
			int dy = 1 + (height - arrowIcon.getIconHeight()) / 2;
			arrowIcon.paintIcon(group, g, x + dx, y + dy);
			// g.setColor(Color.red);
			// g.drawRect(x, y, width, height);
			// g.setColor(Color.green);
			// g.drawRect(x + dx, y + dy, arrowIcon.getIconWidth(), arrowIcon
			// .getIconHeight());
		}

		/*
		 * (non-Javadoc)
		 * 
		 * @seeorg.jdesktop.swingx.plaf.basic.BasicTaskPaneUI$PaneBorder#
		 * isMouseOverBorder()
		 */
		@Override
		protected boolean isMouseOverBorder() {
			return true;
		}

		/*
		 * (non-Javadoc)
		 * 
		 * @see
		 * org.jdesktop.swingx.plaf.basic.BasicTaskPaneUI$PaneBorder#paintFocus
		 * (java.awt.Graphics, java.awt.Color, int, int, int, int)
		 */
		@Override
		protected void paintFocus(Graphics g, Color paintColor, int x, int y,
				int width, int height) {
			SubstanceCoreUtilities.paintFocus(g, group, group,
					SubstanceTaskPaneUI.this, new Rectangle(x, y, width - 1,
							height - 1), label.getBounds(), 1.0f, 0);
		}

		/*
		 * (non-Javadoc)
		 * 
		 * @see
		 * org.jdesktop.swingx.plaf.basic.BasicTaskPaneUI$PaneBorder#configureLabel
		 * (org.jdesktop.swingx.JXTaskPane)
		 */
		@Override
		protected void configureLabel(JXTaskPane group) {
			label.applyComponentOrientation(group.getComponentOrientation());
			label.setFont(group.getFont());
			label.setText(group.getTitle());
			label.setIcon(group.getIcon() == null ? new EmptyIcon() : group
					.getIcon());
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see javax.swing.plaf.ComponentUI#paint(java.awt.Graphics,
	 * javax.swing.JComponent)
	 */
	@Override
	public void paint(Graphics g, JComponent c) {
		this.bgDelegate.paint(c, (Graphics2D) g, false);
	}

	@Override
	public boolean isInside(MouseEvent me) {
		return this.isInBorder(me);
	}

	@Override
	public StateTransitionTracker getTransitionTracker() {
		return this.stateTransitionTracker;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * org.jdesktop.swingx.plaf.basic.BasicTaskPaneUI#getTitleHeight(java.awt
	 * .Component)
	 */
	@Override
	protected int getTitleHeight(Component c) {
		return SubstanceSizeUtils.getAdjustedSize(SubstanceSizeUtils
				.getComponentFontSize(c), super.getTitleHeight(c), 2, 3, false);
	}
}
