/* $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:`WorldObject.java'
//
//
// DESCRIPTION
//	World Object
// 
// Author
//
//	Peter Pilgrim
//	Fri Jun 19 00:09:21 BST 1998
// 
// RCS HEADER ``WorldObject.java''
// 
//	$Author$
//	$Date$
//	$Source$
//	$Revision$    $State$    $Locker$
//

package xenon.gamekit;

import java.beans.*;				// for PropertyChangeSupport
import java.util.Vector;
import java.awt.*;
import java.awt.image.*;

// import xenon.soundkit.*;

public abstract class WorldObject 
{
    public final static String DESTROYED_PROPERTY="DESTROYED";
    public final static String ACTIVE_CHANGE_PROPERTY="ACTIVE_CHANGE";
    public final static String VISIBLE_CHANGE_PROPERTY="VISIBLE_CHANGE";

    public boolean	visible;		// True, if object is visible.
    public boolean	active;			// True, if object is active.
    public int		wx,wy;			// World object coordinates.
    public int		scrx,scry;		// Screen coordinates.
    public int		last_wx, last_wy;	// Last world object coordinates.
    public int		last_scrx, last_scry;	// Last screen coordinates.
    public int		width=-1, height=-1;	// Dimension of the object.
    
    public int  	dx_vel, dy_vel;		// velocity vector
    public int  	dx_acc, dy_acc;		// acceleration vector

    public int		image_index=-1;		// image index
    public GameEngine	gameEngine;		// Game Engine reference.
    public Component	component;		// double buffered component
						// for rendering
    
    public Rectangle	bounds = new Rectangle(); // use to calculcate
						// current bounds of the object.
    
    private boolean	destroyed;

    public PropertyChangeSupport  listeners=null; // Property Change Listener;
    
    /** Constructor take a single game object */
    public WorldObject( GameEngine gameEngine ) 
    {
	this.gameEngine = gameEngine;
	active = true;
	visible = true;
	destroyed = false;
	component = gameEngine.getDoubleBufferedContainer();

	loadImages();
	loadAudioClips();
    }

    /** Gets type description type */
    public String getDescriptionType()
    {
	return ("World Object");
    }

    /** retrieves a default image used for placating an image of
     * the world object on the screen. For example displaying image
     * on the left of the display and the number of points scored
     * destroying this world object on the right.
     * <b>Subclassers</b> should override this methods.
     */
    public Image getDefaultImage() 
    {
	return (null);
    }
    
    /** method used to load images for this world object 
     * Load images beforehand, method does nothing all.
     */
    public boolean	loadImages()	
    {
	// Load images beforehand, method does nothing all.
	return (true);
    }

    /** method used to load audio clips for this world object 
     * <b>Subclasser</b> should override this method
     */
    public boolean	loadAudioClips()	
    {
	// Load images beforehand, method does nothing all.
	return (true);
    }

    // ======================================================================
    // Accessor Modifier functions
    // ======================================================================

    /** gets the game engine associated with this object */
    public GameEngine getGameEngine() 
    {
	return (gameEngine);
    }

    /** a convenience method to get the game engine's
     * double buffered container 
     * 
     * @see  gameEngine#getDoubleBufferedContainer
     */
    public Component getDoubleBufferedContainer() 
    {
	return gameEngine.getDoubleBufferedContainer();
    }


    /** sets the width */
    public void setWidth( int width )
    {
	this.width = width;
    }
    
    /** gets the width*/
    public int getWidth()
    {
	return (width);
    }
    
    /** sets the height */
    public void setHeight( int height )
    {
	this.height = height;
    }
    
    /** gets the height*/
    public int getHeight()
    {
	return (height);
    }
    
//

    /** sets the x axis velocity */
    public void setXVelocity( int velocity )
    {
	this.dx_vel = velocity;
    }
    
    /** gets the x axis velocity*/
    public int getXVelocity()
    {
	return (dx_vel);
    }
    
    /** sets the y axis velocity */
    public void setYVelocity( int velocity )
    {
	this.dy_vel = velocity;
    }
    
    /** gets the y axis velocity*/
    public int getYVelocity()
    {
	return (dy_vel);
    }
    
