using System;
using System.Drawing;

using Icarus.Graphics.Animation;

namespace Icarus.LUCIE
{
	/// <summary>
	/// This is a status bar, which can be used to display the value of something out of a maximum. So, for examples, such things as health of the player, or the progress of loading a level can all be displayed using a bar.
	/// </summary>
	public class StatusBar : AbstractComponent
	{
		private int mMaximumValue;
		private int mCurrentValue;

		private int mLeftOffset;
		private AbstractComponent mRightEnd;
		private TiledFrame mBar;

		/// <summary>
		/// Creates a new status bar.
		/// </summary>
		/// <param name="leftEnd">The image to use on the left end of the bar. Can be null to have no left end.</param>
		/// <param name="barFiller">The image to use for the filler for the bar itself. Must be provided.</param>
		/// <param name="rightEnd">The image to use on the right end of the bar. Can be null to have no right end.</param>
		public StatusBar(Bitmap leftEnd, Bitmap barFiller, Bitmap rightEnd)
		{
			int height = 0;

			if (leftEnd == null)
			{
				mLeftOffset = 0;
			}
			else
			{
				mLeftOffset = leftEnd.Width;
				ImageComponent left = new ImageComponent(leftEnd);
				left.Location = new Point(0, 0);
				left.IsVisible = true;
				this.AddChild(left);

				height = Math.Max(height, left.Size.Height);
			}

			if (rightEnd == null) 
			{
				mRightEnd = null;
			}
			else
			{
				mRightEnd = new ImageComponent(rightEnd);
				mRightEnd.Location = new Point(mLeftOffset, 0);
				mRightEnd.IsVisible = true;
				this.AddChild(mRightEnd);

				height = Math.Max(height, mRightEnd.Size.Height);
			}

			mBar = new TiledFrame();
			IFrame barFrame = AnimationFactory.getInstance().makeBlankFrame();
			barFrame.initialize(new Point(0, 0), barFiller);
			mBar.ChildRenderable = barFrame;
			mBar.GridSize = new Size(0, 1);

			height = Math.Max(height, mBar.GetSize().Height);

			//Set my own size, at least we know the height:
			Size s = this.Size;
			s.Height = height;
			this.Size = s;
		}

		/// <summary>
		/// The maximum value the bar can display.
		/// </summary>
		public int MaximumValue
		{
			get { return mMaximumValue; }
			set
			{
				// Set the value:
				mMaximumValue = value;

				// Enforce current value constraints and update as needed:
				CurrentValue = CurrentValue; 
			}
		}

		/// <summary>
		/// The current value the bar is displaying.
		/// </summary>
		public int CurrentValue
		{
			get { return mCurrentValue; }
			set
			{
				if (mCurrentValue == value) return;

				mCurrentValue = Math.Min(value, MaximumValue);
				ProcessValueUpdate();
			}
		}

		/// <summary>
		/// Updates the visual representation of the bar as needed.
		/// </summary>
		private void ProcessValueUpdate()
		{
			// Compute the length of the bar for 100%:
			double length = this.Size.Width - mLeftOffset - mRightEnd.Size.Width;

			// Compute the percent fill of the bar:
			double percent = ((double) CurrentValue) / ((double) MaximumValue);

			if (percent <= 0.001) 
			{
				this.IsVisible = false;
			} 
			else 
			{
				this.IsVisible = true;

				// Compute the length of the bar:
				length *= percent;

				// Now, find the number of units of filler we need:
				int fillerCount = (int) Math.Floor(length / ((double) mBar.ChildRenderable.GetSize().Width));

				// Now we can set the size of the tiled grid:
				Size s = mBar.GridSize;
				s.Width = fillerCount;
				mBar.GridSize = s;

				// Finally, offset right end appropriately:
				Point l = mRightEnd.Location;
				l.X = mLeftOffset + mBar.GetSize().Width;
				mRightEnd.Location = l;
			}
		}

		protected override void RenderSelf(Rectangle coordinates, IRenderParameters parameters)
		{
			coordinates.X += mLeftOffset;

			mBar.Render(coordinates, parameters);

			coordinates.X -= mLeftOffset;
		}
	}
}
