/*
 * FSShapeStyle.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.*;

/**
 * FSShapeStyle is used to change the drawing environment when a shape is drawn.
 * Three operations can be performed:
 * 
 * <ul>
 * <li>Select a line style or fill style.</li>
 * <li>Move the current drawing point.</li>
 * <li>Define a new set of line and fill styles.</li>
 * </ul>
 * 
 * <p>
 * An FSShapeStyle object can specify one or more of the operations rather than
 * specifying them in separate FSShapeStyle objects - compacting the size of the
 * binary data when the object is encoded. Conversely if an operation is not
 * defined then the values may be omitted.
 * </p>
 * 
 * <table class="datasheet">
 * 
 * <tr>
 * <th align="left" colspan="2">Attributes</th>
 * </tr>
 * 
 * <tr>
 * <td>lineStyle</td>
 * <td>The index of the line style in the parent shape's line style array.
 * Optional. May be set to the framework constant Transform.VALUE_NOT_SET if no
 * style is selected.</td>
 * </tr>
 * 
 * <tr>
 * <td>fillStyle</td>
 * <td>The index of the fill style in the parent shape's fill style array.
 * Optional. May be set to the framework constant Transform.VALUE_NOT_SET if no
 * style is selected.</td>
 * </tr>
 * 
 * <tr>
 * <td>altFillStyle</td>
 * <td>The index of the alternate fill style in the parent shape's fill style
 * array. Optional. May be set to the framework constant Transform.VALUE_NOT_SET
 * if no style is selected.</td>
 * </tr>
 * 
 * <tr>
 * <td>moveX</td>
 * <td>The absolute x-coordinate of the new drawing point. Optional. May be set
 * to the framework constant Transform.VALUE_NOT_SET if the drawing point is not
 * being moved.</td>
 * </tr>
 * 
 * <tr>
 * <td>moveY</td>
 * <td>The absolute y-coordinate of the new drawing point. Optional. May be set
 * to the framework constant Transform.VALUE_NOT_SET if the drawing point is not
 * being moved.</td>
 * </tr>
 * 
 * <tr>
 * <td>lineStyles</td>
 * <td>An array of new line styles for the parent shape. Optional. May be set
 * to null or an empty array if no line or fill styles are being added.</td>
 * </tr>
 * 
 * <tr>
 * <td>fillStyles</td>
 * <td>An array of new fill styles for the parent shape. Optional. May be set
 * to null or an empty array if no line or fill styles are being added.</td>
 * </tr>
 * 
 * </table>
 * 
 * <p>
 * Line and Fill styles are selected by the index position, starting at 1, of
 * the style in an array of styles. An index of zero means that no style is
 * selected. Using the constant Transform.VALUE_NOT_SET means that the current
 * style is unchanged. Two types of fill style are supported: fillStyle is used
 * where a shape does not contain overlapping areas and altFillStyle is used
 * where areas overlap. This differs from graphics environments that only
 * support one fill style as the overlapping area would form a hole in the shape
 * and not be filled.
 * </p>
 * 
 * <p>
 * In the following example, when a shape is drawn the fillStyle defines the
 * style to the left of the line drawing the outline while the altFillStyle
 * defines the style applied to the right of the outline.
 * </p>
 * 
 * <img src="doc-files/fillStyles.gif">
 * <p>
 * New fill and line styles can be added to the FSShapeStyle object to change
 * the way shapes are drawn.
 * </p>
 * 
 * <p>
 * A new drawing point is specified using the absolute x and y coordinates. If
 * an FSShapeStyle object is the first in a shape then the current drawing point
 * is the origin of the shape (0,0).
 * </p>
 * 
 * <h1 class="datasheet">Optional Attributes</h1>
 * 
 * <p>
 * Since the FSShapeStyle object can encode three different types of operation,
 * the unused attributes are optional. Setting line or fill styles to the
 * framework constant Transform.VALUE_NOT_SET omits the attribute when the
 * object is encoded - reduced the size.
 * </p>
 * 
 * <p>
 * For the line and fill styles setting the respective attribute to
 * Transform.VALUE_NOT_SET is different from setting to zero. When the attribute
 * is set to Transform.VALUE_NOT_SET attribute is not encoded. When the
 * attribute is set to zero then that instructs the Flash Player that no current
 * line or fill style is selected. The latter is important when filling areas of
 * a shape that overlap - the altFillStyle attribute will be set to the desired
 * style while the lineStyle and fillStyle attributes are set to zero.
 * </p>
 * 
 * <p>
 * Similarly moveX and moveY may be set to Transform.VALUE_NOT_SET to indicate
 * that the current drawing point will not be changed. This can also be achieved
 * by setting moveX and moveY to zero. However using Transform.VALUE_NOT_SET
 * means that the attributes are not encoded reducing the binary representation.
 * For large files the savings can be significant.
 * </p>
 * 
 * <p>
 * The line or fill style arrays May be empty if no new styles are being
 * specified.
 * </p>
 * 
 * <p>
 * Note that the values for the moveX and moveY attributes and the line and fill
 * styles arrays are defined in pairs and are optional only if both are set to
 * zero or NULL respectively.
 * </p>
 * 
 * <h1 class="datasheet">Examples</h1>
 * 
 * <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. If
 * an attribute is not specified in a constructor then it will be assigned the
 * default value of Transform.VALUE_NOT_SET and will be omitted when the object
 * is encoded.
 * </p>
 * 
 * <p>
 * 1. Select line and fill styles.
 * </p>
 * 
 * <pre>
 *  FSDefineShape shape = new FSShape(movie.newIdentifier(), ...);
 * 
 *  // Add fill styles to the shape definition
 *  shape.add(new FSSolidFill(FSColor(0, 0, 255)));
 *  shape.add(new FSBitmapFill(FSFillStyle.Tiled, imageId, transform));
 * 
 *  // Add a line styles to the shape definition
 * 
 *  shape.add(new FSSolidLine(20, FSColor(0, 0, 0)));
 * 
 *  // Select the solid line style and the bitmap fill style. Note that the
 *  // ordinal position of the styles is used. A style index of 0 means that
 *  // style is not set.
 * 
 *  shape.add(new FSShapeStyle(1, 2, 0));
 * </pre>
 * 
 * <p>
 * 2. Move the current drawing point.
 * </p>
 * 
 * <p>
 * The drawing point is changed by specifying the position from the current
 * drawing point:
 * </p>
 * 
 * <pre>
 * FSShapeStyle style = new FSShapeStyle(x, y);
 * </pre>
 * 
 * <p>
 * 3. Specifying New Styles.
 * </p>
 * 
 * <p>
 * Adding a new set of styles to an FSShapeStyle object will replace the
 * existing set of styles defined for the shape.
 * </p>
 * 
 * <pre>
 * FSDefineShape shape = new FSDefineShape(movie.newIdentifier(), bounds,
 * 				fillStyles, lineStyles, shape);
 * 
 * // Replace the existing set of fill and line styles in the shape definition
 * 
 * ArrayList fillStyles = new ArrayList();
 * ArrayList lineStyles = new ArrayList();
 * 
 * fillStyles.add(new FSSolidFill(FSColor(255, 0, 0)));
 * fillStyles.add(new FSSolidFill(FSColor(0, 255, 0)));
 * fillStyles.add(new FSSolidFill(FSColor(0, 0, 255)));
 * 
 * lineStyles.add(new FSSolidLine(1, FSColor(0, 0, 0)));
 * 
 * shape.add(new FSShapeStyle(lineStyles, fillStyles));
 * </pre>
 * 
 * <p>
 * 4. Creating an overlapping shape.
 * </p>
 * 
 * <pre>
 * // Create the bounding rectangle for the shape. Two rectangles 100 x 100 pixels 
 * // are drawn. The area overlap area is 50 x 50 pixels.
 * 
 * FSBounds bounds = new FSBounds(0, -50, 150, 100);
 * 
 * // Define the styles for the shape.
 * 
 * ArrayList fillStyles = new ArrayList();
 * ArrayList lineStyles = new ArrayList();
 * 
 * fillStyles.add(new FSSolidFill(new FSColor(255, 0, 0)));
 * fillStyles.add(new FSSolidFill(new FSColor(255, 255, 0)));
 * fillStyles.add(new FSSolidFill(new FSColor(16, 123, 255)));
 * 
 * lineStyles.add(new FSSolidLine(20, new FSColor(0, 0, 0)));
 * 
 * FSDefineShape shape = new FSDefineShape(movie.newIdentifier, bounds,
 * 				fillStyles, lineStyles, new FSShape());
 * </pre>
 * 
 * <pre>
 * // To create the overlapping shape shown above the following styles 
 * // selections are made while the shape is drawn.
 * 
 * shape.add(new FSShapeStyle(1, 1, 0, 0, 0));
 * shape.add(new FSLine(50, 0));
 * shape.add(new FSShapeStyle(1, 1, 3));
 * shape.add(new FSLine(0, 50));
 * shape.add(new FSLine(50, 0));
 * shape.add(new FSShapeStyle(1, 0, 2));
 * shape.add(new FSLine(50, 0));
 * shape.add(new FSLine(0, -100));
 * shape.add(new FSLine(-100, 0));
 * shape.add(new FSLine(0, 50));
 * shape.add(new FSShapeStyle(1, 3, 2));
 * shape.add(new FSLine(50, 0));
 * shape.add(new FSLine(0, 50));
 * shape.add(new FSShapeStyle(1, 1, 0));
 * shape.add(new FSLine(0, 50));
 * shape.add(new FSLine(-100, 0));
 * shape.add(new FSLine(0, -100));
 * </pre>
 * 
 * <h1 class="datasheet">History</h1>
 * 
 * <p>
 * The FSShapeStyle class represents the StyleChange record from the Macromedia
 * Flash (SWF) File Format Specification. It was introduced in Flash 1.
 * </p>
 */
