package Icarus.Graphics.Animation;

/**
 * This is an animation that uses a frameset as the underlying set of images.
 * 
 * @author $Author: asskoala $
 * @version $Revision: 1.1 $ $Date: 2005/12/11 21:07:21 $
 */
public class FramesetAnimation
{
	/**
	 * The path where the animation was loaded from.
	 */
	private String m_FramesetPath;

	/**
	 * The set of frames used for the animation.
	 */
	private Frameset m_Frameset;

	/**
	 * The looping type of the animation.
	 */
	private AnimationType m_AnimationType;

	/**
	 * The delay, in abstract time units, between the frames of the animation.
	 */
	private long m_AnimationDelay;

	/**
	 * The index of the currently displayed frame in the animation.
	 */
	private int m_CurrentFrameIndex;

	/**
	 * If we receive an update message and it is not time yet to update the frame, then we can just add to the cumulative delay over here, and check the correct number next time around.
	 */
	private long m_CumulativeDelay;

	/**
	 * Sets up a blank frameset animation.
	 */
	public FramesetAnimation()
	{
		this.set_AnimationDelay(1);
		this.set_AnimationType(AnimationType.None);
	}

	/**
	 * Sets up the new frameset animation.
	 */
	public FramesetAnimation(Frameset frames, AnimationType loopMode, long delay) 
	{
		this.set_AnimationType(loopMode);
		this.setFrameset(frames);
		this.set_AnimationDelay(delay);
	}

	/**
	 * This is just a little optimization that makes it so when the animation has only one frame, the update method won't try to do anything funny.
	 */
	private void processAnimationType() 
	{
		if (m_Frameset != null && m_Frameset.getFrameCount() < 2) 
		{
			//No reason to loop:
			m_AnimationType = AnimationType.None;
		}
	}

	/**
	 * Returns the frameset.
	 */
	public Frameset getFrameset() { return m_Frameset; }

	/**
	 * Sets up a new frameset.
	 */
	protected void setFrameset(Frameset frames) 
	{
		m_Frameset = frames;
		m_CurrentFrameIndex = 0;
		m_CumulativeDelay = 0;

		processAnimationType();
	}

	/**
	 * Returns the path to the frameset.
	 * 
	 * @property
	 */
	public String get_FramesetPath() { return this.m_FramesetPath; }

	/**
	 * Sets the path to the frameset.
	 * 
	 * @property
	 */
	public void set_FramesetPath(String value) 
	{
		this.m_FramesetPath = value;
		this.setFrameset(FramesetManager.getInstance().getFrameset(value));
	}

	/**
	 * Returns the animation type.
	 * 
	 * @property
	 * @attribute System.Xml.Serialization.XmlIgnore()
	 */
	public AnimationType get_AnimationType() { return m_AnimationType; }

	/**
	 * Sets up the new animation type.
	 * 
	 * @property
	 */
	public void set_AnimationType(AnimationType type) 
	{
		m_AnimationType = type; 
		processAnimationType();
	}

	/**
	 * This is sort of a hack property for serialization purposes because J# + enums = headache.
	 * 
	 * @property
	 */
	public String get_AnimationTypeId() { return this.m_AnimationType.ToString(); }

	/**
	 * This is sort of a hack property for serialization purposes because J# + enums = headache.
	 * 
	 * @property
	 */
	public void set_AnimationTypeId(String v) 
	{
		this.set_AnimationType(AnimationType.FromString(v));
	}

	/**
	 * Returns the delay, in abstract time units, between the frames of the animation.
	 * 
	 * @property
	 */
	public long get_AnimationDelay() { return m_AnimationDelay; }

	/**
	 * Sets the new animation delay.
	 * 
	 * @property
	 */
	public void set_AnimationDelay(long delay) { m_AnimationDelay = delay; }

	/**
	 * Returns the index of the current frame.
	 * 
	 * @property
	 * @attribute System.Xml.Serialization.XmlIgnore()
	 */
	public int get_CurrentFrameIndex() 
	{
		return m_CurrentFrameIndex;
	}

	/**
	 * Returns the current frame of the animation.
	 * 
	 * @property
	 * @attribute System.Xml.Serialization.XmlIgnore()
	 */
	public IFrame get_CurrentFrame() 
	{
		return m_Frameset.getFrameAt(this.get_CurrentFrameIndex());
	}

	/**
	 * Performs an update of the animation given the delay since the previous call in abstract time units.
	 */
	public void update(long time) 
	{
		if (m_AnimationType == AnimationType.None) 
		{
			//Nothing much to do...
			return;
		}

		m_CumulativeDelay += time;
		if (m_CumulativeDelay < this.get_AnimationDelay()) 
		{
			//Not time yet...
			return;
		}
		
		m_CumulativeDelay = 0;

		int index = m_CurrentFrameIndex;
		if (index >= m_Frameset.getFrameCount() - 1) 
		{
			if (m_AnimationType == AnimationType.LoopForever) 
			{
				index ++;
				index %= m_Frameset.getFrameCount();
			}
		} 
		else 
		{
			index ++;
		}

		m_CurrentFrameIndex = index;
	}
}
