/*
 * Copyright (c) 2005-2010 Substance 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 Substance 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.substance.internal.painter;

import java.awt.*;

import javax.swing.*;

import org.pushingpixels.lafwidget.LafWidgetUtilities;
import org.pushingpixels.substance.api.*;
import org.pushingpixels.substance.api.painter.decoration.SubstanceDecorationPainter;
import org.pushingpixels.substance.api.watermark.SubstanceWatermark;
import org.pushingpixels.substance.internal.utils.SubstanceCoreUtilities;

/**
 * Contains utility methods related to decoration painters. This class is for
 * internal use only.
 * 
 * @author Kirill Grouchnikov
 */
public class DecorationPainterUtils {
	/**
	 * Client property for marking a component with an instance of
	 * {@link DecorationAreaType} enum.
	 */
	private static final String DECORATION_AREA_TYPE = "substancelaf.internal.painter.decorationAreaType";

	/**
	 * Sets the decoration type of the specified component.
	 * 
	 * @param comp
	 *            Component.
	 * @param type
	 *            Decoration type of the component and its children.
	 */
	public static void setDecorationType(JComponent comp,
			DecorationAreaType type) {
		comp.putClientProperty(DECORATION_AREA_TYPE, type);
	}

	/**
	 * Clears the client properties related to the decoration area type.
	 * 
	 * @param comp
	 *            Component.
	 */
	public static void clearDecorationType(JComponent comp) {
		if (comp != null) {
			comp.putClientProperty(DECORATION_AREA_TYPE, null);
		}
	}

	/**
	 * Returns the decoration area type of the specified component. The
	 * component and its ancestor hierarchy are scanned for the registered
	 * decoration area type. If
	 * {@link #setDecorationType(JComponent, DecorationAreaType)} has been
	 * called on the specified component, the matching decoration type is
	 * returned. Otherwise, the component hierarchy is scanned to find the
	 * closest ancestor that was passed to
	 * {@link #setDecorationType(JComponent, DecorationAreaType)} - and its
	 * decoration type is returned. If neither the component, nor any one of its
	 * parent components has been passed to the setter method,
	 * {@link DecorationAreaType#NONE} is returned.
	 * 
	 * @param comp
	 *            Component.
	 * @return Decoration area type of the component.
	 */
	public static DecorationAreaType getDecorationType(Component comp) {
		Component c = comp;
		while (c != null) {
			if (c instanceof JComponent) {
				JComponent jc = (JComponent) c;
				Object prop = jc.getClientProperty(DECORATION_AREA_TYPE);
				if (prop instanceof DecorationAreaType) {
					return (DecorationAreaType) prop;
				}
			}
			c = c.getParent();
		}
		return DecorationAreaType.NONE;
	}

	/**
	 * Returns the immediate decoration area type of the specified component.
	 * The component is checked for the registered decoration area type. If
	 * {@link #setDecorationType(javax.swing.JComponent, org.pushingpixels.substance.api.DecorationAreaType)} was
	 * not called on this component, this method returns <code>null</code>.
	 * 
	 * @param comp
	 *            Component.
	 * @return Immediate decoration area type of the component.
	 */
	public static DecorationAreaType getImmediateDecorationType(Component comp) {
		Component c = comp;
		if (c instanceof JComponent) {
			JComponent jc = (JComponent) c;
			Object prop = jc.getClientProperty(DECORATION_AREA_TYPE);
			if (prop instanceof DecorationAreaType)
				return (DecorationAreaType) prop;
		}
		return null;
	}

	/**
	 * Paints the decoration background on the specified component. The
	 * decoration background is not painted when the <code>force</code>
	 * parameter is <code>false</code> and at least one of the following
	 * conditions holds:
	 * <ul>
	 * <li>The component is in a cell renderer.</li>
	 * <li>The component is not showing on the screen.</li>
	 * <li>The component is in the preview mode.</li>
	 * </ul>
	 * 
	 * @param g
	 *            Graphics context.
	 * @param c
	 *            Component.
	 * @param force
	 *            If <code>true</code>, the painting of decoration background is
	 *            enforced.
	 */
	public static void paintDecorationBackground(Graphics g, Component c,
			boolean force) {
		DecorationAreaType decorationType = SubstanceLookAndFeel
				.getDecorationType(c);
		paintDecorationBackground(g, c, decorationType, force);
	}

	/**
	 * Paints the decoration background on the specified component. See comments
	 * on {@link #paintDecorationBackground(Graphics, Component, boolean)} for
	 * the cases when the decoration background painting is skipped.
	 * 
	 * @param g
	 *            Graphics context.
	 * @param c
	 *            Component.
	 * @param decorationType
	 *            Decoration area type of the component.
	 * @param force
	 *            If <code>true</code>, the painting of decoration background is
	 *            enforced. #see
	 *            {@link #paintDecorationBackground(Graphics, Component, boolean)}
	 */
	public static void paintDecorationBackground(Graphics g, Component c,
			DecorationAreaType decorationType, boolean force) {
		// System.out.println("Painting " + c.getClass().getSimpleName());
		boolean isInCellRenderer = (SwingUtilities.getAncestorOfClass(
				CellRendererPane.class, c) != null);
		boolean isPreviewMode = false;
		if (c instanceof JComponent) {
			isPreviewMode = (Boolean.TRUE.equals(((JComponent) c)
					.getClientProperty(LafWidgetUtilities.PREVIEW_MODE)));
		}

		if (!force && !isPreviewMode && !c.isShowing() && !isInCellRenderer) {
			return;
		}

		if ((c.getHeight() == 0) || (c.getWidth() == 0))
			return;

		SubstanceSkin skin = SubstanceCoreUtilities.getSkin(c);
		SubstanceDecorationPainter painter = skin.getDecorationPainter();

		Graphics2D g2d = (Graphics2D) g.create();
		painter.paintDecorationArea(g2d, c, decorationType, c.getWidth(), c
				.getHeight(), skin);

		SubstanceWatermark watermark = SubstanceCoreUtilities.getSkin(c)
				.getWatermark();
		if ((watermark != null) && !isPreviewMode && !isInCellRenderer
				&& c.isShowing() && SubstanceCoreUtilities.toDrawWatermark(c)) {
			// paint the watermark over the component
			watermark.drawWatermarkImage(g2d, c, 0, 0, c.getWidth(), c
					.getHeight());

			// paint the background second time with 50%
			// translucency, making the watermark' bleed' through.
			g2d.setComposite(LafWidgetUtilities.getAlphaComposite(c, 0.5f, g));
			painter.paintDecorationArea(g2d, c, decorationType, c.getWidth(), c
					.getHeight(), skin);
		}
		g2d.dispose();
	}
}
