/*
 * @(#)LayeredComponent.java    2.6.0   11 June 2007
 *
 * Copyright 2007
 * College of Computer and Information Science
 * Northeastern University
 * Boston, MA  02115
 *
 * The Java Power Tools software may be used for educational
 * purposes as long as this copyright notice is retained intact
 * at the top of all source files.
 *
 * To discuss possible commercial use of this software, 
 * contact Richard Rasala at Northeastern University, 
 * College of Computer and Information Science,
 * 617-373-2462 or rasala@ccs.neu.edu.
 *
 * The Java Power Tools software has been designed and built
 * in collaboration with Viera Proulx and Jeff Raab.
 *
 * Should this software be modified, the words "Modified from 
 * Original" must be included as a comment below this notice.
 *
 * All publication rights are retained.  This software or its 
 * documentation may not be published in any media either
 * in whole or in part without explicit permission.
 *
 * This software was created with support from Northeastern 
 * University and from NSF grant DUE-9950829.
 */

package edu.neu.ccs.gui;

import edu.neu.ccs.*;

import java.awt.*;
import javax.swing.*;


/**
 * <p>Class <code>LayeredComponent</code> encapsulates a
 * <code>JComponent</code> which is in the foreground of the panel
 * and a background which consists of multiple layers.</p>
 * 
 * <p>The multiple layers of the background are provided
 * as follows:</p>
 * 
 * <ul>
 *   <li>The bottom layer of the background is given by a
 *       <code>Paint</code> object that is used to paint
 *       the background initially.</li>
 *   <li>The middle layer of the background is a paintable
 *       that will tile the background repeatedly.</li>
 *   <li>The topmost layer of the background is a paintable
 *       that will be painted once and that may be centered
 *       if desired.</li>
 * </ul>
 * 
 * <p>Both the repeated tile and the topmost paintable
 * may be provided as general objects.  In that case, they are
 * converted to <code>Paintable</code> via
 * <code>ComponentFactory.makePaintable</code>.</p>
 * 
 * <p>Any of the background layers may be <code>null</code>.
 * If all layers are <code>null</code>, however, this panel is
 * not very useful.  On the other hand, if all layers are
 * non-<code>null</code>, the background may be too busy and
 * may overwhelm the foreground.  Hence, this facility must be
 * used with good taste.</p>
 * 
 * <p>The user of this class should not add additional components
 * to an instance of this class beyond those installed by the
 * constructor of the instance and should not change the layout
 * manager.  Methods of the class will permit the paint, the
 * repeat tile, the topmost paintable, and the opacity to be
 * changed, and when this happens the panel will repaint.</p>
 * 
 * <p>When the foreground <code>JComponent</code> is passed to the
 * constructor of this class, the opaqueness of the foreground is
 * set to false via <code>setOpaque(false)</code>.  This is what
 * allows the background to show through.  This call is not made
 * recursively since it is up to the user of this class to decide
 * what to do about opaqueness inside the foreground component.</p>
 * 
 * <p>Experiment has shown that, for some Java components, things
 * do not render properly if the opaqueness is set to false.  This
 * problem, if it occurs, cannot be resolved by this class.  Hence,
 * thorough testing is recommended.</p>
 * 
 * @author Richard Rasala
 * @version 2.6.0
 */
