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

/**
 * FSColor is used to represent colours.
 * 
 * <p>
 * The FSColor class supports the RGB colour space with an optional alpha
 * channel which is used to specify the transparency of an object. An FSColor
 * object supports 32-bit colours with 8 bits per colour channel.
 * </p>
 * 
 * <table class="datasheet">
 * 
 * <tr>
 * <th align="left" colspan="2">Attributes</th>
 * </tr>
 * 
 * <tr>
 * <td>red</td>
 * <td>The red channel of the colour, with values in the range 0..255.</td>
 * </tr>
 * 
 * <tr>
 * <td>green</td>
 * <td>The green channel of the colour, with values in the range 0..255.</td>
 * </tr>
 * 
 * <tr>
 * <td>blue</td>
 * <td>The blue channel of the colour, with values in the range 0..255.</td>
 * </tr>
 * 
 * <tr>
 * <td>alpha</td>
 * <td>The alpha channel of the colour, with values in the range 0..255.</td>
 * </tr>
 * 
 * </table>
 * 
 * <p>
 * Whether a colour contains transparency information is determined by the
 * context created by the object that contains the colour. For example colours
 * within the FSDefineShape and FSDefineShape2 objects do not contain alpha
 * channel information while those in an FSDefineShape3 object do. To simplify
 * the use of the colours only a single class is provided. The alpha channel can
 * be ignored if not required within the object the colour is defined for. When
 * a FSColor object is encoded or decoded the object notifies the colour objects
 * it contains whether the alpha channel information should also be encoded or
 * decoded. As of Version 5 of the Macromedia Flash (SWF) File Format
 * Specification only the FSPlaceObject2, FSDefineText2 and FSDefineShape3
 * classes use colours containing an alpha channel.
 * </p>
 * 
 * <p>
 * The class provides a range of constructors to instantiate objects using
 * colour information presented in a variety of formats. To create a colour,
 * specifying values for colour channels in the following order: red, green,
 * blue and optionally alpha:
 * </p>
 * 
 * <pre>
 * FSColor red = new FSColor(255, 0, 0);
 * 
 * FSColor green = new FSColor(0, 255, 0);
 * 
 * FSColor blue = new FSColor(0, 0, 255);
 * </pre>
 * 
 * <p>
 * When the alpha channel is not specified it defaults to a value of 255 which
 * defines the colour to be completely opaque. For transparent colours the alpha
 * channel determines how opaque the colour is. 0 represents a fully transparent
 * colour and 255 is fully opaque.
 * </p>
 * 
 * <pre>
 * FSColor transparentRed = new FSColor(255, 0, 0, 128);
 * 
 * FSColor transparentGreen = new FSColor(0, 255, 0, 128);
 * 
 * FSColor transparentBlue = new FSColor(0, 0, 255, 128);
 * </pre>
 * 
 * <p>
 * When the alpha channel is not specified it defaults to a value of 255 which
 * defines the colour to be completely opaque. The classes that contain colour
 * objects control whether the value for the alpha channel is encoded. For
 * example FSDefineShape and FSDefineShape2 support opaque colours while
 * FSDefineShape3 supports transparent colours. If only opaque colours are
 * supported then the alpha channel can be ignored when creating and
 * manipulating FSColor objects.
 * </p>
 * 
 * <h1 class="datasheet">History</h1>
 * 
 * <p>
 * FSColor class represents the colour data types, RGB and RGBA in the
 * Macromedia Flash (SWF) File Format Specification. The colour data structure
 * was available was introduced in Flash 1 and transparent colours were
 * introduced in Flash 3.
 * </p>
 */
public class FSColor extends FSTransformObject
{
	private int red = 0;
	private int green = 0;
	private int blue = 0;
	private int alpha = 255;

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

	/**
	 * Constructs an FSColor object containing red, green and blue channels. The
	 * alpha channel defaults to the value 255 - defining an opaque colour.
	 * 
	 * @param r
	 *            value for the red channel, in the range 0..255.
	 * @param g
	 *            value for the green channel, in the range 0..255.
	 * @param b
	 *            value for the blue channel, in the range 0..255.
	 */
	public FSColor(int r, int g, int b)
	{
		setRed(r);
		setGreen(g);
		setBlue(b);
	}

