/* $Id$
 *
 * Copyright (c) 1999 Xenonsoft Limited. All Rights Reserved.
 *
 * This software is protected by copyright. You are hereby notified from
 * now by reading this message. This software is also the confidential 
 * and proprietary information of Xenonsoft Limited.  ("Confidential
 * Information"). 
 *
 * This software is distributed under the Xenonsoft Public end user 
 * License ("XPeL"), where the machine-readable source code is provided 
 * under the "Open Source" model.
 * For more information, please read the file "LICENSE-XPL.txt" 
 */

// File:`DoubleBufferedContainer.java'

//
// DESCRIPTION
//	Double buffered container
// 
// Author
//
//	Peter Pilgrim
//	Fri Jun 19 00:09:21 BST 1998
// 
// RCS HEADER ``DoubleBufferedContainer.java''
// 
//	$Author$
//	$Date$
//	$Source$
//	$Revision$    $State$    $Locker$
//

package xenon.gamekit;

import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import java.util.*;


/**
 * A <em>lightweight</em> AWT container that implements a
 * double buffer.
 *
 * <b>WARNING:</b> It is still a direct subclass of <b>java.awt.Panel</b>
 * it should be really a direct lightweight <b>java.awt.Container</b>.
 * It should be then possible to mix the gamekit with Swing/JFC for example.
 *
 * @author Peter Pilgrim
 * @version Wed Jun 09 21:44:21 BST 1999 $Id$ 
 */
public class DoubleBufferedContainer extends Panel  /*Container*/
implements ImageObserver 
{
    /** Default window size */
    public final static Dimension DEFAULT_SIZE=new Dimension( 500, 400 );
    
    /** double buffered image for off screen */
    protected Image		offScreenImage;

    /** Reference to offscreen graphics context */
    protected Graphics		offScreenGC;

    /** the game engine */
    protected GameEngine	engine;
    
    /** preferred size of the container */
    protected Dimension		screenSize;
    
    /**
     * constructs a double buffered container.
     * @param engine the object that can render an image.
     */
    public DoubleBufferedContainer( GameEngine engine )
    {
	this( engine, DEFAULT_SIZE );
    }
    
    /**
     * constructs a double buffered container.
     * @param engine the object that can render an image.
     * @param width the screen width
     * @param height the screen height
     */
    public DoubleBufferedContainer( 
	GameEngine engine, int width, int height )
    {
	this( engine, new Dimension( width, height ) );
    }
    
    /**
     * constructs a double buffered container.
     * @param render the object that can render an image.
     * @param size the screen dimensions; the width and height
     */
    public DoubleBufferedContainer( 
	GameEngine engine, Dimension size )
    {
	this.engine = engine;
	
	setPreferredSize( size );
	offScreenGC = null;
	offScreenImage = null;

	// WARNING: I can't do this yet because I haven't time to
	// figure out how to double buffer lightweight AWT Container
	// *PP* June 1999
	//
	// Since this is a (lightweight) AWT Container, 
	// set a default layout manager
	setLayout( new BulletinLayout() );
    }

    /** 
     * sets the preferred size
     * @param newSize the new preferred size
     */
    public void setPreferredSize( Dimension newSize )
    {
	screenSize = newSize;
    }

    /** 
     * gets the preferred size
     * override the <b>Component.getPreferredSize()</b>
     */
    public Dimension getPreferredSize(   )
    {
	return (screenSize);
    }
    
    public void setBounds( int x, int y, int width, int height)
    {
	super.setBounds( x, y, width, height );
	
	// System.out.println( "DBC.setBounds("+x+","+y+","+width+","+height+")" );

	// Intercept resize of the component to handle double buffer size.
	if ( width != getSize().width || height != getSize().height )
	    offScreenImage = null;
    }
    
    /**
     * override update to <b>not</b> erase the background before
     * painting.
     * 
     * @param g the graphics context.
     */
    public void update( Graphics g )
    {
	paint(g);
    }
    
    /**
     * paint into the off screen buffer, then blit the image to
     * the display window all at once.
     * 
     * @param g the graphics context.
     */
    public void paint( Graphics g )
    {
	if (offScreenImage == null) {
	    // // create off screen image & graphics context
	    // // Maintain a lock on the object
	    // System.out.println( "DBC.paint() create double buffer");
	    // System.out.println( "    component size:"+getSize().width+","+getSize().height);
	    // synchronized (LOCK_OffScreen) {
	    offScreenImage = createImage( getSize().width, getSize().height );
	    offScreenGC = offScreenImage.getGraphics();

	    // }

	    // System.out.println( "    off image:"+offScreenImage+"  off GC:"+offScreenGC );
	    // System.out.println( "    off image size:"+offScreenImage.getWidth(this)+","+offScreenImage.getHeight(this) );
	    // System.out.println( "    DBC:"+this.hashCode() );
	    
	}

	// Prefer to use the gameEngine, because of classes perserve
	// polymorphism and inheritance, whereas an interface does not.
	engine.renderImage( offScreenGC );
	// Since this is lightweight AWT Container, make sure the
	// children components are also painted now.
	// super.paint( offScreenGC );

	g.setClip( 0, 0, getSize().width, getSize().height );
	g.drawImage( offScreenImage, 0, 0, this );
	
	// blitImageToScreen( g, null );
    }
    
    /**
     * gets reference to off-screen image buffer
     */
    public Image getOffScreenImage()
    {
	return (offScreenImage);
    }

    /**
     * gets reference to off-screen graphics context
     */
    public Graphics getOffScreenGC()
    {
	return (offScreenGC);
    }

    /**
     * blit the double buffered image to the screen
     * @param g the graphics context
     */
    public void blitImageToScreen( Graphics g )
    {
	blitImageToScreen( g, null );
    }
    
    /**
     * blit the double buffered image to the screen
     * @param g the graphics context
     * @param clip the clipping area
     */
    public void blitImageToScreen( Graphics g, Rectangle clip )
    {
        if (g != null ) {
	    if (clip != null) 
		g.setClip(clip);
	    g.drawImage( offScreenImage, 0, 0, this );
	}
    }
    
    /**
     * blit the double buffered image to another image drawable
     * @param imd target image drawable
     */
    public void blitImageToScreen( Image imd )
    {
	blitImageToScreen( imd, null );
    }
    
    /**
     * blit the double buffered image to another image drawable
     * @param imd target image drawable
     * @param clip the clipping area
     */
    public void blitImageToScreen( Image imd, Rectangle clip )
    {
	Graphics g = imd.getGraphics();
        if (g != null ) {
	    // the graphics context should be always disposed off.
	    try {
		if (clip != null) 
		    g.setClip(clip);
		g.drawImage( offScreenImage, 0, 0, this );
	    }
	    finally {
		g.dispose();
	    }
	}
    }

    public synchronized boolean imageUpdate(Image img, int infoflags, int x,
					    int y, int width, int height) {
	if( (infoflags & (ImageObserver.ALLBITS | ImageObserver.FRAMEBITS)) != 0 )
	    // image is ready
	    repaint();
	return true;
    }
    
    /** Returns true if the window is damaged or only partial
     * visible
     */
    protected synchronized boolean isWindowDamaged( Graphics g )
    {
	Rectangle clip = g.getClipBounds();
	Dimension size = getSize();
	return ((clip.x != 0 || clip.y != 0 ) ||
		(clip.width < size.width || clip.height < size.height ));
    }

    
}

// fini

