using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;

namespace XELF.Sphynx {

	class MenuItem {
		public readonly StringBuilder Text = new StringBuilder();
		public SpriteFont Font;
		public Vector2 Position;
		public Color ShadowColor = Color.Black;
		public Color DefaultColor = Color.White;
		public Color SelectedColor = Color.Orange;
		public event EventHandler Click;
		public bool Selected;
		public Texture2D Texture;
		public Rectangle Rectangle;

		public string TextString {
			get { return Text.ToString(); }
			set { Text.Clear(); Text.Append(value); }
		}

		public virtual void Update() {
		}

		public void PerformClick() {
			if (Click != null) {
				Click(this, EventArgs.Empty);
			}
		}
	}

	class Menu {
		int selectedIndex;
		readonly SpriteBatch spriteBatch;
		readonly GraphicsDevice graphicsDevice;
		public readonly List<MenuItem> Items = new List<MenuItem>();

		public MenuItem Selected {
			get {
				if (SelectedIndex < 0 || SelectedIndex >= Count) return null;
				return Items[selectedIndex];
			}
		}

		public int SelectedIndex {
			get { return selectedIndex; }
			set {
				selectedIndex = value;
				UpdateSelectedItems();
			}
		}

		void UpdateSelectedItems() {
			for (int i = 0; i < Items.Count; i++) {
				Items[i].Selected = selectedIndex == i;
			}
		}

		public Menu(GraphicsDevice graphicsDevice) {
			this.graphicsDevice = graphicsDevice;
			spriteBatch = new SpriteBatch(graphicsDevice);
		}
		Vector2 shadowOffset = new Vector2(1, 1);
		public void Draw() {
			spriteBatch.Begin();
			foreach (var item in Items) {
				if (item.Texture != null)
					spriteBatch.Draw(item.Texture, item.Rectangle, Color.White);
				Vector2 shadowPosition;
				Vector2.Add(ref item.Position, ref shadowOffset, out shadowPosition);
				spriteBatch.DrawString(item.Font, item.Text, shadowPosition,
					item.ShadowColor);
				spriteBatch.DrawString(item.Font, item.Text, item.Position,
					item.Selected ? item.SelectedColor : item.DefaultColor);
			}
			spriteBatch.End();
		}
		public void Update() {
			foreach (var item in Items) {
				item.Update();
			}
		}
		public int Count {
			get { return Items.Count; }
		}

		public void Add(MenuItem item) {
			Items.Add(item);
			UpdateSelectedItems();
		}

		public void Clear() {
			Items.Clear();
		}
	}

	class StageSelector : DrawableGameComponent {
		public Menu Menu { get; protected set; }
		SpriteFont font;
		Texture2D background;

		public event Action<int> StageLoading;
		public event Action<int> StageSaving;

		KeyboardState current;
		KeyboardState previous;

		public StageSelector(Game game)
			: base(game) {
		}

		public void ShowLoadMenu() {
			var lineSpacing = font.LineSpacing;
			Menu.Clear();
			{
				var item = new MenuItem
				{
					Font = font,
					Position = new Vector2(0x60, 0x40 + 0 * lineSpacing),
				};
				item.Text.AppendFormat("Stage Load");
				item.Texture = background;
				item.Rectangle = new Rectangle(0, 0, GraphicsDevice.Viewport.Width, GraphicsDevice.Viewport.Height);
				Menu.Add(item);
			}

			for (int i = 0; i < 10; i++) {
				var item = new MenuItem
				{
					Font = font,
					Position = new Vector2(0x80, 0x40 + (i + 1) * lineSpacing),
				};
				item.Text.AppendFormat("Stage {0}", i);
				item.Click += LoadStage;
				Menu.Add(item);
			}

			{
				var item = new MenuItem
				{
					Font = font,
					Position = new Vector2(0x60, 0x40 + 11 * lineSpacing),
				};
				item.Text.AppendFormat("Cancel");
				item.Click += LoadStage;
				Menu.Add(item);
			}

			Menu.SelectedIndex = 1;
		}
		public void ShowSaveMenu() {
			var lineSpacing = font.LineSpacing;
			Menu.Clear();
			{
				var item = new MenuItem
				{
					Font = font,
					Position = new Vector2(0x60, 0x40 + 0 * lineSpacing),
				};
				item.Text.AppendFormat("Stage Save");
				item.Texture = background;
				item.Rectangle = new Rectangle(0, 0, GraphicsDevice.Viewport.Width, GraphicsDevice.Viewport.Height);
				Menu.Add(item);
			}

			for (int i = 0; i < 10; i++) {
				var item = new MenuItem
				{
					Font = font,
					Position = new Vector2(0x80, 0x40 + (i + 1) * lineSpacing),
				};
				item.Text.AppendFormat("Stage {0}", i);
				item.Click += SaveStage;
				Menu.Add(item);
			}

			{
				var item = new MenuItem
				{
					Font = font,
					Position = new Vector2(0x60, 0x40 + 11 * lineSpacing),
				};
				item.Text.AppendFormat("Cancel");
				item.Click += SaveStage;
				Menu.Add(item);
			}

			Menu.SelectedIndex = 1;
		}

		void LoadStage(object sender, EventArgs e) {
			if (StageLoading != null) StageLoading(Menu.SelectedIndex - 1);
		}
		void SaveStage(object sender, EventArgs e) {
			if (StageSaving != null) StageSaving(Menu.SelectedIndex - 1);
		}

		protected override void LoadContent() {
			Menu = new Menu(GraphicsDevice);
			font = Game.Content.Load<SpriteFont>("BigFont");
			background = new Texture2D(GraphicsDevice, 1, 1);
			background.SetData(new[] { new Color(0x40, 0x60, 0xa0, 0xc0), });
			base.LoadContent();
		}

		public void MoveUp() {
			if (--Menu.SelectedIndex < 1) {
				Menu.SelectedIndex = Menu.Count - 1;
			}
		}

		public void MoveDown() {
			if (++Menu.SelectedIndex >= Menu.Count) {
				Menu.SelectedIndex = 1;
			}
		}
		public void PerformClick() {
			if (Menu.Selected != null)
				Menu.Selected.PerformClick();
		}

		public void Cancel() {
			Menu.SelectedIndex = Menu.Count - 1;
			if (Menu.Selected != null)
				Menu.Selected.PerformClick();
		}
		public void Hide() {
			Menu.Clear();
		}

		public override void Draw(GameTime gameTime) {
			Menu.Draw();
			base.Draw(gameTime);
		}
		public override void Update(GameTime gameTime) {
			previous = current;
			current = Keyboard.GetState();
			if (Enabled) {
				if (IsPress(Keys.Up)) {
					MoveUp();
				}
				if (IsPress(Keys.Down)) {
					MoveDown();
				}
				if (IsPress(Keys.Enter)) {
					PerformClick();
				}
				if (IsPress(Keys.Escape)) {
					Cancel();
				}
			}
			Menu.Update();
			base.Update(gameTime);
		}
		public bool IsPress(Keys key) {
			return current.IsKeyDown(key) && previous.IsKeyUp(key);
		}
	}
}
