using System;

using Icarus.Graphics.Animation;

namespace Icarus.Logic
{
	/// <summary>
	/// This is the representation of a single layer of background for the level. It's all fancy and parallax, and stuff.
	/// </summary>
	public class LevelBackground: IComparable
	{
		/// <summary>
		/// The actual background animation.
		/// </summary>
		private FramesetAnimation mBackgroundAnimation;

		/// <summary>
		/// Teh Parallax factor!!1! Read the documentation for the corresponding property.
		/// </summary>
		private double mParallaxFactor;

		/// <summary>
		/// The actual renderable used to display the background. See BackgroundRenderable property documentation for more information.
		/// </summary>
		private IRenderable mActualRenderable;

		private int mRows = 1;
		private int mColumns = 1;

		/// <summary>
		/// Gets the background animation.
		/// </summary>
		public FramesetAnimation Animation
		{
			get { return mBackgroundAnimation; }
			set 
			{
				mBackgroundAnimation = value;
				UpdateActualRenderable();
			}
		}

		/// <summary>
		/// Gets or sets how many times we need to tile the background vertically.
		/// </summary>
		public int Rows
		{
			get { return mRows; }
			set 
			{
				mRows = value; 
				UpdateActualRenderable();
			}
		}

		/// <summary>
		/// Gets or sets how many times we need to tile the background horizontally.
		/// </summary>
		public int Columns
		{
			get { return mColumns; }
			set 
			{
				mColumns = value; 
				UpdateActualRenderable();
			}
		}

		/// <summary>
		/// Gets or sets the parallax factor. The value of 0 means the background will be stationary. The value of 1 means it will be moving the same as the character. Stuff in between is stuff in between. Stuff outside the range... Hell, I don't know what will happen, but probably nothing good.
		/// </summary>
		public double ParallaxFactor
		{
			get { return mParallaxFactor; }
			set { mParallaxFactor = value; }
		}

        /// <summary>
        /// Gets the center of the background.
        /// </summary>
        [System.Xml.Serialization.XmlIgnore]
        public Icarus.Logic.Physics.Point BackgroundCenter
        {
            get 
            {
                return new Icarus.Logic.Physics.Point(this.BackgroundRenderable.GetSize().Width / 2,
                    this.BackgroundRenderable.GetSize().Height / 2,
                    0);
            }
		}

		/// <summary>
		/// Gets the renderable thing that we acutally need to make the background. This needs to be used instead of the Animation property, because the background will sometimes need to be tiled.
		/// </summary>
		[System.Xml.Serialization.XmlIgnore]
		public IRenderable BackgroundRenderable
		{
			get { return mActualRenderable; }
		}

		/// <summary>
		/// Sets the animation with the specified parameters.
		/// </summary>
		/// <param name="id"></param>
		/// <param name="t"></param>
		/// <param name="delay"></param>
		public void SetAnimation(string id, AnimationType t, int delay)
		{
            FramesetAnimation anim = new FramesetAnimation();
            anim.FramesetPath = id;
            anim.AnimationType = t;
            anim.AnimationDelay = delay;

            this.Animation = anim;

            UpdateActualRenderable();
		}

		#region IComparable Members

		/// <summary>
		/// Allows is to sort level backgrounds by parallax factor.
		/// </summary>
		/// <param name="obj"></param>
		/// <returns></returns>
		public int CompareTo(object obj)
		{
			if (obj is LevelBackground)
			{
				LevelBackground lb = (LevelBackground) obj;

				return this.ParallaxFactor.CompareTo(lb.ParallaxFactor);
			}

			throw new ArgumentException("obj is not a LevelBackground", "obj");
		}

		#endregion

		public void UpdateAnimation(long time)
		{
			int oldFrame = mBackgroundAnimation.CurrentFrameIndex;
			mBackgroundAnimation.update(time);

			if (mBackgroundAnimation.CurrentFrameIndex != oldFrame)
			{
				UpdateActualRenderable();
			}
		}

		private void UpdateActualRenderable()
		{
			if (this.Rows < 2 && this.Columns < 2)
			{
				this.mActualRenderable = this.Animation.CurrentFrame;
			}
			else
			{
				TiledFrame result = new TiledFrame();
				result.GridSize = new System.Drawing.Size(Columns, Rows);
				result.ChildRenderable = this.Animation.CurrentFrame;

				this.mActualRenderable = result;
			}
		}
	}
}
