/*
 * FSPlaceObject2.java
 * Transform
 * 
 * Copyright (c) 2001-2008 Flagstone Software Ltd. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification, 
 * are permitted provided that the following conditions are met:
 *
 *  * Redistributions of source code must retain the above copyright notice, this
 *    list of conditions and the following disclaimer.
 *  * 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.
 *  * Neither the name of Flagstone Software Ltd. 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 com.flagstone.transform;

import java.util.*;

/**
 * FSPlaceObject2 is used to add and manipulate objects (shape, button, etc.) on
 * the Flash Player's display list.
 * 
 * <p>
 * FSPlaceObject2 supersedes the FSPlaceObject class providing more
 * functionality and easier manipulation of objects in the display list through
 * the following operations:
 * </p>
 * 
 * <ul>
 * <li>Place a new shape on the display list.</li>
 * <li>Change an existing shape by moving it to new location or changing its
 * appearance.</li>
 * <li>Replace an existing shape with a another.</li>
 * <li>Define clipping layers to mask objects displayed in front of a shape.</li>
 * <li>Control the morphing process that changes one shape into another.</li>
 * <li>Assign names to objects rather than using their identifiers.</li>
 * <li>Define the sequence of actions that are executed when an event occurs in
 * movie clip.</li>
 * </ul>
 * 
 * <p>
 * <b>Clipping Depth</b><br/> With the introduction of Flash 3 the display
 * list supported a clipping layer. This allowed the outline of an object to
 * define a clipping path that is used to mask other objects placed in front of
 * it. The clipping depth can be set to mask objects between the layer
 * containing the clipping path and a specified layer.
 * </p>
 * 
 * <p<b>Shape Morphing</b><br/> Shapes that will be morphed are defined using
 * the FSDefineMorphShape class which defines a start and end shape. The Flash
 * Player performs the interpolation that transforms one shape into another. The
 * progress of the morphing process is controlled by a ratio which ranges from
 * 0.0 to 1.0, where 0 generates a shape identical to the starting shape in the
 * FSDefineMorphShape object and 1.0 generates the shape at the end of the
 * morphing process.
 * </p>
 * 
 * <p<b>Movie Clip Events</b><br/> With the introduction of Flash 5, movie
 * clips (defined using the FSDefineMovieClip class) could specify sequences of
 * actions that would be performed in response to mouse or keyboard events. The
 * actions are specified using FSClipEvent objects and the FSPlaceObject2 class
 * is used to register the actions in response to a particular event with the
 * Flash player. Multiple events can be handled by defining an FSClipEvent for
 * each type of event. For more information see the FSClipEvent class.
 * </p>
 * 
 * <table class="datasheet">
 * 
 * <tr>
 * <th align="left" colspan="2">Attributes</th>
 * </tr>
 * 
 * <tr>
 * <td>type</td>
 * <td>Identifies the data structure when it is encoded. Read-only.</td>
 * </tr>
 * 
 * <tr>
 * <td>place</td>
 * <td>Indicates whether a new object is being placed in the display list or an
 * existing object is being modified or replaced.</td>
 * </tr>
 * 
 * <tr>
 * <td>identifier</td>
 * <td>The identifier of the object to added to the display list.</td>
 * </tr>
 * 
 * <tr>
 * <td>layer</td>
 * <td>Defines the order in which objects are displayed.</td>
 * </tr>
 * 
 * <tr>
 * <td>transform</td>
 * <td>Optional. A coordinate transform defines the position, scale and
 * rotation of the object.</td>
 * </tr>
 * 
 * <tr>
 * <td>colorTransform</td>
 * <td>Optional. A colour transform defines any colour changes such as
 * transparency effects.</td>
 * </tr>
 * 
 * <tr>
 * <td>depth</td>
 * <td>Optional. The layer number up to which objects will be clipped by this
 * object's outline.</td>
 * </tr>
 * 
 * <tr>
 * <td>ratio</td>
 * <td>Optional. The point for the morphing process of a shape.</td>
 * </tr>
 * 
 * <tr>
 * <td>name</td>
 * <td>Optional. A name to assign to the object.</td>
 * </tr>
 * 
 * <tr>
 * <td>events</td>
 * <td>Optional. An array of FSClipEvent objects that define the actions that
 * are performed when a specific movie clip event occurs.</td>
 * </tr>
 * 
 * <tr>
 * <td>encodedEvents</td>
 * <td>An array of bytes containing encoded clip events can also be set. The
 * encoded objects are typically generated by the parser in the Translate
 * framework. The events array and encodedEvents cannot both be valid at the
 * same time. Accessor methods used to set either of the attributes will set the
 * other to null.</td>
 * </tr>
 * 
 * </table>
 * 
 * <p>
 * Since only one object can be placed on a given layer an existing object on
 * the display list can be identified by the layer it is displayed on rather
 * than its identifier. Therefore Layer is the only required attribute. The
 * remaining attributes are optional according to the different operation being
 * performed:
 * </p>
 * 
 * <ul>
 * <li>If an existing object on the display list is being modified then only
 * the layer number is required. Previously in the FSPlaceObject class both the
 * identifier and the layer number were required.</li>
 * <li>If no coordinate transform is applied to the shape (the default is a
 * unity transform that does not change the shape) then it is not encoded.</li>
 * <li>Similarly if no colour transform is applied to the shape (the default is
 * a unity transform that does not change the shape's colour) then it is not
 * encoded.</li>
 * <li>If a shape is not being morphed then the ratio attribute may be left at
 * its default value (-1.0).</li>
 * <li>If a shape is not used to define a clipping area then the depth
 * attribute may be left at its default value (0).</li>
 * <li>If a name is net assigned to an object the name attribute may be left
 * its default value (an empty string).</li>
 * <li>If no events are being defined for a movie clip then the array of
 * FSClipEvent object may be left empty.</li>
 * </ul>
 * 
 * <p>
 * The class provides a range of constructors which define different subsets of
 * the attributes according to the type of operation that will be performed on
 * an object in the Flash Player's display list. If an attribute is not
 * specified in a constructor then it will be assigned a default value and will
 * be omitted when the object is encoded.
 * </p>
 * 
 * <h1 class="datasheet">Examples</h1>
 * 
 * <p>
 * The following code samples illustrate how to use the FSPlaceObject2 class to
 * manipulate objects on the display list. These examples use constructors with
 * the most convenient (simplest) arguments. To define more complex place
 * operations constructors are provided that take larger numbers of arguments
 * and allow more complex transformations to be specified.
 * </p>
 * 
 * <p>
 * 1. Display an object.<br/> To display an object at a given location the
 * placeType, identifier, layer number and x,y coordinates are required.
 * </p>
 * 
 * <pre>
 *  // Add the shape to the display list - on layer 1 at coordinates (400, 400)
 *  // The constructor allows just the x and y coordinate to be defined, the
 *  // FSCoordTransform object is constructed internally.
 * 
 *  FSDefineShape shape = new FSDefineShape(movie.newIdentifier(), ......);
 * 
 *  movie.add(new FSPlaceObject2(shape.getIdentifier(), 1, 400, 400));
 *  movie.add(new FSShowFrame());
 * </pre>
 * 
 * <p>
 * 2. Move an object.<br/> To move an object only the layer number is required.
 * This simplifies the code required to manipulate existing objects when
 * compared to the original FSPlaceObject class.
 * </p>
 * 
 * <pre>
 * // Add the shape to the display list.
 * movie.add(new FSPlaceObject2(shape.getIdentifier(), 1, 400, 400));
 * movie.add(new FSShowFrame());
 * 
 * // Move shape to a new location.
 * movie.add(new FSPlaceObject2(1, 250, 300));
 * movie.add(new FSShowFrame());
 * </pre>
 * 
 * <p>
 * 3. Replace an existing object with another.<br/> To move an object only the
 * layer number is required. This simplifies the code required to manipulate
 * existing objects when compared to the original FSPlaceObject class.
 * </p>
 * 
 * <pre>
 * // Add the shape to the display list.
 * movie.add(new FSPlaceObject2(shape.getIdentifier(), 1, 400, 400));
 * movie.add(new FSShowFrame());
 * 
 * // Replace the shape.
 * movie.add(new FSPlaceObject2(newShape.getIdentifier(), 1));
 * movie.add(new FSShowFrame());
 * </pre>
 * 
 * <p>
 * 4. Defining a clipping layer.<br/> The number of layers to clip using the
 * outline of the shape as a path can be specified when placing the shape on the
 * display list.
 * </p>
 * 
 * <pre>
 * // Add the shape to the display list and clip objects on the next two layers
 * 
 * currentLayer = 1;
 * clipTo = currentLayer + 2;
 * 
 * movie.add(new FSPlaceObject2(shape.getIdentifier(), currentLayer, clipTo, 400,
 * 				400));
 * movie.add(new FSShowFrame());
 * </pre>
 * 
 * <p>
 * 5. Controlling the morphing process.<br/> The ratio attribute controls the
 * progress as an FSDefineMorphShape object changes from one shape into another.
 * </p>
 * 
 * <pre>
 *  FSShape triangle = new FSShape(....);
 *  FSShape hexagon = new FSShape(....);
 * 
 *  FSDefineMorphShape shape = new FSDefineMorphShape(movie.newIdentifier(), ...., triangle, hexagon);
 * 
 *  movie.add(new FSPlaceObject2(shape.getIdentifier(), 1, 0.0, 400, 400));
 * 
 *  // Morph the shapes over 10 frames
 * 
 *  for (float i=0.1; i&lt;1.0; i+= 0.1)
 *  {
 *  movie.add(new FSPlaceObject(1, i));
 *  movie.add(new FSShowFrame());
 *  }
 * </pre>
 * 
 * <p>
 * 6. Defining a movie clip event.<br/> FSClipEvent objects are ONLY used to
 * define the sequence of actions executed when a particular event occurs in a
 * movie clip.
 * </p>
 * 
 * <pre>
 *  // Define an array of clip events that will contain one event that defines the
 *  // behaviour of the movie clip in response to a mouse down event.
 * 
 *  ArrayList events = new ArrayList();
 * 
 *  FSClipEvent clipEvent = new FSClipEvent(FSClipEvent.MouseDown);
 * 
 *  // Add one or more actions for the mouse down event.
 * 
 *  clipEvent.add(...);
 *  ...
 * 
 *  // Add the clip event to the array of events for the movie clip.
 * 
 *  events.add(clipEvent);
 * 
 *  // Create the commands to animate the movie clip.
 * 
 *  ArrayList commands = new ArrayList();
 * 
 *  commands.add(...);
 * 
 *  FSDefineMovieClip movieClip = new FSDefineMovieClip(movie.newIdentifier(), commands);
 * 
 *  // Now place the movie clip on the screen on layer 1 at (400, 400).
 * 
 *  movie.add(new FSPlaceObject2(movieClip.getIdentifier(), 1, events, 400, 400));
 * </pre>
 * 
 * <h1 class="datasheet">History</h1>
 * 
 * <p>
 * The FSPlaceObject2 class represents the PlaceObject2 tag from the Macromedia
 * Flash (SWF) File Format Specification. It was introduced in Flash 3.
 * </p>
 */
