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

/**
 * FSMorphBitmapFill uses a bitmap image to fill an area of a morphing shape.
 * 
 * <p>
 * Two types of bitmap fill are supported:
 * </p>
 * 
 * <ul>
 * <li>Clipped - the image is placed in the middle of the area to be filled. If
 * the area to be filled is larger than the image then the colour at the edge of
 * the image is used to fill the remaining area.</li>
 * <li>Tiled - the image is tiled across the area to be filled.</li>
 * </ul>
 * 
 * <table class="datasheet">
 * 
 * <tr>
 * <th align="left" colspan="2">Attributes</th>
 * </tr>
 * 
 * <tr>
 * <td>type</td>
 * <td>The type of bitmap fill, either FSFillStyle.Tiled or
 * FSFillStyle.Clipped.</td>
 * </tr>
 * 
 * <tr>
 * <td>identifier</td>
 * <td>The identifier of the image that will be displayed.</td>
 * </tr>
 * 
 * <tr>
 * <td>start</td>
 * <td>An optional FSCoordTransform object that will be applied to the image,
 * to change its origin, orientation, etc., at the start of the morphing
 * process.</td>
 * </tr>
 * 
 * <tr>
 * <td>end</td>
 * <td>An optional FSCoordTransform object that will be applied to the image,
 * to change its origin, orientation, etc., at the end of the morphing process.</td>
 * </tr>
 * 
 * </table>
 * 
 * <p>
 * The bitmap is referenced using the identifier obtains from an instance of an
 * FSDefineJPEGImage, FSDefineJPEGImage2, FSDefineJPEGImage3, FSDefineImage or
 * FSDefineImage2 object. Only one image can be displayed throughout the
 * morphing process.
 * </p>
 * 
 * <p>
 * When an image is loaded its width and height default to twips rather than
 * pixels. An image 300 x 200 pixels will be displayed as 300 x 200 twips (15 x
 * 10 pixels). Scaling the image by 20 (20 twips = 1 pixel) would restore it to
 * its original size. The FSCoordTransform attribute in the FSBitmapFill object
 * can be used to scale the image to its original size.
 * </p>
 * 
 * <p>
 * The image is drawn with the top left corner at the origin of the shape being
 * filled so the scaling transform must be composited with the translation
 * transform that places the image at the desired location within the shape.
 * </p>
 * 
 * <h1 class="datasheet">History</h1>
 * 
 * <p>
 * The FSMorphBitmapFill class represents the MorphFillStyle structure from the
 * Macromedia Flash (SWF) File Format Specification. Shape morphing was
 * introduced in Flash 3.
 * </P>
 */
public class FSMorphBitmapFill extends FSFillStyle
{
	private int identifier = 0;
	private FSCoordTransform startTransform = new FSCoordTransform(0, 0);
	private FSCoordTransform endTransform = new FSCoordTransform(0, 0);

	/**
	 * Construct an FSMorphBitmapFill object an initialise it with values
	 * decoded from an encoded FSMorphBitmapFill object.
	 * 
	 * @param coder
	 *            an FSCoder object containing an FSMorphBitmapFill encoded as
	 *            binary data.
	 */
	public FSMorphBitmapFill(FSCoder coder)
	{
		decode(coder);
	}

	/**
	 * Constructs an FSMorphBitmapFill specifying the type, bitmap image and
	 * coordinated transforms for the image at the start and end of the morphing
	 * process.
	 * 
	 * @param aType
	 *            the type of fill either FSFillStyle.Tiled or
	 *            FSFillStyle.Clipped.
	 * @param anIdentifier
	 *            the identifier for the bitmap image.
	 * @param aStartTransform
	 *            the transform applied to the image at the start of the
	 *            morphing process.
	 * @param anEndTransform
	 *            the transform applied to the image at the end of the morphing
	 *            process.
	 */
	public FSMorphBitmapFill(int aType, int anIdentifier,
								FSCoordTransform aStartTransform,
								FSCoordTransform anEndTransform)
	{
		super(aType);
		setIdentifier(anIdentifier);
		setStartTransform(aStartTransform);
		setEndTransform(anEndTransform);
	}

	/**
	 * Construct an FSMorphBitmapFill object by copying an existing object.
	 * 
	 * @param obj
	 *            an FSMorphBitmapFill object.
	 */
	public FSMorphBitmapFill(FSMorphBitmapFill obj)
	{
		identifier = obj.identifier;
		startTransform = new FSCoordTransform(obj.startTransform);
		endTransform = new FSCoordTransform(obj.endTransform);
	}

	/**
	 * Gets the identifier of the bitmap image.
	 * 
	 * @return the image's identifier.
	 */
	public int getIdentifier()
	{
		return identifier;
	}

	/**
	 * Gets the starting coordinate transform for the image.
	 * 
	 * @return the starting coordinate transform.
	 */
	public FSCoordTransform getStartTransform()
	{
		return startTransform;
	}

	/**
	 * Gets the ending coordinate transform for the image.
	 * 
	 * @return the ending coordinate transform.
	 */
	public FSCoordTransform getEndTransform()
	{
		return endTransform;
	}

	/**
	 * Sets the identifier of the bitmap image to be used in the morphing
	 * process.
	 * 
	 * @param anIdentifier
	 *            the identifier of the bitmap image.
	 */
	public void setIdentifier(int anIdentifier)
	{
		identifier = anIdentifier;
	}

	/**
	 * Sets the coordinate transform for the image at the start of the morphing
	 * process.
	 * 
	 * @param aTransform
	 *            the starting coordinate transform.
	 */
	public void setStartTransform(FSCoordTransform aTransform)
	{
		startTransform = aTransform;
	}

	/**
	 * Sets the coordinate transform for the image at the end of the morphing
	 * process.
	 * 
	 * @param aTransform
	 *            the ending coordinate transform.
	 */
	public void setEndTransform(FSCoordTransform aTransform)
	{
		endTransform = aTransform;
	}

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

		anObject.startTransform = (startTransform != null) ? (FSCoordTransform) startTransform
						.clone()
						: null;
		anObject.endTransform = (endTransform != null) ? (FSCoordTransform) endTransform
						.clone()
						: null;

		return anObject;
	}

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

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

			result = identifier == typedObject.identifier;

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

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

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

		if (depth > 0)
		{
			buffer.append(": { ");
			Transform.append(buffer, "identifier", identifier);
			Transform.append(buffer, "startTransform", startTransform, depth);
			Transform.append(buffer, "endTransform", endTransform, depth);
			buffer.append("}");
		}
	}

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

		length += 2;
		length += startTransform.length(coder);
		length += endTransform.length(coder);

		return length;
	}

	public void encode(FSCoder coder)
	{
		super.encode(coder);
		coder.writeWord(identifier, 2);
		startTransform.encode(coder);
		endTransform.encode(coder);
	}

	public void decode(FSCoder coder)
	{
		super.decode(coder);
		identifier = coder.readWord(2, false);
		startTransform.decode(coder);
		endTransform.decode(coder);
	}
}
