namespace Icarus.Graphics.Animation
{
	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.AnimationDelay = 1;
			this.AnimationType = AnimationType.None;
		}

		/**
		 * Sets up the new frameset animation.
		 */
		public FramesetAnimation(Frameset frames, AnimationType loopMode, long delay)
		{
			this.AnimationType = loopMode;
			this.setFrameset(frames);
			this.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();
		}

		public string FramesetPath
		{
			get { return this.m_FramesetPath; }
			set 
			{
                this.m_FramesetPath = value;
                this.setFrameset(FramesetManager.getInstance().getFrameset(value));
            }
		}

		public AnimationType AnimationType
		{
			get { return m_AnimationType; }
			set
			{
                m_AnimationType = value;
                processAnimationType();
            }
		}

		public string AnimationTypeId
		{
			get { return this.m_AnimationType.ToString(); }
			set 
			{
				this.AnimationType = AnimationType.FromString(value);
            }
		}

		public long AnimationDelay
		{
			get { return m_AnimationDelay; }
			set
			{
                m_AnimationDelay = value;
            }
		}

		public int CurrentFrameIndex
		{
			get { return m_CurrentFrameIndex; }
		}

		public IFrame CurrentFrame
		{
			get
			{
                return m_Frameset.getFrameAt(this.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.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;
		}
	}
}