public class FSPlaceObject2 extends FSMovieObject
{
	/* Update an existing object on the display list */
	public static final int Modify = 1;

	/* Add a new object to the display list */
	public static final int New = 2;

	/* Replace an existing object on the display list with a new one */
	public static final int Replace = 3;

	private int place = New;
	private int layer = 0;
	private int identifier = 0;
	private FSCoordTransform transform = null;
	private FSColorTransform colorTransform = null;
	private float ratio = -1.0f;
	private int clippingDepth = 0;
	private String name = null;
	private ArrayList clipEvents = null;
	private byte[] encodedEvents = null;

	/**
	 * Construct an FSPlaceObject2 object, initialising it with values decoded
	 * from an encoded object.
	 * 
	 * @param coder
	 *            an FSCoder containing the binary data.
	 */
	public FSPlaceObject2(FSCoder coder)
	{
		super(PlaceObject2);
		decode(coder);
	}

	/**
	 * Constructs an FSPlaceObject2 object to place a new object on the display
	 * list at the coordinates on the screen.
	 * 
	 * @param anIdentifier
	 *            the identifier of a new object to be displayed.
	 * @param aLayer
	 *            the layer number on which an object is being displayed.
	 * @param xLocation
	 *            the x-coordinate where the object will be displayed.
	 * @param yLocation
	 *            the y-coordinate where the object will be displayed.
	 */
	public FSPlaceObject2(int anIdentifier, int aLayer, int xLocation,
							int yLocation)
	{
		super(PlaceObject2);
		setPlaceType(FSPlaceObject2.New);
		setIdentifier(anIdentifier);
		setLayer(aLayer);
		setTransform(new FSCoordTransform(xLocation, yLocation));
	}

