/*
 * $Id$
 *
 * Copyright 2009 Sun Microsystems, Inc., 4150 Network Circle,
 * Santa Clara, California 95054, U.S.A. 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.jdesktop.swingx.plaf.synth;

import java.awt.Graphics;
import java.awt.Rectangle;
import java.beans.PropertyChangeEvent;

import javax.swing.JComponent;
import javax.swing.LookAndFeel;
import javax.swing.UIManager;
import javax.swing.plaf.synth.ColorType;
import javax.swing.plaf.synth.Region;
import javax.swing.plaf.synth.SynthConstants;
import javax.swing.plaf.synth.SynthContext;
import javax.swing.plaf.synth.SynthLookAndFeel;
import javax.swing.plaf.synth.SynthPainter;
import javax.swing.plaf.synth.SynthStyle;

/**
 * Utility class as stand-in for package private synth utility methods.
 * 
 * @author Jeanette Winzenburg
 */
public class SynthUtils {

//----------------------- context-related
    
    /**
     * Used to avoid null painter checks everywhere.
     */
    private static SynthPainter NULL_PAINTER = new SynthPainter() {};
    
    /**
     * Returns a SynthContext with the specified values. 
     *
     * @param component JComponent
     * @param region Identifies the portion of the JComponent
     * @param style Style associated with the component
     * @param state State of the component as defined in SynthConstants.
     * @return a SynthContext with the specified values.
     * 
     * @throws NullPointerException if component, region of style is null.
     * 
     */
    public static SynthContext getContext(JComponent c, Region region, SynthStyle style, int state) {
        return new SynthContext(c, region, style, state);
    }

    /**
     * @param context
     * @param style
     * @return
     */
    public static SynthContext getContext(SynthContext context, SynthStyle style) {
        if (context.getStyle().equals(style)) return context;
        return getContext(context.getComponent(), context.getRegion(), style, context.getComponentState());
    }
    /**
     * Returns a context with the given component state and all other fields same as input context.
     * 
     * @param context the context, must not be null 
     * @param state the component state.
     * @return a context with the given component state and other fields as inpu context.
     */
    public static SynthContext getContext(SynthContext context, int state) {
        if (context.getComponentState() == state) return context;
        return getContext(context.getComponent(), context.getRegion(), context.getStyle(), state);
    }
    
    /**
     * Returns a SynthPainter from the context's style. Fall-back to default if 
     * none available.
     * 
     * @param context SynthContext containing the style, must not be null.
     * @return a SynthPainter from the context's style, or a default if null.
     */
    public static SynthPainter getPainter(SynthContext context) {
        SynthPainter painter = context.getStyle().getPainter(context);
        return painter != null ? painter : NULL_PAINTER;
    }
    
//------------------- style-related
    
    /**
     * Returns true if the Style should be updated in response to the
     * specified PropertyChangeEvent. This forwards to
     * <code>shouldUpdateStyleOnAncestorChanged</code> as necessary.
     */
    public static boolean shouldUpdateStyle(PropertyChangeEvent event) {
        String eName = event.getPropertyName();
        if ("name" == eName) {
            // Always update on a name change
            return true;
        }
        else if ("componentOrientation" == eName) {
            // Always update on a component orientation change
            return true;
        }
        else if ("ancestor" == eName && event.getNewValue() != null) {
            // Only update on an ancestor change when getting a valid
            // parent and the LookAndFeel wants this.
            LookAndFeel laf = UIManager.getLookAndFeel();
            return (laf instanceof SynthLookAndFeel &&
                    ((SynthLookAndFeel)laf).
                     shouldUpdateStyleOnAncestorChanged());
        }
        // Note: The following two nimbus based overrides should be refactored
        // to be in the Nimbus LAF. Due to constraints in an update release,
        // we couldn't actually provide the public API necessary to allow
        // NimbusLookAndFeel (a subclass of SynthLookAndFeel) to provide its
        // own rules for shouldUpdateStyle.
        else if ("Nimbus.Overrides" == eName) {
            // Always update when the Nimbus.Overrides client property has
            // been changed
            return true;
        }
        else if ("Nimbus.Overrides.InheritDefaults" == eName) {
            // Always update when the Nimbus.Overrides.InheritDefaults
            // client property has changed
            return true;
        }
        else if ("JComponent.sizeVariant" == eName) {
            // Always update when the JComponent.sizeVariant
            // client property has changed
            return true;
        }
        return false;
    }
    
//--------------- component related
    
    public static int getComponentState(JComponent c) {
        if (c.isEnabled()) {
            if (c.isFocusOwner()) {
                return SynthConstants.ENABLED | SynthConstants.FOCUSED;
            }
            return SynthConstants.ENABLED;
        }
        return SynthConstants.DISABLED;
    }

    // ---------------- divers ...

    /**
     * A convenience method that handles painting of the background. All SynthUI
     * implementations should override update and invoke this method.
     * 
     * @param context must not be null
     * @param g must not be null
     */
    public static void update(SynthContext context, Graphics g) {
        update(context, g, null);
    }
    
    /**
     * A convenience method that handles painting of the background. All SynthUI
     * implementations should override update and invoke this method.
     * 
     * @param context must not be null
     * @param g must not be null
     * @param the bounds to fill, may be null to indicate the complete size
     */
    public static void update(SynthContext context, Graphics g, Rectangle bounds) {
        JComponent c = context.getComponent();
        SynthStyle style = context.getStyle();
        int x, y, width, height;

        if (bounds == null) {
            x = 0;
            y = 0;
            width = c.getWidth();
            height = c.getHeight();
        } else {
            x = bounds.x;
            y = bounds.y;
            width = bounds.width;
            height = bounds.height;
        }

        // Fill in the background, if necessary.
        boolean subregion = context.getRegion().isSubregion();
        if ((subregion && style.isOpaque(context))
                || (!subregion && c.isOpaque())) {
            g.setColor(style.getColor(context, ColorType.BACKGROUND));
            g.fillRect(x, y, width, height);
        }
    }

}

