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

/**
 * FSFontInfo2 describes the mapping of codes for a given character set to the
 * glyphs that are drawn to represent the character.
 * 
 * <p>
 * It extends the functionality provided by FSFontInfo by adding a language
 * attribute which is support to support line-breaking when displaying text in
 * different spoken languages. Support for small fonts was added in Flash 7.
 * </P>
 * 
 * <p>
 * The class allows the font associated with a Flash file to be mapped to a font
 * installed on the device where the Flash Player displaying the file is hosted.
 * The use of a font from a device is not automatic but is determined by the
 * HTML tag option <i>deviceFont</i> which is passed to the Flash Player when
 * it is first started. If a device does not support a given font then the
 * glyphs in the FSDefineFont class are used to render the characters.
 * </p>
 * 
 * <p>
 * An important distinction between the host device to specify the font and
 * using the glyphs in an FSDefineFont object is that the device is not
 * anti-aliased and the rendering is dependent on the host device. The glyphs in
 * an FSDefineFont object are anti-aliased and are guaranteed to look identical
 * on every device the text is displayed.
 * </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>identifier</td>
 * <td>The unique identifier for an FSDefineFont object that contains all the
 * glyphs for the font.</td>
 * </tr>
 * <tr>
 * <td>name</td>
 * <td>The name of the font.</td>
 * </tr>
 * <tr>
 * <td>small</td>
 * <td>Indicates the font is small enough to align to pixel boundaries.</td>
 * </tr>
 * <tr>
 * <td>encoding</td>
 * <td>Specifies whether the character codes areTransform.ANSI,Transform.SJIS,
 * Unicode.</td>
 * </tr>
 * <tr>
 * <td>bold</td>
 * <td>Whether the font is bold.</td>
 * </tr>
 * <tr>
 * <td>italic</td>
 * <td>Whether the font is italicised.</td>
 * </tr>
 * <tr>
 * <td>language</td>
 * <td>A code identifying the spoken language for a font, either Latin,
 * Japanese, Korean, SimplifiedChinese or TraditionalChinese. The language code
 * is used to assist in determining the position of line breaks in text
 * displayed using the font.</td>
 * </tr>
 * <tr>
 * <td>codes</td>
 * <td>An array mapping a glyph index to a particular character code in the
 * font.</td>
 * </tr>
 * </table>
 * 
 * <p>
 * The index of each entry in the codes array matches the index in the
 * corresponding glyph in the shapes array of an FSDefineFont object, allowing a
 * given character code to be mapped to a given glyph.
 * </p>
 * 
 * <h1 class="datasheet">History</h1>
 * 
 * <p>
 * The FSFontInfo2 class supports the DefineFontInfo2 data structure introduced
 * in Flash 6. Support for small fonts was added in Flash 7.
 * </p>
 */
public class FSFontInfo2 extends FSMovieObject
{
	private int identifier = 0;
	private String name = "";
	private boolean small = false;
	private int encoding = FSText.Unicode;
	private boolean italic = false;
	private boolean bold = false;
	private int language = 1; // FSText.Latin
	private ArrayList codes = new ArrayList();

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

	/**
	 * Constructs a basic FSFontInfo2 object specifying only the name of the
	 * font.
	 * 
	 * @param anIdentifier
	 *            the unique identifier of the FSDefineFont that contains the
	 *            glyphs for the font.
	 * @param aName
	 *            the name assigned to the font, identifying the font family.
	 */
	public FSFontInfo2(int anIdentifier, String aName)
	{
		super(FontInfo2);
		setIdentifier(anIdentifier);
		setName(aName);
	}

	/**
	 * Constructs an FSFontInfo2 object by copying values from an existing
	 * object.
	 * 
	 * @param obj
	 *            an FSFontInfo2 object.
	 */
	public FSFontInfo2(FSFontInfo2 obj)
	{
		super(obj);
		identifier = obj.identifier;
		name = new String(obj.name);
		encoding = obj.encoding;
		small = obj.small;
		italic = obj.italic;
		bold = obj.bold;
		language = obj.language;

		codes = new ArrayList(obj.codes.size());

		for (Iterator i = obj.codes.iterator(); i.hasNext();)
			codes.add(new Integer(((Integer) i.next()).intValue()));
	}

	/**
	 * Gets the identifier of the font that this font information is for.
	 * 
	 * @return the identifier of the FSDefineFont object that contains the
	 *         glyphs for the font.
	 */
	public int getIdentifier()
	{
		return identifier;
	}

	/**
	 * Gets the name of the font family.
	 * 
	 * @return the name of the font.
	 */
	public String getName()
	{
		return name;
	}

	/**
	 * Gets the encoding scheme used for characters rendered in the font, either
	 * FSText.ASCII, FSText.SJIS or FSText.Unicode.
	 * 
	 * @return the font character encoding.
	 */
	public int getEncoding()
	{
		return encoding;
	}

	// Flash 7
	/**
	 * Does the font have a small point size. This is used only with a Unicode
	 * font encoding.
	 * 
	 * @return a boolean indicating whether the font will be aligned on pixel
	 *         boundaries.
	 */
	public boolean isSmall()
	{
		return small;
	}

	/**
	 * Sets the font is small. Used only with Unicode fonts.
	 * 
	 * @param aBool
	 *            a boolean flag indicating the font will be aligned on pixel
	 *            boundaries.
	 */
	public void setSmall(boolean aBool)
	{
		small = aBool;
	}

	// Flash 7

	/**
	 * Is the font italics.
	 * 
	 * @return a boolean indicating whether the font is rendered in italics.
	 */
	public boolean isItalic()
	{
		return italic;
	}