	/**
	 * Constructs an FSPlaceObject2 object to place new morphing shape on the
	 * display list at the coordinates on the screen.
	 * 
	 * @param anIdentifier
	 *            the identifier of a new object to be displayed.
	 * @param aLayer
	 *            the layer number on which an object is being displayed.
	 * @param aRatio
	 *            the ratio of the progress in morphing the shape.
	 * @param xLocation
	 *            the x-coordinate where the object will be displayed.
	 * @param yLocation
	 *            the y-coordinate where the object will be displayed.
	 */
	public FSPlaceObject2(int anIdentifier, int aLayer, float aRatio,
							int xLocation, int yLocation)
	{
		super(PlaceObject2);
		setPlaceType(FSPlaceObject2.New);
		setIdentifier(anIdentifier);
		setLayer(aLayer);
		setRatio(aRatio);
		setTransform(new FSCoordTransform(xLocation, yLocation));
	}

	/**
	 * Constructs an FSPlaceObject2 object to place a new shape on the display
	 * list with the specified coordinate and optional colour transform.
	 * 
	 * @param anIdentifier
	 *            the identifier of a new object to be displayed.
	 * @param aLayer
	 *            the layer number on which an object is being displayed.
	 * @param aTransform
	 *            an FSCoordTransform object that will be applied to the object
	 *            displayed in the display list at layer, aLayer.
	 */
	public FSPlaceObject2(int anIdentifier, int aLayer,
							FSCoordTransform aTransform)
	{
		super(PlaceObject2);
		setPlaceType(FSPlaceObject2.New);
		setIdentifier(anIdentifier);
		setLayer(aLayer);
		setTransform(aTransform);
	}