public class FSShapeStyle extends FSTransformObject
{
	private int moveX = Transform.VALUE_NOT_SET;
	private int moveY = Transform.VALUE_NOT_SET;
	private int fillStyle = Transform.VALUE_NOT_SET;
	private int altFillStyle = Transform.VALUE_NOT_SET;
	private int lineStyle = Transform.VALUE_NOT_SET;
	private ArrayList fillStyles = null;
	private ArrayList lineStyles = null;

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

	/**
	 * Constructs an FSShapeStyle object, selecting the line and fill styles.
	 * 
	 * @param lineStyleIndex
	 *            selects the line style at lineStyleIndex in the line styles
	 *            array of the parent FSShape object.
	 * @param fillStyleIndex
	 *            selects the fill style at fillStyleIndex in the fill styles
	 *            array of the parent FSShape object.
	 * @param altFillStyleIndex
	 *            selects the alternate fill style at altFillStyleIndex in the
	 *            fill styles array of the parent FSShape object.
	 */
	public FSShapeStyle(int lineStyleIndex, int fillStyleIndex,
						int altFillStyleIndex)
	{
		setLineStyle(lineStyleIndex);
		setFillStyle(fillStyleIndex);
		setAltFillStyle(altFillStyleIndex);
	}

	/**
	 * Constructs an FSShapeStyle object, selecting the relative drawing point.
	 * 
	 * @param relativeX
	 *            move the current point by relativeX in the x direction.
	 * @param relativeY
	 *            move the current point by relativeY in the y direction.
	 */
	public FSShapeStyle(int relativeX, int relativeY)
	{
		setMoveX(relativeX);
		setMoveY(relativeY);
	}

