/*
 * FSFrame.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.*;
import com.flagstone.transform.*;

/**
 * The Frame class is used to provide a higher level view of a movie. Rather
 * than viewing movies as a sequence of individual objects each representing a
 * given data structure in the encoded Flash file, objects can be grouped
 * together in frames which presents a more logical view of a movie and makes
 * movie manipulation and search for specific objects easier to handle.
 * 
 * Each Frame object has the following attributes:
 * 
 * number - The position in the movie when the frame will be displayed.
 * 
 * label - An optional name assigned to a frame. The FSGotoFrame2 object can be
 * used to move to a named frame when playing a movie or movie clip.
 * 
 * definitions - An array containing objects that define items for display in a
 * movie. Definitions are sub-classes of the FSDefineObject class and define
 * shapes, fonts, images and sounds that are displayed or played by the Flash
 * Player.
 * 
 * commands - An array containing objects that define commands that affect the
 * display list or the Flash Player directly.
 * 
 * actions - An array that define actions that are executed when a frame is
 * displayed.
 * 
 * Frame objects simplify the handling of movies. FSDoAction, FSFrameLabel and
 * FSShowFrame classes can now "hidden" from view. They are generated
 * automatically by the FSFrame object when it is added to an FSMovie object.
 * 
 * The framesFromMovie(FSMovie aMovie) method allows an existing movie to be
 * viewed as an array of FSFrame objects. Objects from the movie are copied into
 * each frame so changes made to the attributes of each object are reflected in
 * the movie. The frame objects are not synchronised with the movie, so any
 * objects added to a frame are not added to the FSMovie. The easiest way to do
 * this is to remove the existing objects from the movie and add all the frames.
 * 
 * ArrayList frames = FSFrame.framesFromMovie(aMovie); ... ...
 * aMovie.getObjects().clear();
 * 
 * for (Iterator i = frames.iterator(); i.hasNext();)
 * ((FSFrame)i.next()).addToMovie(aMovie);
 * 
 * When the contents of an FSFrame object is added to a movie if a label defined
 * then an FSFrameLabel object will be added. Similarly if actions are defined
 * then an FSDoAction object will be added. An FSShowFrame object which
 * instructs the Flash Player to update the display list with all the changes is
 * added.
 * 
 */
public final class FSFrame
{
	/**
	 * Create a frame based view of a movie. Objects from the movie are grouped
	 * into Frame objects. Objects from the movie are added to the frame so any
	 * changes made are reflected in the movie. However objects added or removed
	 * from a frame are not reflected in the movie.
	 * 
	 * @param aMovie
	 *            an FSMovie object.
	 * @return an array of Frame objects.
	 */
	public static ArrayList framesFromMovie(FSMovie aMovie)
	{
		ArrayList frames = new ArrayList();

		FSFrame currentFrame = new FSFrame();

		for (Iterator i = aMovie.getObjects().iterator(); i.hasNext();)
		{
			FSMovieObject currentObject = (FSMovieObject) i.next();

			if (currentObject instanceof FSDoAction)
			{
				currentFrame.actions = ((FSDoAction) currentObject)
								.getActions();
			} else if (currentObject instanceof FSFrameLabel)
			{
				currentFrame.label = ((FSFrameLabel) currentObject).getLabel();
			} else if (currentObject instanceof FSDefineObject)
			{
				currentFrame.addDefinition((FSDefineObject) currentObject);
			} else if (currentObject instanceof FSShowFrame)
			{
				frames.add(currentFrame);
				currentFrame = new FSFrame();
			} else
			{
				currentFrame.addCommand(currentObject);
			}
		}
		return frames;
	}

	private String label = null;

	private ArrayList definitions = null;

	private ArrayList commands = null;

	private ArrayList actions = null;

	/**
	 * Constructs an empty frame with no label defined and the definitions,
	 * commands and actions arrays empty.
	 */
	public FSFrame()
	{
		definitions = new ArrayList();
		commands = new ArrayList();
		actions = new ArrayList();
	}