	/**
	 * Constructs an FSPlaceObject2 object to place a new shape on the display
	 * list with the specified coordinate and optional colour transform.
	 * 
	 * @param anIdentifier
	 *            the identifier of a new object to be displayed.
	 * @param aLayer
	 *            the layer number on which an object is being displayed.
	 * @param aTransform
	 *            an FSCoordTransform object that will be applied to the object
	 *            displayed in the display list at layer, aLayer.
	 * @param aColorTransform
	 *            a FSColorTransform object that will be applied to the object
	 *            displayed in the display list at layer, aLayer.
	 */
	public FSPlaceObject2(int anIdentifier, int aLayer,
							FSCoordTransform aTransform,
							FSColorTransform aColorTransform)
	{
		super(PlaceObject2);
		setPlaceType(FSPlaceObject2.New);
		setIdentifier(anIdentifier);
		setLayer(aLayer);
		setTransform(aTransform);
		setColorTransform(aColorTransform);
	}

	/**
	 * Constructs an FSPlaceObject2 object to place a new shape on the display
	 * list at the coordinates on the screen that defines a clipping path that
	 * will clip objects for the specified number of layers.
	 * 
	 * @param anIdentifier
	 *            the identifier of a new object to be displayed.
	 * @param aLayer
	 *            the layer number on which an object is being displayed.
	 * @param aDepth
	 *            the layer number up to which objects will be clipped by this
	 *            object's outline.
	 * @param xLocation
	 *            the x-coordinate where the object will be displayed.
	 * @param yLocation
	 *            the y-coordinate where the object will be displayed.
	 */
	public FSPlaceObject2(int anIdentifier, int aLayer, int aDepth,
							int xLocation, int yLocation)
	{
		super(PlaceObject2);
		setPlaceType(FSPlaceObject2.New);
		setIdentifier(anIdentifier);
		setLayer(aLayer);
		setClippingDepth(aDepth);
		setTransform(new FSCoordTransform(xLocation, yLocation));
	}

	/**
	 * Constructs an FSPlaceObject2 object that places a new, named movie clip
	 * on the display list at the coordinates on the screen.
	 * 
	 * @param anIdentifier
	 *            the identifier of a new movie clip to be displayed.
	 * @param aLayer
	 *            the layer number on which the clip is being displayed.
	 * @param anArray
	 *            an array of FSClipEvent objects that define the events
	 *            responded to by the movie clip.
	 * @param aName
	 *            a c-string that define a name for the movie clip.
	 * @param xLocation
	 *            the x-coordinate where the object will be displayed.
	 * @param yLocation
	 *            the y-coordinate where the object will be displayed.
	 */
	public FSPlaceObject2(int anIdentifier, int aLayer, ArrayList anArray,
							String aName, int xLocation, int yLocation)
	{
		super(PlaceObject2);
		setPlaceType(FSPlaceObject2.New);
		setIdentifier(anIdentifier);
		setLayer(aLayer);
		setClipEvents(anArray);
		setName(aName);
		setTransform(new FSCoordTransform(xLocation, yLocation));
	}

	/**
	 * Constructs an FSPlaceObject2 object that places a new, named movie clip
	 * on the display list at the coordinates on the screen. The array of bytes
	 * contains the encoded FSClipEvent objects which specify the events and
	 * actions that the movie clip will respond to. These are typically
	 * generated using the classes in the Translate framework.
	 * 
	 * @param anIdentifier
	 *            the identifier of a new movie clip to be displayed.
	 * @param aLayer
	 *            the layer number on which the clip is being displayed.
	 * @param bytes
	 *            an array of encoded clip event objects.
	 * @param aName
	 *            a c-string that define a name for the movie clip.
	 * @param xLocation
	 *            the x-coordinate where the object will be displayed.
	 * @param yLocation
	 *            the y-coordinate where the object will be displayed.
	 */
	public FSPlaceObject2(int anIdentifier, int aLayer, byte[] bytes,
							String aName, int xLocation, int yLocation)
	{
		super(PlaceObject2);
		setPlaceType(FSPlaceObject2.New);
		setIdentifier(anIdentifier);
		setLayer(aLayer);
		setEncodedEvents(bytes);
		setName(aName);
		setTransform(new FSCoordTransform(xLocation, yLocation));
	}

	/**
	 * Constructs an FSPlaceObject2 object that replaces the object at the
	 * specified layer in the display list, aLayer with the object with the
	 * identifier, anIdentifier. The location and transforms currently applied
	 * to the object being replaced will be preserved.
	 * 
	 * @param anIdentifier
	 *            the identifier of the object that will replaced the one
	 *            current displayed on <i>aLayer</i>.
	 * @param aLayer
	 *            the layer number on which the object being replaced is
	 *            displayed.
	 */
	public FSPlaceObject2(int anIdentifier, int aLayer)
	{
		super(PlaceObject2);
		setPlaceType(FSPlaceObject2.Replace);
		setIdentifier(anIdentifier);
		setLayer(aLayer);
	}