//

    /** sets the x axis acceleration */
    public void setXAcceleration( int acceleration )
    {
	this.dx_acc = acceleration;
    }
    
    /** gets the x axis acceleration*/
    public int getXAcceleration()
    {
	return (dx_acc);
    }
    
    /** sets the y axis acceleration */
    public void setYAcceleration( int acceleration )
    {
	this.dy_acc = acceleration;
    }
    
    /** gets the y axis acceleration*/
    public int getYAcceleration()
    {
	return (dy_acc);
    }
    
//

    /** sets the world x coordinate */
    public void	setWorldXpos( int wx )
    {
	this.wx = wx;
    }
    
    /** gets the world x coordinate */
    public int getWorldXpos()
    {
	return (wx);
    }
    
    /** gets the world x coordinate */
    public void	setWorldYpos( int wy )
    {
	this.wy = wy;
    }
    
    /** gets the world y coordinate */
    public int getWorldYpos()
    {
	return (wy);
    }
    
//

    /** sets the last world x coordinate */
    public void	setLastWorldXpos( int last_wx )
    {
	this.last_wx = last_wx;
    }
    
    /** gets the last world x coordinate */
    public int getLastWorldXpos()
    {
	return (last_wx);
    }
    
    /** gets the last world x coordinate */
    public void	setLastWorldYpos( int last_wy )
    {
	this.last_wy = last_wy;
    }
    
    /** gets the last world y coordinate */
    public int getLastWorldYpos()
    {
	return (last_wy);
    }
    
//

    /** sets the last screen x coordinate */
    public void	setScreenXpos( int wx )
    {
	this.wx = wx;
    }
    
    /** gets the screen x coordinate */
    public int getScreenXpos()
    {
	return (wx);
    }
    
    /** gets the screen x coordinate */
    public void	setScreenYpos( int wy )
    {
	this.wy = wy;
    }
    
    /** gets the screen y coordinate */
    public int getScreenYpos()
    {
	return (wy);
    }

//

    /** sets the last screen x coordinate */
    public void	setLastScreenXpos( int last_scrx )
    {
	this.last_scrx = last_scrx;
    }
    
    /** gets the last screen x coordinate */
    public int getLastScreenXpos()
    {
	return (last_scrx);
    }
    
    /** gets the last screen x coordinate */
    public void	setLastScreenYpos( int last_scry )
    {
	this.last_scry = last_scry;
    }
    
    /** gets the last screen y coordinate */
    public int getLastScreenYpos()
    {
	return (last_scry);
    }
    