	/**
	 * Constructs a frame with with the specified label, definitions, commands
	 * and actions. The label is optional and may be set to null if no label is
	 * defined. Similarly if no definitions, commands or actions are defined for
	 * a given frame then the corresponding argument may be set to null.
	 * 
	 * @param label
	 *            a label for the frame. Maybe null if no label is defined.
	 * 
	 * @param definitions
	 *            an array of definition objects. Maybe null if no definitions
	 *            are defined.
	 * 
	 * @param commands
	 *            an array of objects used to manipulate the display list. Maybe
	 *            null if no display list commands are defined.
	 * 
	 * @param actions
	 *            an array of action objects that will be executed when the
	 *            frame is displayed. Maybe null if no actions are defined.
	 */
	public FSFrame(String label, ArrayList definitions, ArrayList commands,
					ArrayList actions)
	{
		setLabel(label);
		setDefinitions(definitions);
		setCommands(commands);
		setActions(actions);
	}

	/**
	 * Adds the action object to the frame.
	 * 
	 * @param anObject
	 *            the action object to be added to the frame.
	 */
	public void addAction(FSActionObject anObject)
	{
		if (actions == null)
			actions = new ArrayList();

		actions.add(anObject);
	}

	/**
	 * Adds an object to the frame that defines an object to be displayed in the
	 * movie.
	 * 
	 * @param anObject
	 *            a sub-class of FSDefineObject.
	 */
	public void addDefinition(FSDefineObject anObject)
	{
		if (definitions == null)
			definitions = new ArrayList();

		definitions.add(anObject);
	}

	/**
	 * Adds the display list command to the frame.
	 * 
	 * @param anObject
	 *            an FSMovieObject the manipulates the display list.
	 */
	public void addCommand(FSMovieObject anObject)
	{
		commands.add(anObject);
	}

	/**
	 * Gets the label assigned to the frame.
	 * 
	 * @return the label.
	 */
	public String getLabel()
	{
		return label;
	}

	/**
	 * Gets the array of definition objects contained in the frame.
	 * 
	 * @return the array of definitions.
	 */
	public ArrayList getDefinitions()
	{
		return definitions;
	}

	/**
	 * Gets the array of commands that update the display list.
	 * 
	 * @return the array of commands objects.
	 */
	public ArrayList getCommands()
	{
		return commands;
	}

	/**
	 * Gets the array of action objects that will be execute when the frame is
	 * displayed.
	 * 
	 * @return the array of actions defined for the frame.
	 */
	public ArrayList getActions()
	{
		return actions;
	}

	/**
	 * Sets the label for the frame.
	 * 
	 * @param aString
	 *            the label.
	 */
	public void setLabel(String aString)
	{
		label = aString;
	}

	/**
	 * Sets the array of action objects for the frame.
	 * 
	 * @param anArray
	 *            the array of actions.
	 */
	public void setDefinitions(ArrayList anArray)
	{
		definitions = anArray;
	}

	/**
	 * Sets the array of commands that updated the display list for the frame.
	 * The changes are visible when the frame is displayed.
	 * 
	 * @param anArray
	 *            the array of command objects.
	 */
	public void setCommands(ArrayList anArray)
	{
		commands = anArray;
	}

	/**
	 * Sets the array of action objects for the frame.
	 * 
	 * @param anArray
	 *            the array of actions.
	 */
	public void setActions(ArrayList anArray)
	{
		actions = anArray;
	}

	/**
	 * Add the objects in the frame to the movie. The contents of the
	 * definitions and commands arrays are added to the movie. If a label is
	 * assigned to the frame then an FSFrameLabel object is added to the movie.
	 * If actions are defined then an FSDoAction object is added containing the
	 * actions defined in the frame.
	 * 
	 * @param aMovie
	 *            an FSMovie object.
	 */
	public void addToMovie(FSMovie aMovie)
	{
		if (definitions != null || definitions.size() > 0)
		{
			for (Iterator i = definitions.iterator(); i.hasNext();)
				aMovie.add((FSMovieObject) i.next());
		}

		if (label != null && label.length() > 0)
			aMovie.add(new FSFrameLabel(label));

		if (actions != null && actions.size() > 0)
			aMovie.add(new FSDoAction(actions));

		for (Iterator j = commands.iterator(); j.hasNext();)
			aMovie.add((FSMovieObject) j.next());

		aMovie.add(new FSShowFrame());
	}
}