	/**
	 * Constructs an FSPlaceObject2 object that changes the location of the
	 * object in the display list at layer, aLayer to the coordinates (x,y).
	 * 
	 * @param aLayer
	 *            the layer number on which the object is being displayed.
	 * @param xLocation
	 *            the x-coordinate where the object will be displayed.
	 * @param yLocation
	 *            the y-coordinate where the object will be displayed.
	 */
	public FSPlaceObject2(int aLayer, int xLocation, int yLocation)
	{
		super(PlaceObject2);
		setPlaceType(FSPlaceObject2.Modify);
		setLayer(aLayer);
		setTransform(new FSCoordTransform(xLocation, yLocation));
	}

	/**
	 * Constructs an FSPlaceObject2 object that applies the specified coordinate
	 * transform to the object in the display list at layer, aLayer.
	 * 
	 * @param aLayer
	 *            the layer number on which the object is being displayed.
	 * @param transform
	 *            an FSCoordTransform object that will be applied to the object
	 *            displayed in the display list at layer, aLayer.
	 */
	public FSPlaceObject2(int aLayer, FSCoordTransform transform)
	{
		super(PlaceObject2);
		setPlaceType(FSPlaceObject2.Modify);
		setLayer(aLayer);
		setTransform(transform);
	}

	/**
	 * Constructs an FSPlaceObject2 object that applied the specified colour
	 * transform to the object in the display list at layer, aLayer.
	 * 
	 * @param aLayer
	 *            the layer number on which the object is being displayed.
	 * @param colorTransform
	 *            an FSColorTransform object that will be applied to the object
	 *            displayed in the display list at layer, aLayer.
	 */
	public FSPlaceObject2(int aLayer, FSColorTransform colorTransform)
	{
		super(PlaceObject2);
		setPlaceType(FSPlaceObject2.Modify);
		setLayer(aLayer);
		setColorTransform(colorTransform);
	}

	/**
	 * Constructs an FSPlaceObject2 object that applies the specified coordinate
	 * and colour transforms to the object in the display list at layer, aLayer.
	 * 
	 * @param aLayer
	 *            the layer number on which the object is being displayed.
	 * @param transform
	 *            an FSCoordTransform object that will be applied to the object
	 *            displayed in the display list at layer, aLayer.
	 * @param colorTransform
	 *            an FSColorTransform object that will be applied to the object
	 *            displayed in the display list at layer, aLayer.
	 */
	public FSPlaceObject2(int aLayer, FSCoordTransform transform,
							FSColorTransform colorTransform)
	{
		super(PlaceObject2);
		setPlaceType(FSPlaceObject2.Modify);
		setLayer(aLayer);
		setTransform(transform);
		setColorTransform(colorTransform);
	}

	/**
	 * Constructs an FSPlaceObject2 object that changes the location of the
	 * object in the display list at layer, aLayer to the coordinates (x,y).
	 * 
	 * @param aLayer
	 *            the layer number on which the object is being displayed.
	 * @param aRatio
	 *            the ratio of the progress in morphing the shape.
	 * @param xLocation
	 *            the x-coordinate where the object will be displayed.
	 * @param yLocation
	 *            the y-coordinate where the object will be displayed.
	 */
	public FSPlaceObject2(int aLayer, float aRatio, int xLocation, int yLocation)
	{
		super(PlaceObject2);
		setPlaceType(FSPlaceObject2.Modify);
		setLayer(aLayer);
		setRatio(aRatio);
		setTransform(new FSCoordTransform(xLocation, yLocation));
	}

	/**
	 * Constructs an FSPlaceObject2 object that modifies the morphing shape
	 * located on layer, aLayer in the display list. The progress of the
	 * morphing process is controlled by aRatio.
	 * 
	 * @param aLayer
	 *            the layer number on which the object is being displayed.
	 * @param aRatio
	 *            the ratio of the progress in morphing the shape.
	 * @param transform
	 *            an FSCoordTransform object that will be applied to the object
	 *            displayed.
	 */
	public FSPlaceObject2(int aLayer, float aRatio, FSCoordTransform transform)
	{
		super(PlaceObject2);
		setPlaceType(FSPlaceObject2.Modify);
		setLayer(aLayer);
		setRatio(aRatio);
		setTransform(transform);
	}

	/**
	 * Constructs an FSPlaceObject2 object with the specified placement type,
	 * object identifier, layer number, coordinate transform and colour
	 * transform. The exact operation performed is dependent on the placement
	 * type specified.
	 * 
	 * @param aPlace
	 *            the type of operation to be performed, either New, Modify or
	 *            Replace.
	 * @param anIdentifier
	 *            the identifier of a new object to be displayed.
	 * @param aLayer
	 *            the layer number on which an object is being displayed.
	 * @param transform
	 *            an FSCoordTransform object that will be applied to the object
	 *            displayed in the display list at layer, aLayer.
	 * @param colorTransform
	 *            an FSColorTransform object that will be applied to the object
	 *            displayed in the display list at layer, aLayer.
	 */
	public FSPlaceObject2(int aPlace, int anIdentifier, int aLayer,
							FSCoordTransform transform,
							FSColorTransform colorTransform)
	{
		super(PlaceObject2);
		setPlaceType(aPlace);
		setIdentifier(anIdentifier);
		setLayer(aLayer);
		setTransform(transform);
		setColorTransform(colorTransform);
	}