	/**
	 * Is the font bold.
	 * 
	 * @return a boolean indicating whether the font is rendered in a bold face.
	 */
	public boolean isBold()
	{
		return bold;
	}

	/**
	 * Gets the language code identifying the type of spoken language for the
	 * font, either FSText.Japanese, FSText.Korean, FSText.Latin,
	 * FSText.SimplifiedChinese or FSText.TraditionalChinese.
	 * 
	 * @return the language code used to determine how line-breaks are inserted
	 *         into text rendered using the font.
	 */
	public int getLanguage()
	{
		return language;
	}

	/**
	 * Gets the array of character codes.
	 * 
	 * @return the array of character codes.
	 */
	public ArrayList getCodes()
	{
		return codes;
	}

	/**
	 * Sets the identifier of the font that this font information is for.
	 * 
	 * @param anIdentifier
	 *            the unique identifier of the FSDefineFont that contains the
	 *            glyphs for the font.
	 */
	public void setIdentifier(int anIdentifier)
	{
		identifier = anIdentifier;
	}

	/**
	 * Sets the name of the font.
	 * 
	 * @param aString
	 *            the name assigned to the font, identifying the font family.
	 */
	public void setName(String aString)
	{
		name = aString;
	}

	/**
	 * Sets the font character encoding.
	 * 
	 * @param anEncoding
	 *            the encoding used to identify characters, either FSText.ASCII,
	 *            FSText.SJIS or FSText.Unicode.
	 */
	public void setEncoding(int anEncoding)
	{
		encoding = anEncoding;
	}

	/**
	 * Sets the font is italics.
	 * 
	 * @param aBool
	 *            a boolean flag indicating whether the font will be rendered in
	 *            italics.
	 */
	public void setItalic(boolean aBool)
	{
		italic = aBool;
	}

	/**
	 * Sets the font is bold.
	 * 
	 * @param aBool
	 *            a boolean flag indicating whether the font will be rendered in
	 *            bold face.
	 */
	public void setBold(boolean aBool)
	{
		bold = aBool;
	}

	/**
	 * Sets the language code used to determine the position of line-breaks in
	 * text rendered using the font.
	 * 
	 * The language attribute is ignored if the object is encoded in a Flash 5
	 * movie.
	 * 
	 * @param code
	 *            the code identifying the spoken language either Latin,
	 *            Japanese, Korean, SimplifiedChinese or TraditionalChinese.
	 */
	public void setLanguage(int code)
	{
		language = code;
	}

	/**
	 * Sets the array of character codes.
	 * 
	 * @param anArray
	 *            the array mapping glyphs to particular character codes. The
	 *            ordinal position of a character code in the array identifies
	 *            the index of the corresponding glyph in the FSDefineFont
	 *            object.
	 */
	public void setCodes(ArrayList anArray)
	{
		codes = anArray;
	}

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

		anObject.codes = new ArrayList();

		for (Iterator i = codes.iterator(); i.hasNext();)
			anObject.codes.add(new Integer(((Integer) i.next()).intValue()));

		return anObject;
	}

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

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

			result = identifier == typedObject.identifier;
			result = result && encoding == typedObject.encoding;
			result = result && small == typedObject.small;
			result = result && italic == typedObject.italic;
			result = result && bold == typedObject.bold;
			result = result && language == typedObject.language;

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

			if (codes != null)
				result = result && codes.equals(typedObject.codes);
			else
				result = result && codes == typedObject.codes;
		}
		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, "encoding", encoding);
			// Flash 7
			Transform.append(buffer, "small", small);
			// End Flash 7
			Transform.append(buffer, "italic", italic);
			Transform.append(buffer, "bold", bold);
			Transform.append(buffer, "language", language);
			Transform.append(buffer, "name", name);
			Transform.append(buffer, "codes", codes, depth);
			buffer.append("}");
		}
	}

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

		length += 4;
		length += coder.strlen(name, false);
		length += 1;
		length += codes.size() * 2;

		return length;
	}

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

		coder.writeWord(identifier, 2);
		coder.writeWord(coder.strlen(name, false), 1);
		coder.writeString(name);
		coder.writeBits(0, 2);
		coder.writeBits(small ? 1 : 0, 1);
		coder.writeBits(encoding, 2);
		coder.writeBits(italic ? 1 : 0, 1);
		coder.writeBits(bold ? 1 : 0, 1);
		coder.writeBits(1, 1);
		coder.writeWord(language, 1);

		for (Iterator codesIterator = codes.iterator(); codesIterator.hasNext();)
			coder.writeWord(((Integer) codesIterator.next()).intValue(), 2);

		coder.endObject(name());
	}

	public void decode(FSCoder coder)
	{
		int nameLength = 0;

		codes = new ArrayList();

		super.decode(coder);

		identifier = coder.readWord(2, false);
		nameLength = coder.readWord(1, false);
		name = coder.readString(nameLength);

		if (name.length() > 0)
		{
			while (name.charAt(name.length() - 1) == 0)
			{
				name = name.substring(0, name.length() - 1);
			}
		}

		/* reserved */coder.readBits(2, false);
		small = coder.readBits(1, false) != 0 ? true : false;
		encoding = coder.readBits(2, false);
		italic = coder.readBits(1, false) != 0 ? true : false;
		bold = coder.readBits(1, false) != 0 ? true : false;
		/* containsWideCodes */coder.readBits(1, false);

		int bytesRead = 4 + nameLength + 1;

		language = coder.readWord(1, false);

		while (bytesRead < length)
		{
			codes.add(new Integer(coder.readWord(2, false)));
			bytesRead += 2;
		}

		coder.endObject(name());
	}
}