	/**
	 * Constructs an FSShapeStyle object, specifying the new set of line and
	 * fill styles for the parent shape.
	 * 
	 * @param lineStylesArray
	 *            an array of FSLineStyle objects.
	 * @param fillStylesArray
	 *            an array of fill style objects.
	 */
	public FSShapeStyle(ArrayList lineStylesArray, ArrayList fillStylesArray)
	{
		setLineStyles(lineStylesArray);
		setFillStyles(fillStylesArray);
	}

	/**
	 * Constructs an FSShapeStyle object, selecting the line and fill styles and
	 * drawing point.
	 * 
	 * @param lineStyleIndex
	 *            selects the line style at lineStyleIndex in the line styles
	 *            array of the parent FSShape object.
	 * @param fillStyleIndex
	 *            selects the fill style at fillStyleIndex in the fill styles
	 *            array of the parent FSShape object.
	 * @param altFillStyleIndex
	 *            selects the alternate fill style at altFillStyleIndex in the
	 *            fill styles array of the parent FSShape object.
	 * @param relativeX
	 *            move the current point by relativeX in the x direction.
	 * @param relativeY
	 *            move the current point by relativeY in the y direction.
	 */
	public FSShapeStyle(int lineStyleIndex, int fillStyleIndex,
						int altFillStyleIndex, int relativeX, int relativeY)
	{
		setLineStyle(lineStyleIndex);
		setFillStyle(fillStyleIndex);
		setAltFillStyle(altFillStyleIndex);
		setMoveX(relativeX);
		setMoveY(relativeY);
	}