	/**
	 * Constructs an FSPlaceObject2 object by copying values from an existing
	 * object.
	 * 
	 * @param obj
	 *            an FSPlaceObject2 object.
	 */
	public FSPlaceObject2(FSPlaceObject2 obj)
	{
		super(obj);
		place = obj.place;
		layer = obj.layer;
		identifier = obj.identifier;

		if (obj.transform != null)
			transform = new FSCoordTransform(obj.transform);

		if (obj.colorTransform != null)
			colorTransform = new FSColorTransform(obj.colorTransform);

		ratio = obj.ratio;
		clippingDepth = obj.clippingDepth;

		if (obj.name != null)
			name = new String(obj.name);

		if (obj.clipEvents != null)
		{
			clipEvents = new ArrayList(obj.clipEvents.size());

			for (Iterator i = obj.clipEvents.iterator(); i.hasNext();)
				clipEvents.add(((FSClipEvent) i.next()).clone());
		} else
		{
			encodedEvents = Transform.clone(encodedEvents);
		}
	}

	/**
	 * Adds a clip event to the array of clip events. If the object already
	 * contains a set of encoded clip event objects they will be deleted.
	 * 
	 * @param aClipEvent
	 *            a clip event object.
	 */
	public void add(FSClipEvent aClipEvent)
	{
		if (clipEvents == null)
			clipEvents = new ArrayList();

		clipEvents.add(aClipEvent);
		encodedEvents = null;
	}

	/**
	 * Gets the array of FSClipEvent object that define the actions that will be
	 * executed in response to events that occur in the FSDefineMovieClip being
	 * placed.
	 * 
	 * @return the array of clip events
	 */
	public ArrayList getClipEvents()
	{
		return clipEvents;
	}

	/**
	 * Set the array of Clip events. Clip Events are only valid for movie clips
	 * and the argument should be set to null when placing other types of
	 * object.
	 * 
	 * If the object already contains a set of encoded clip event objects they
	 * will be deleted.
	 * 
	 * @param anArray
	 *            an array of FSClipEvent objects.
	 */
	public void setClipEvents(ArrayList anArray)
	{
		clipEvents = anArray;

		if (encodedEvents != null)
			encodedEvents = null;
	}

	/**
	 * Set the encoded clip event objects generated by the classes in the
	 * Translate framework. If the object already contains an array of clip
	 * events objects then they will be deleted.
	 * 
	 * @param bytes
	 *            the array of encoded clip events.
	 */
	public void setEncodedEvents(byte[] bytes)
	{
		encodedEvents = bytes;
		clipEvents = null;
	}

	/**
	 * Gets the type of place operation being performed.
	 * 
	 * @return the placement type.
	 */
	public int getPlaceType()
	{
		return place;
	}

	/**
	 * Gets the Layer on which the object will be displayed in the display list.
	 * 
	 * @return the layer number being referenced.
	 */
	public int getLayer()
	{
		return layer;
	}

	/**
	 * Gets the identifier of the object to be placed. This is only required
	 * when placing an object for the first time. Subsequent references to the
	 * object on this layer can simply use the layer number.
	 * 
	 * @return the identifier of an object being placed on the display list.
	 */
	public int getIdentifier()
	{
		return identifier;
	}

	/**
	 * Gets the coordinate transform.
	 * 
	 * @return the FSCoordTransform that will be applied to an object in the
	 *         display list.
	 */
	public FSCoordTransform getTransform()
	{
		return transform;
	}

	/**
	 * Gets the colour transform.
	 * 
	 * @return the FSColorTransform that will be applied to an object in the
	 *         display list.
	 */
	public FSColorTransform getColorTransform()
	{
		return colorTransform;
	}

	/**
	 * Gets the morph ratio, in the range 0.0 to 1.0 that defines the progress
	 * in the morphing process performed by the Flash Player from the defined
	 * start and end shapes. A value of 0 indicates the start of the process and
	 * 65535 the end.
	 * 
	 * @return the morph ratio, in the range 0.0..1.0.
	 */
	public float getRatio()
	{
		return ratio;
	}

	/**
	 * Gets the number of layers that will be clipped by the object placed on
	 * the layer specified in this object.
	 * 
	 * @return the number of layers that will be clipped.
	 */
	public int getClippingDepth()
	{
		return clippingDepth;
	}

	/**
	 * Gets the name of the object.
	 * 
	 * @return the name assigned to an object.
	 */
	public String getName()
	{
		return name;
	}

	/**
	 * Sets the type of placement.
	 * 
	 * @param aType
	 *            the type of operation to be performed, either New, Modify or
	 *            Replace.
	 */
	public void setPlaceType(int aType)
	{
		place = aType;
	}

	/**
	 * Sets the layer at which the object will be placed.
	 * 
	 * @param aLayer
	 *            the layer number on which the object is being displayed.
	 */
	public void setLayer(int aLayer)
	{
		layer = aLayer;
	}