public class LayeredComponent
    extends JComponent
{
    /**
     * The foreground component on which this layered component
     * is built.
     */
    protected JComponent foreground = null;
    
    /** The lower layer of this layered component. */
    protected PaintableComponent lowerLayer = null;
    
    /** The paintable used as the topmost object. */
    protected Paintable topmostPaintable = null;
    
    /** The paintable used as the tile. */
    protected Paintable tilePaintable = null;
    
    /** The corner of the topmost object. */
    protected XPoint2D corner = null;
    
    /** The innermost Tile helper object. */
    protected Tile tile1 = new Tile();
    
    /** The outermost Tile helper object. */
    protected Tile tile2 = new Tile(tile1);
    
    /** Whether or not the topmost object should be centered. */ 
    protected boolean centered = false;
    
    
    /**
     * <p>Constructs a <code>LayeredComponent</code>.</p>
     * 
     * <p>See the introduction to this class for detailed discussion.</p>
     * 
     * @param foreground the foreground component
     * @param topmost    the paintable in      the topmost background layer
     * @param tile       the paintable to tile the middle  background layer
     * @param paint      the paint to paint    the bottom  background layer
     * @param opacity    the opacity of the background layers
     * @param centered   whether or not the topmost object is centered
     */
    public LayeredComponent
        (JComponent foreground,
         Object     topmost,
         Object     tile,
         Paint      paint,
         float      opacity,
         boolean    centered)
    {
        if (foreground == null) {
            String message =
                "Null foreground component passed to LayeredComponent";
            
            throw new NullPointerException(message);
        }
        
        this.foreground = foreground;
        
        foreground.setOpaque(false);
        
        Dimension size = foreground.getPreferredSize();
        
        int w = size.width;
        int h = size.height;
        
        XRect bounds = new XRect(0, 0, w, h);
        
        tile2.setDefaultBounds2D(bounds);
        tile2.setOpacity(opacity);
        
        this.centered = centered;
        
        setTopmostPaintable(topmost);
        setBackgroundPaint(paint);
        setBackgroundTile(tile);
        
        setLayout(new AbsoluteLayout());
        
        lowerLayer = new PaintableComponent(tile2);
        
        add(foreground);
        add(lowerLayer);
    }
    
    
    /**
     * <p>Sets the topmost background paintable object
     * of this layered component.</p>
     * 
     * <p>The topmost object is an object which should either be
     * <code>null</code> or be an object that is convertible to a
     * <code>Paintable</code> using
     * <code>ComponentFactory.makePaintable</code>.</p>
     * 
     * @param topmost the topmost background paintable object
     */
    public void setTopmostPaintable(Object topmost) {
        topmostPaintable = ComponentFactory.makePaintable(topmost);
        
        if (topmostPaintable != null)
            corner = topmostPaintable.getCorner();
        else
            corner = null;
        
        tile1.setPaintable(topmostPaintable);
        
        setCentered(centered);
    }
    
    
    /**
     * <p>Gets the topmost background paintable object
     * of this layered component.</p>
     */
    public Paintable getTopmostPaintable() {
        return topmostPaintable;
    }
    
    
    /**
     * <p>Sets the tile paintable object for the middle layer
     * of this layered component.</p>
     * 
     * <p>The tile tile object is an object which should either be
     * <code>null</code> or be an object that is convertible to a
     * <code>Paintable</code> using
     * <code>ComponentFactory.makePaintable</code>.</p>
     * 
     * @param tile the tile paintable object
     */
    public void setBackgroundTile(Object tile) {
        tilePaintable = ComponentFactory.makePaintable(tile);
        
        tile2.setBackgroundTile(tilePaintable);
        
        repaint();
    }
    
    
    /**
     * <p>Gets the tile paintable object for the middle layer
     * of this layered component.</p>
     */
    public Paintable getBackgroundTile() {
        return tilePaintable;
    }
    
    
    /**
     * <p>Sets the background paint for the bottom layer
     * of this layered component.</p>
     * 
     * @param paint the background paint for the bottom layer
     */
    public void setBackgroundPaint(Paint paint) {
        tile2.setBackgroundPaint(paint);
        
        repaint();
    }
    
    
    /**
     * <p>Gets the background paint for the bottom layer
     * of this layered component.</p>
     */
    public Paint getBackgroundPaint() {
        return tile2.getBackgroundPaint();
    }
    
    
    /**
     * Sets the opacity of the background layers
     * of this layered component.
     * 
     * @param opacity the opacity of the background layers
     */
    public void setOpacity(float opacity) {
        tile2.setOpacity(opacity);
        
        repaint();
    }
    
    
    /**
     * Gets the opacity of the background layers
     * of this layered component.
     */
    public float getOpacity() {
        return tile2.getOpacity();
    }
    
    
    /**
     * Sets whether or not the topmost background object is centered
     * in this layered component.
     * 
     * @param centered the centered setting for the topmost background object
     */
    public void setCentered(boolean centered) {
        this.centered = centered;
        
        if (topmostPaintable != null)
        {
            if (centered) {
                Dimension size = foreground.getPreferredSize();
                
                int w = size.width;
                int h = size.height;
                
                tile1.moveCenterTo(w/2, h/2);
            }
            else {
                tile1.moveCornerTo(corner);
            }
        }
        
        repaint();
    }
    
    
    /**
     * Gets whether or not the topmost background object is centered
     * in this layered component.
     */
    public boolean isCentered() {
        return centered;
    }
    
    
}