	/**
	 * Constructs an FSShapeStyle object, selecting the line and fill styles,
	 * new line and new fill styles.
	 * 
	 * @param lineStyleIndex
	 *            selects the line style at lineStyleIndex in the line styles
	 *            array of the parent FSShape object.
	 * @param fillStyleIndex
	 *            selects the fill style at fillStyleIndex in the fill styles
	 *            array of the parent FSShape object.
	 * @param altFillStyleIndex
	 *            selects the alternate fill style at altFillStyleIndex in the
	 *            fill styles array of the parent FSShape object.
	 * @param lineStylesArray
	 *            an array of FSLineStyle objects.
	 * @param fillStylesArray
	 *            an array of fill style objects.
	 */
	public FSShapeStyle(int lineStyleIndex, int fillStyleIndex,
						int altFillStyleIndex, ArrayList lineStylesArray,
						ArrayList fillStylesArray)
	{
		setLineStyle(lineStyleIndex);
		setFillStyle(fillStyleIndex);
		setAltFillStyle(altFillStyleIndex);
		setLineStyles(lineStylesArray);
		setFillStyles(fillStylesArray);
	}

	/**
	 * Constructs an FSShapeStyle object, selecting the line and fill styles,
	 * drawing point, new line and new fill styles.
	 * 
	 * @param lineStyleIndex
	 *            selects the line style at lineStyleIndex in the line styles
	 *            array of the parent FSShape object.
	 * @param fillStyleIndex
	 *            selects the fill style at fillStyleIndex in the fill styles
	 *            array of the parent FSShape object.
	 * @param altFillStyleIndex
	 *            selects the alternate fill style at altFillStyleIndex in the
	 *            fill styles array of the parent FSShape object.
	 * @param relativeX
	 *            move the current point by relativeX in the x direction.
	 * @param relativeY
	 *            move the current point by relativeY in the y direction.
	 * @param lineStylesArray
	 *            an array of FSLineStyle objects.
	 * @param fillStylesArray
	 *            an array of fill style objects.
	 */
	public FSShapeStyle(int lineStyleIndex, int fillStyleIndex,
						int altFillStyleIndex, int relativeX, int relativeY,
						ArrayList lineStylesArray, ArrayList fillStylesArray)
	{
		setLineStyle(lineStyleIndex);
		setFillStyle(fillStyleIndex);
		setAltFillStyle(altFillStyleIndex);
		setMoveX(relativeX);
		setMoveY(relativeY);
		setLineStyles(lineStylesArray);
		setFillStyles(fillStylesArray);
	}

	/**
	 * Constructs an FSShapeStyle object by copying values from an existing
	 * object.
	 * 
	 * @param obj
	 *            an FSShapeStyle object.
	 */
	public FSShapeStyle(FSShapeStyle obj)
	{
		moveX = obj.moveX;
		moveY = obj.moveY;
		fillStyle = obj.fillStyle;
		altFillStyle = obj.altFillStyle;
		lineStyle = obj.lineStyle;

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

			for (Iterator i = obj.fillStyles.iterator(); i.hasNext();)
				fillStyles.add(((FSFillStyle) i.next()).clone());
		}

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