	/**
	 * Sets the identifier of the object.
	 * 
	 * @param anIdentifier
	 *            the identifier of a new object to be displayed.
	 */
	public void setIdentifier(int anIdentifier)
	{
		identifier = anIdentifier;
	}

	/**
	 * Sets the coordinate transform that defines the position where the object
	 * will be displayed. The argument may be null if the location of the object
	 * is not being changed.
	 * 
	 * @param aTransform
	 *            an FSCoordTransform object that will be applied to the object
	 *            displayed.
	 */
	public void setTransform(FSCoordTransform aTransform)
	{
		transform = aTransform;
	}

	/**
	 * Sets the colour transform that defines the colour effects applied to the
	 * object. The argument may be null if the color of the object is not being
	 * changed.
	 * 
	 * @param aTransform
	 *            an FSColorTransform object that will be applied to the object
	 *            displayed.
	 */
	public void setColorTransform(FSColorTransform aTransform)
	{
		colorTransform = aTransform;
	}

	/**
	 * Sets point of the morphing process for a morph shape in the range 0..1.
	 * May be set to zero if the shape being placed is not being morphed.
	 * 
	 * @param aNumber
	 *            the progress in the morphing process.
	 */
	public void setRatio(float aNumber)
	{
		ratio = aNumber;
	}

	/**
	 * Sets the number of layers that this object will mask. May be set to zero
	 * if the shape being placed does not define a clipping area.
	 * 
	 * @param aNumber
	 *            the number of layers clipped.
	 */
	public void setClippingDepth(int aNumber)
	{
		clippingDepth = aNumber;
	}

	/**
	 * Set the name of an object to be displayed. If a shape is not being
	 * assigned a name then setting the argument to null will omit the attribute
	 * when the object is encoded.
	 * 
	 * @param aString
	 *            the name assigned to the object.
	 */
	public void setName(String aString)
	{
		name = aString;
	}

	public Object clone()
	{
		FSPlaceObject2 anObject = (FSPlaceObject2) super.clone();

		anObject.transform = (transform != null) ? (FSCoordTransform) transform
						.clone() : null;
		anObject.colorTransform = (colorTransform != null) ? (FSColorTransform) colorTransform
						.clone()
						: null;

		if (clipEvents != null)
		{
			anObject.clipEvents = new ArrayList();

			for (Iterator i = clipEvents.iterator(); i.hasNext();)
				anObject.clipEvents.add(((FSClipEvent) i.next()).clone());
		} else
		{
			anObject.encodedEvents = Transform.clone(encodedEvents);
		}
		return anObject;
	}

	public boolean equals(Object anObject)
	{
		boolean result = false;

		if (super.equals(anObject))
		{
			FSPlaceObject2 typedObject = (FSPlaceObject2) anObject;

			result = place == typedObject.place;
			result = result && layer == typedObject.layer;
			result = result && identifier == typedObject.identifier;

			if (transform == null)
				result = result && (typedObject.transform == null);
			else
				result = result && transform.equals(typedObject.transform);

			if (colorTransform == null)
				result = result && (typedObject.colorTransform == null);
			else
				result = result
								&& colorTransform
												.equals(typedObject.colorTransform);

			result = result && ratio == typedObject.ratio;
			result = result && clippingDepth == typedObject.clippingDepth;

			if (name == null)
				result = result && (typedObject.name == null);
			else
				result = result && name.equals(typedObject.name);

			if (transform != null)
				result = result && transform.equals(typedObject.transform);
			else
				result = result && transform == typedObject.transform;

			if (clipEvents != null)
				result = result && clipEvents.equals(typedObject.clipEvents);
			else
				result = result
								&& Transform.equals(encodedEvents,
												typedObject.encodedEvents);
		}
		return result;
	}

	public void appendDescription(StringBuffer buffer, int depth)
	{
		buffer.append(name());

		if (depth > 0)
		{
			buffer.append(": { ");
			Transform.append(buffer, "place", place);
			Transform.append(buffer, "layer", layer);
			Transform.append(buffer, "identifier", identifier);
			Transform.append(buffer, "transform", transform, depth);
			Transform.append(buffer, "colorTransform", colorTransform, depth);
			Transform.append(buffer, "ratio", ratio);
			Transform.append(buffer, "clippingDepth", clippingDepth);
			Transform.append(buffer, "name", name);

			if (clipEvents != null)
				Transform.append(buffer, "clipEvents", clipEvents, depth);
			else
				buffer.append("clipEvents = <data>; ");

			buffer.append("}");
		}
	}

	public int length(FSCoder coder)
	{
		super.length(coder);

		coder.context[FSCoder.TransparentColors] = 1;

		length += 3;
		length += (place == 2 || place == 3) ? 2 : 0;
		length += (containsTransform()) ? transform.length(coder) : 0;
		length += (containsColorTransform(coder)) ? colorTransform
						.length(coder) : 0;
		length += (containsRatio()) ? 2 : 0;
		length += (containsClippingDepth()) ? 2 : 0;

		length += containsName() ? coder.strlen(name, true) : 0;

		if (containsClipEvents())
		{
			if (clipEvents != null)
			{
				int eventSize = coder.context[FSCoder.Version] > 5 ? 4 : 2;

				length += 2 + eventSize;

				for (Iterator i = clipEvents.iterator(); i.hasNext();)
					length += ((FSClipEvent) i.next()).length(coder);

				length += eventSize;
			} else
			{
				length += encodedEvents.length;
			}
		}
		coder.context[FSCoder.TransparentColors] = 0;

		return length;
	}