	/**
	 * Constructs an FSColor object containing red, green, blue and alpha
	 * channels.
	 * 
	 * @param r
	 *            value for the red channel, in the range 0..255.
	 * @param g
	 *            value for the green channel, in the range 0..255.
	 * @param b
	 *            value for the blue channel, in the range 0..255.
	 * @param a
	 *            value for the alpha channel, in the range 0..255.
	 */
	public FSColor(int r, int g, int b, int a)
	{
		setRed(r);
		setGreen(g);
		setBlue(b);
		setAlpha(a);
	}

	/**
	 * Construct an FSColor object and initialise it with value from another
	 * FSColor object.
	 * 
	 * @param obj
	 *            an FSColor object.
	 */
	public FSColor(FSColor obj)
	{
		red = obj.red;
		green = obj.green;
		blue = obj.blue;
		alpha = obj.alpha;
	}

	/**
	 * Gets the value for the red colour channel.
	 * 
	 * @return the value for the red channel in the range 0..255.
	 */
	public int getRed()
	{
		return red;
	}

	/**
	 * Gets the value for the green colour channel.
	 * 
	 * @return the value for the green channel in the range 0..255.
	 */
	public int getGreen()
	{
		return green;
	}

	/**
	 * Gets the value for the blue colour channel.
	 * 
	 * @return the value for the blue channel in the range 0..255.
	 */
	public int getBlue()
	{
		return blue;
	}

	/**
	 * Gets the value for the alpha colour channel.
	 * 
	 * @return the value for the alpha channel in the range 0..255.
	 */
	public int getAlpha()
	{
		return alpha;
	}

	/**
	 * Set the value for the red colour channel.
	 * 
	 * @param r
	 *            value for the red channel.
	 */
	public void setRed(int r)
	{
		red = r;
	}

	/**
	 * Set the value for the green colour channel.
	 * 
	 * @param g
	 *            value for the green channel.
	 */
	public void setGreen(int g)
	{
		green = g;
	}

	/**
	 * Set the value for the blue colour channel.
	 * 
	 * @param b
	 *            value for the blue channel, in the range 0..255.
	 */
	public void setBlue(int b)
	{
		blue = b;
	}

	/**
	 * Set the value for the alpha channel.
	 * 
	 * @param a
	 *            value for the alpha channel, in the range 0..255.
	 */
	public void setAlpha(int a)
	{
		alpha = a;
	}

	/**
	 * Set the values for the red, green and blue colour channels. The alpha
	 * channel is not changed.
	 * 
	 * @param r
	 *            value for the red channel, in the range 0..255.
	 * @param g
	 *            value for the green channel, in the range 0..255.
	 * @param b
	 *            value for the blue channel, in the range 0..255.
	 */
	public void setChannels(int r, int g, int b)
	{
		setRed(r);
		setGreen(g);
		setBlue(b);
	}

	/**
	 * Set the values for each of the four colour channels.
	 * 
	 * @param r
	 *            value for the red channel, in the range 0..255.
	 * @param g
	 *            value for the green channel, in the range 0..255.
	 * @param b
	 *            value for the blue channel, in the range 0..255.
	 * @param a
	 *            value for the alpha channel, in the range 0..255.
	 */
	public void setChannels(int r, int g, int b, int a)
	{
		setRed(r);
		setGreen(g);
		setBlue(b);
		setAlpha(a);
	}

	/**
	 * 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))
		{
			FSColor typedObject = (FSColor) anObject;

			result = red == typedObject.red;
			result = result && green == typedObject.green;
			result = result && blue == typedObject.blue;
			result = result && alpha == typedObject.alpha;
		}
		return result;
	}

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

		if (depth > 0)
		{
			buffer.append(": { ");
			Transform.append(buffer, "red", red);
			Transform.append(buffer, "green", green);
			Transform.append(buffer, "blue", blue);
			Transform.append(buffer, "alpha", alpha);
			buffer.append("}");
		}
	}

	public int length(FSCoder coder)
	{
		return (coder.context[FSCoder.TransparentColors] != 0) ? 4 : 3;
	}

	public void encode(FSCoder coder)
	{
		coder.writeWord(red, 1);
		coder.writeWord(green, 1);
		coder.writeWord(blue, 1);

		if (coder.context[FSCoder.TransparentColors] != 0)
			coder.writeWord(alpha, 1);
	}

	public void decode(FSCoder coder)
	{
		red = coder.readWord(1, false);
		green = coder.readWord(1, false);
		blue = coder.readWord(1, false);

		if (coder.context[FSCoder.TransparentColors] != 0)
			alpha = coder.readWord(1, false);
	}
}