//    
    
    /** Set the display mode of the world object. If true then
     * object is visible.
     */
    public void		setVisible( boolean flag )
    {
	boolean oldvisible = visible;
	visible = flag;
	if (listeners != null )
	    firePropertyChange( VISIBLE_CHANGE_PROPERTY, 
				new Boolean(oldvisible), new Boolean(visible) );
    }
    
    /** Set the active mode of the world object. If true then
     * object is active.
     */
    public void		setActive( boolean flag )
    {
	boolean oldactive = active;
	active = flag;
	if (listeners != null )
	    firePropertyChange( ACTIVE_CHANGE_PROPERTY, 
				new Boolean(oldactive), new Boolean(active) );
    }
    
    
    // Accessor functions

    /** Get the active state of this world object */
    public boolean	isVisible()
    {
	return (visible);
    }
    
    /** Get the active state of this world object */
    public boolean	isActive()
    {
	return (active);
    }
    
    /** update world object */
    public WorldObject	update()
    {
	return (this);
    }
    
    /** draw object if displayed */
    public WorldObject	drawObject( Graphics g ) 
    {
	return (this);
    }

    /** undraw object if displayed */
    public WorldObject	undrawObject( Graphics g )
    {
	return (this);
    }

    /** Get the bounds of this object */
    public Rectangle	getBounds()
    {
	return getBounds( new Rectangle() );
    }

    /** Get the bounds of this object 
     * The bounds are returned int term screen coordinate space,
     * because this gets us around the wrap around world problem
     */
    public Rectangle	getBounds( Rectangle rect )
    {
	rect.x = scrx;
	rect.y = scry;
	rect.width = width;
	rect.height = height;
	return rect;
    }
    
    /** if not being displayed allows return false
	return TRUE if this collides with reference object */
    public boolean	hasCollided( WorldObject refObject )
    {
	// Calculate updated rectangular boundaries
	refObject.getBounds( refObject.bounds );
	getBounds( bounds );
//	  System.out.println( "bounds["+bounds.x+","+bounds.y+","+
//			      bounds.width+","+bounds.height+"]   ["+
//			      refObject.bounds.x+","+refObject.bounds.y+","+
//			      refObject.bounds.width+","+refObject.bounds.height+"]");
	
	// Does this object bounds intersect with the reference object?
	// If true then we have a collision, false otherwise.
	return bounds.intersects( refObject.bounds );
    }

    public void		translateWorldToScreen()
    {
	/** Read the catalogues ... */
	int xpos = wx - gameEngine.world_xpos;
	if ( wx < gameEngine.world_xpos ) 
	    xpos = (wx + 3*gameEngine.world_width - gameEngine.world_xpos ) % 
		gameEngine.world_width;
	
	// if ( (xpos) < 0 || gameEngine.world_xpos > gameEngine.world_width ) {
	//     System.out.print( "ERROR ");
	//     System.out.print( "wx:" + wx + ", wy:" + wy + "  " );
	//     System.out.print( "( world_xpos:" + gameEngine.world_xpos + ", world_ypos:" + 
	// 			 gameEngine.world_ypos + " )  " );
	//     System.out.println( "xpos:" + xpos );
	// }
	

	scrx = xpos;
	scry = gameEngine.screen_height - wy;
    }

    /** Returns true if this world object is viewable on the screen */
    public boolean isViewable()
    {
	return ( scrx > -width && scry > -height &&
		 scrx < gameEngine.screen_width && scry < gameEngine.screen_height );
    }
    
    /** Returns true if this world object is destroyed */
    public boolean isDestroyed()
    {
	return (destroyed);
    }
    
    public void	destroy()
    {
	destroyed = true;
	    
	if (listeners != null )
	    firePropertyChange( DESTROYED_PROPERTY, 
				new Boolean(false), new Boolean(true) );
	setActive(false);
	setVisible(false);
	
	gameEngine.removeWorldObject( this );
    }
    

    /** Gets the points score for this world object */
    public int getPointsScored()
    {
	return (0);
    }
    
    /** Returns a boolean true if this object can be seen byte the radar */
    public boolean isRadarable()
    {
	return (true);
    }
    
    /** Get the radar color for this world object */
    public Color  getRadarColor()
    {
	return Color.white;
    }

    /** 
     * add property change listener to this world object 
     * @param listener the listener to register
     */
    public void addPropertyChangeListener( PropertyChangeListener listener )
    {
	if ( listeners == null )
	    listeners = new PropertyChangeSupport(this);
	listeners.addPropertyChangeListener(listener);
    }

    /** 
     * remove property change listener from this world object 
     * @param listener the listener to unregister
     */
    public void removePropertyChangeListener( PropertyChangeListener listener )
    {
	if ( listeners != null ) 
	    listeners.removePropertyChangeListener(listener);
    }

    /** 
     * fire a property change event for this world object
     * @param name the property name
     * @param oldValue the old value of the property
     * @param newValue the new value of the property
     */
    protected void firePropertyChange( 
	String name, Object oldValue, Object newValue )
    {
	if ( listeners != null ) 
	    listeners.firePropertyChange( name, oldValue, newValue );
    }
    
}

// fini