	public void encode(FSCoder coder)
	{
		super.encode(coder);

		coder.context[FSCoder.TransparentColors] = 1;

		coder.writeBits(containsClipEvents() ? 1 : 0, 1);
		coder.writeBits(containsClippingDepth() ? 1 : 0, 1);
		coder.writeBits(containsName() ? 1 : 0, 1);
		coder.writeBits(containsRatio() ? 1 : 0, 1);
		coder.writeBits(containsColorTransform(coder) ? 1 : 0, 1);
		coder.writeBits(containsTransform() ? 1 : 0, 1);
		coder.writeBits(place, 2);
		coder.writeWord(layer, 2);

		if (place == 2 || place == 3)
			coder.writeWord(identifier, 2);
		if (containsTransform())
			transform.encode(coder);
		if (containsColorTransform(coder))
			colorTransform.encode(coder);
		if (containsRatio())
			coder.writeWord((int) (ratio * 65535.0f), 2);

		// if (coder.context[FSCoder.Version] > 5)
		// {
		if (containsName())
		{
			coder.writeString(name);
			coder.writeWord(0, 1);
		}

		if (containsClippingDepth())
			coder.writeWord(clippingDepth, 2);
		/*
		 * } else { if (containsClippingDepth())
		 * coder.writeWord(clippingDepth+1, 2);
		 * 
		 * if (containsName()) { coder.writeString(name); coder.writeWord(0, 1); } }
		 */
		if (containsClipEvents())
		{
			if (clipEvents != null)
			{
				int eventSize = coder.context[FSCoder.Version] > 5 ? 4 : 2;
				int eventMask = 0;

				coder.writeWord(0, 2);

				for (Iterator i = clipEvents.iterator(); i.hasNext();)
					eventMask |= ((FSClipEvent) i.next()).getEvent();

				coder.writeWord(eventMask, eventSize);

				for (Iterator i = clipEvents.iterator(); i.hasNext();)
					((FSTransformObject) i.next()).encode(coder);

				coder.writeWord(0, eventSize);
			} else
			{
				coder.writeBytes(encodedEvents);
			}
		}
		coder.context[FSCoder.TransparentColors] = 0;
		coder.endObject(name());
	}

	public void decode(FSCoder coder)
	{
		boolean containsClipEvents = false;
		boolean containsClippingDepth = false;
		boolean containsName = false;
		boolean containsRatio = false;
		boolean containsColorTransform = false;
		boolean containsTransform = false;

		super.decode(coder);

		coder.context[FSCoder.TransparentColors] = 1;

		containsClipEvents = coder.readBits(1, false) != 0 ? true : false;
		containsClippingDepth = coder.readBits(1, false) != 0 ? true : false;
		containsName = coder.readBits(1, false) != 0 ? true : false;
		containsRatio = coder.readBits(1, false) != 0 ? true : false;
		containsColorTransform = coder.readBits(1, false) != 0 ? true : false;
		containsTransform = coder.readBits(1, false) != 0 ? true : false;
		place = coder.readBits(2, false);
		layer = coder.readWord(2, false);

		if (place == 2 || place == 3)
			identifier = coder.readWord(2, false);

		if (containsTransform)
			transform = new FSCoordTransform(coder);

		if (containsColorTransform)
			colorTransform = new FSColorTransform(coder);

		if (containsRatio)
			ratio = coder.readWord(2, false) / 65535.0f;

		if (containsName)
			name = coder.readString();

		if (containsClippingDepth)
			clippingDepth = coder.readWord(2, false);

		if (containsClipEvents)
		{
			int eventSize = coder.context[FSCoder.Version] > 5 ? 4 : 2;
			clipEvents = new ArrayList();

			coder.readWord(2, false);
			coder.readWord(eventSize, false);

			while (coder.scanWord(eventSize, false) != 0)
				clipEvents.add(new FSClipEvent(coder));

			coder.readWord(eventSize, false);
		}
		coder.context[FSCoder.TransparentColors] = 0;
		coder.endObject(name());
	}

	private boolean containsTransform()
	{
		return transform != null;
	}

	private boolean containsColorTransform(FSCoder coder)
	{
		return colorTransform != null;
	}

	private boolean containsClipEvents()
	{
		return (clipEvents != null && clipEvents.size() > 0)
						|| encodedEvents != null;
	}

	private boolean containsClippingDepth()
	{
		return clippingDepth > 0;
	}

	private boolean containsName()
	{
		return name != null && name.length() > 0;
	}

	private boolean containsRatio()
	{
		return ratio >= 0.0;
	}
}