			for (Iterator i = obj.lineStyles.iterator(); i.hasNext();)
				lineStyles.add(((FSLineStyle) i.next()).clone());
		}
	}

	/**
	 * Add a FSSolidLine object to the array of line styles.
	 * 
	 * @param aLineStyle
	 *            and FSSolidLine object.
	 */
	public void add(FSLineStyle aLineStyle)
	{
		lineStyles.add(aLineStyle);
	}

	/**
	 * Add the fill style object to the array of fill styles.
	 * 
	 * @param aFillStyle
	 *            and FSFillStyle object.
	 */
	public void add(FSFillStyle aFillStyle)
	{
		fillStyles.add(aFillStyle);
	}

	/**
	 * Gets the x-coordinate of any relative move specified.
	 * 
	 * @return the x-coordinate of the relative move.
	 */
	public int getMoveX()
	{
		return moveX;
	}

	/**
	 * Gets the y-coordinate of any relative move specified. If the attribute is
	 * set to Transform.VALUE_NOT_SET then the value is not affected.
	 * 
	 * @return the y-coordinate of the relative move.
	 */
	public int getMoveY()
	{
		return moveY;
	}

	/**
	 * Gets the index of the line style that will be applied to any line drawn.
	 * Returns the value Transform.VALUE_NOT_SET if no line style is defined.
	 * 
	 * @return the index of the selected line style.
	 */
	public int getLineStyle()
	{
		return lineStyle;
	}

	/**
	 * Gets the index of the fill style that will be applied to any area filled.
	 * Returns the value Transform.VALUE_NOT_SET if no fill style is defined.
	 * 
	 * @return the index of the selected fill style.
	 */
	public int getFillStyle()
	{
		return fillStyle;
	}

	/**
	 * Gets the index of the fill style that will be applied to any overlapping
	 * area filled. Returns the value Transform.VALUE_NOT_SET if no alternate
	 * fill style is defined.
	 * 
	 * @return the index of the selected alternate style.
	 */
	public int getAltFillStyle()
	{
		return altFillStyle;
	}

	/**
	 * Gets the array of new line styles.
	 * 
	 * @return the array of FSLineStyle objects.
	 */
	public ArrayList getLineStyles()
	{
		return lineStyles;
	}

	/**
	 * Gets the array of new fill styles.
	 * 
	 * @return the array of fill style objects.
	 */
	public ArrayList getFillStyles()
	{
		return fillStyles;
	}

	/**
	 * Sets the x-coordinate of any relative move. May be set to
	 * Transform.VALUE_NOT_SET if the attribute should not be encoded.
	 * 
	 * @param aNumber
	 *            move the current point by aNumber in the x direction.
	 */
	public void setMoveX(int aNumber)
	{
		moveX = aNumber;
	}

	/**
	 * Sets the y-coordinate of any relative move. May be set to
	 * Transform.VALUE_NOT_SET if the attribute should not be encoded.
	 * 
	 * @param aNumber
	 *            move the current point by aNumber in the y direction.
	 */
	public void setMoveY(int aNumber)
	{
		moveY = aNumber;
	}

	/**
	 * Sets the drawing point.
	 * 
	 * @param x
	 *            the x-coordinate of the drawing point.
	 * @param y
	 *            the y-coordinate of the drawing point.
	 */
	public void setMove(int x, int y)
	{
		moveX = x;
		moveY = y;
	}

	/**
	 * Sets the index of the fill style that will be applied to any area filled.
	 * May be set to zero if no style is selected or Transform.VALUE_NOT_SET if
	 * the attribute should not be encoded.
	 * 
	 * @param anIndex
	 *            selects the fill style at anIndex in the fill styles array of
	 *            the parent FSShape object.
	 */
	public void setFillStyle(int anIndex)
	{
		fillStyle = anIndex;
	}

	/**
	 * Sets the index of the fill style that will be applied to any overlapping
	 * area filled. May be set to zero if no style is selected or
	 * Transform.VALUE_NOT_SET if the attribute should not be encoded.
	 * 
	 * @param anIndex
	 *            selects the alternate fill style at anIndex in the fill styles
	 *            array of the parent FSShape object.
	 */
	public void setAltFillStyle(int anIndex)
	{
		altFillStyle = anIndex;
	}

	/**
	 * Sets the index of the line style that will be applied to any line drawn.
	 * May be set to zero if no style is selected or Transform.VALUE_NOT_SET if
	 * the attribute should not be encoded.
	 * 
	 * @param anIndex
	 *            selects the line style at anIndex in the line styles array of
	 *            the parent FSShape object.
	 */
	public void setLineStyle(int anIndex)
	{
		lineStyle = anIndex;
	}

	/**
	 * Sets the array of new line styles. May be set to null if no styles are
	 * being defined.
	 * 
	 * @param anArray
	 *            an array of FSLineStyle objects.
	 */
	public void setLineStyles(ArrayList anArray)
	{
		lineStyles = anArray;
	}

	/**
	 * Sets the array of new fill styles. May be set to null if no styles are
	 * being defined.
	 * 
	 * @param anArray
	 *            an array of fill style objects.
	 */
	public void setFillStyles(ArrayList anArray)
	{
		fillStyles = anArray;
	}

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

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

			for (Iterator i = fillStyles.iterator(); i.hasNext();)
				anObject.fillStyles.add(((FSFillStyle) i.next()).clone());
		}

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

			for (Iterator i = lineStyles.iterator(); i.hasNext();)
				anObject.lineStyles.add(((FSLineStyle) i.next()).clone());
		}

		return anObject;
	}

	/**
	 * Returns true if anObject is equal to this one. Objects are considered
	 * equal if they would generate identical binary data when they are encoded
	 * to a Flash file.
	 * 
	 * @return true if this object would be identical to anObject when encoded.
	 */
	public boolean equals(Object anObject)
	{
		boolean result = false;

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

			result = moveX == typedObject.moveX;
			result = result && moveY == typedObject.moveY;
			result = result && fillStyle == typedObject.fillStyle;
			result = result && altFillStyle == typedObject.altFillStyle;
			result = result && lineStyle == typedObject.lineStyle;

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

			if (lineStyles != null)
				result = result && lineStyles.equals(typedObject.lineStyles);
			else
				result = result && lineStyles == typedObject.lineStyles;
		}
		return result;
	}

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

		if (depth > 0)
		{
			buffer.append(": { ");
			Transform.append(buffer, "moveX", moveX);
			Transform.append(buffer, "moveY", moveY);
			Transform.append(buffer, "fillStyle", fillStyle);
			Transform.append(buffer, "altFillStyle", altFillStyle);
			Transform.append(buffer, "lineStyle", lineStyle);
			Transform.append(buffer, "fillStyles", fillStyles, depth);
			Transform.append(buffer, "lineStyles", lineStyles, depth);
			buffer.append("}");
		}
	}

	public int length(FSCoder coder)
	{
		boolean _containsStyles = containsStyles();
		boolean _containsLineStyle = containsLineStyle();
		boolean _containsAltFillStyle = containsAltFillStyle();
		boolean _containsFillStyle = containsFillStyle();
		boolean _containsMove = containsMove();

		int numberOfBits = 0;

		numberOfBits += 6;

		if (_containsMove)
		{
			int _moveFieldSize = FSCoder.size(new int[]
			{
				moveX, moveY }, true);

			numberOfBits += 5 + _moveFieldSize * 2;
		}

		numberOfBits += (_containsFillStyle) ? coder.context[FSCoder.NumberOfFillBits]
						: 0;
		numberOfBits += (_containsAltFillStyle) ? coder.context[FSCoder.NumberOfFillBits]
						: 0;
		numberOfBits += (_containsLineStyle) ? coder.context[FSCoder.NumberOfLineBits]
						: 0;

		coder.context[FSCoder.NumberOfShapeBits] += numberOfBits;

		if (_containsStyles)
		{
			int numberOfFillBits = FSCoder.size(fillStyles.size(), false);
			int numberOfLineBits = FSCoder.size(lineStyles.size(), false);

			boolean countExtended = coder.context[FSCoder.ArrayCountExtended] != 0;

			int numberOfStyleBits = 0;
			int flushBits = coder.context[FSCoder.NumberOfShapeBits];

			numberOfStyleBits += (flushBits % 8 > 0) ? 8 - (flushBits % 8) : 0;
			numberOfStyleBits += (countExtended && fillStyles.size() >= 255) ? 24
							: 8;

			for (Iterator styleIterator = fillStyles.iterator(); styleIterator
							.hasNext();)
				numberOfStyleBits += ((FSTransformObject) styleIterator.next())
								.length(coder) * 8;

			numberOfStyleBits += (countExtended && lineStyles.size() >= 255) ? 24
							: 8;

			for (Iterator styleIterator = lineStyles.iterator(); styleIterator
							.hasNext();)
				numberOfStyleBits += ((FSTransformObject) styleIterator.next())
								.length(coder) * 8;

			numberOfStyleBits += 8;

			coder.context[FSCoder.NumberOfFillBits] = numberOfFillBits;
			coder.context[FSCoder.NumberOfLineBits] = numberOfLineBits;
			coder.context[FSCoder.NumberOfShapeBits] += numberOfStyleBits;

			numberOfBits += numberOfStyleBits;
		}
		return numberOfBits;
	}

	public void encode(FSCoder coder)
	{
		boolean _containsStyles = containsStyles();
		boolean _containsLineStyle = containsLineStyle();
		boolean _containsAltFillStyle = containsAltFillStyle();
		boolean _containsFillStyle = containsFillStyle();
		boolean _containsMove = containsMove();

		int numberOfFillBits = coder.context[FSCoder.NumberOfFillBits];
		int numberOfLineBits = coder.context[FSCoder.NumberOfLineBits];

		coder.writeBits(0, 1);
		coder.writeBits(_containsStyles ? 1 : 0, 1);
		coder.writeBits(_containsLineStyle ? 1 : 0, 1);
		coder.writeBits(_containsAltFillStyle ? 1 : 0, 1);
		coder.writeBits(_containsFillStyle ? 1 : 0, 1);
		coder.writeBits(_containsMove ? 1 : 0, 1);

		if (_containsMove)
		{
			int _moveFieldSize = FSCoder.size(new int[]
			{
				moveX, moveY }, true);

			coder.writeBits(_moveFieldSize, 5);
			coder.writeBits(moveX, _moveFieldSize);
			coder.writeBits(moveY, _moveFieldSize);
		}

		if (_containsFillStyle)
			coder.writeBits(fillStyle, numberOfFillBits);

		if (_containsAltFillStyle)
			coder.writeBits(altFillStyle, numberOfFillBits);

		if (_containsLineStyle)
			coder.writeBits(lineStyle, numberOfLineBits);

		if (_containsStyles)
		{
			boolean countExtended = coder.context[FSCoder.ArrayCountExtended] != 0;

			if (lineStyles == null)
			{
				lineStyles = new ArrayList();
			}

			if (fillStyles == null)
			{
				fillStyles = new ArrayList();
			}

			coder.alignToByte();

			if (countExtended && fillStyles.size() >= 255)
			{
				coder.writeWord(0xFF, 1);
				coder.writeWord(fillStyles.size(), 2);
			} else
			{
				coder.writeWord(fillStyles.size(), 1);
			}

			for (Iterator styleIterator = fillStyles.iterator(); styleIterator
							.hasNext();)
				((FSTransformObject) styleIterator.next()).encode(coder);

			if (countExtended && lineStyles.size() >= 255)
			{
				coder.writeWord(0xFF, 1);
				coder.writeWord(lineStyles.size(), 2);
			} else
			{
				coder.writeWord(lineStyles.size(), 1);
			}

			for (Iterator styleIterator = lineStyles.iterator(); styleIterator
							.hasNext();)
				((FSTransformObject) styleIterator.next()).encode(coder);

			numberOfFillBits = FSCoder.size(fillStyles.size(), false);
			numberOfLineBits = FSCoder.size(lineStyles.size(), false);

			coder.writeBits(numberOfFillBits, 4);
			coder.writeBits(numberOfLineBits, 4);

			// Update the stream with the new numbers of line and fill bits
			coder.context[FSCoder.NumberOfFillBits] = numberOfFillBits;
			coder.context[FSCoder.NumberOfLineBits] = numberOfLineBits;
		}
	}

	public void decode(FSCoder coder)
	{
		boolean containsStyles = false;
		boolean containsLineStyle = false;
		boolean containsAltFillStyle = false;
		boolean containsFillStyle = false;
		boolean containsMove = false;

		int numberOfFillBits = coder.context[FSCoder.NumberOfFillBits];
		int numberOfLineBits = coder.context[FSCoder.NumberOfLineBits];

		/* shapeType */coder.readBits(1, false);
		containsStyles = coder.readBits(1, false) != 0 ? true : false;
		containsLineStyle = coder.readBits(1, false) != 0 ? true : false;
		containsAltFillStyle = coder.readBits(1, false) != 0 ? true : false;
		containsFillStyle = coder.readBits(1, false) != 0 ? true : false;
		containsMove = coder.readBits(1, false) != 0 ? true : false;

		if (containsMove)
		{
			int moveFieldSize = coder.readBits(5, false);
			moveX = coder.readBits(moveFieldSize, true);
			moveY = coder.readBits(moveFieldSize, true);
		}

		if (containsFillStyle)
			fillStyle = coder.readBits(numberOfFillBits, false);

		if (containsAltFillStyle)
			altFillStyle = coder.readBits(numberOfFillBits, false);

		if (containsLineStyle)
			lineStyle = coder.readBits(numberOfLineBits, false);

		if (containsStyles)
		{
			coder.alignToByte();

			fillStyles = new ArrayList();
			lineStyles = new ArrayList();

			int fillStyleCount = coder.readWord(1, false);

			if (coder.context[FSCoder.ArrayCountExtended] != 0 && fillStyleCount == 0xFF)
				fillStyleCount = coder.readWord(2, false);

			fillStyles = new ArrayList();

			for (int i = 0; i < fillStyleCount; i++)
			{
				switch (coder.scanWord(1, false))
				{
					case FSFillStyle.Solid:
						fillStyles.add(new FSSolidFill(coder));
						break;
					case FSFillStyle.Linear:
						fillStyles.add(new FSGradientFill(coder));
						break;
					case FSFillStyle.Radial:
						fillStyles.add(new FSGradientFill(coder));
						break;
					case FSFillStyle.Tiled:
						fillStyles.add(new FSBitmapFill(coder));
						break;
					case FSFillStyle.Clipped:
						fillStyles.add(new FSBitmapFill(coder));
						break;
					case FSFillStyle.Unsmoothed_Tiled:
						fillStyles.add(new FSBitmapFill(coder));
						break;
					case FSFillStyle.Unsmoothed_Clipped:
						fillStyles.add(new FSBitmapFill(coder));
						break;
					default:
						break;
				}
			}

			int lineStyleCount = coder.readWord(1, false);

			if (coder.context[FSCoder.ArrayCountExtended] != 0 && lineStyleCount == 0xFF)
				lineStyleCount = coder.readWord(2, false);

			for (int i = 0; i < lineStyleCount; i++)
				lineStyles.add(new FSSolidLine(coder));

			numberOfFillBits = coder.readBits(4, false);
			numberOfLineBits = coder.readBits(4, false);

			coder.context[FSCoder.NumberOfFillBits] = numberOfFillBits;
			coder.context[FSCoder.NumberOfLineBits] = numberOfLineBits;
		}
	}

	private boolean containsStyles()
	{
		return lineStyles != null || fillStyles != null;
	}

	private boolean containsLineStyle()
	{
		return lineStyle != Transform.VALUE_NOT_SET;
	}

	private boolean containsAltFillStyle()
	{
		return altFillStyle != Transform.VALUE_NOT_SET;
	}

	private boolean containsFillStyle()
	{
		return fillStyle != Transform.VALUE_NOT_SET;
	}

	private boolean containsMove()
	{
		return moveX != Transform.VALUE_NOT_SET
						&& moveY != Transform.VALUE_NOT_SET;
	}
}
