using System;
using System.Collections.Generic;
using System.Text;
using Vortex.Drawing;
using Vortex.SceneFramework;

namespace Vortex.Demo.Pacman {

	///<summary>Enumerates all of ghost states</summary>
	enum GhostState {
		///<summary>Waiting state. Ghost is in cage.</summary>
		Waiting,
		///<summary>Ghost is hunting Pacman</summary>
		Hunting,
		///<summary>Ghost is escaping from Pacman (confusing)</summary>
		Escaping,
		///<summary>Ghost is returning to cage after Pacman eat it</summary>
		Returning
	}

	/// <summary>
	/// Defines ghost unit element
	/// </summary>
	class GhostUnit : Unit {
		static readonly ColorU[] GHOST_CONFUSED_BODY_COLORS = new ColorU[] { ColorU.Navy, ColorU.Wheat };
		static readonly ColorU[] GHOST_CONFUSED_ELEMENTS_COLORS = new ColorU[] { ColorU.White, ColorU.Red };

		#region Sprites

		[ResourceRef] public SpriteList GhostBodyList { get; set; }
		[ResourceRef] public Sprite GhostConfused { get; set; }
		[ResourceRef] public Sprite GhostEyes { get; set; }
		[ResourceRef] public Sprite GhostPupils { get; set; }

		private SimpleSpriteAnimation _ghostBodyAnimation;

		#endregion

		/// <summary>
		/// Ghost state controller
		/// </summary>
		private StateController<GhostState> _controller = new StateController<GhostState>(GhostState.Waiting);

		///<summary>Initial ghost location</summary>
		private Vector2 _initLocation;

		/// <summary>
		/// Gets or sets the color of ghost unit.
		/// </summary>
		/// <value>The color of ghost.</value>
		public ColorU Color { get; set; }

		/// <summary>
		/// Gets the state of ghost.
		/// </summary>
		/// <value>The state of ghost.</value>
		public GhostState State {
			get { return _controller.State; }
		}

		#region Drawing Ghost

		/// <summary>
		/// Draws ghost
		/// </summary>
		/// <param name="canvas">The canvas to use for element drawing.</param>
		protected override void Draw(Canvas2D canvas) {

			//draw background if ghost is returning its cage
			if (_controller.State != GhostState.Returning) {
				ColorU bgColor = Color;
				if (_controller.State == GhostState.Escaping) {
					bgColor = GHOST_CONFUSED_BODY_COLORS[0];
					//check for make color flashing
					if (_controller.TimeLeft < Constants.GHOST_ESCAPE_FLASHING_TIME) {
						bgColor = GHOST_CONFUSED_BODY_COLORS[(_controller.TimeLeft * 2) % 1f > 0.5f ? 1 : 0 ];
					}
					bgColor = bgColor.MultiplyAlpha(0.75f);
				}
				canvas.DrawSprite(Vector2.Zero, Vector2.DoubleUnit, _ghostBodyAnimation.ToSprite(), bgColor);
			}

			//draw eyes
			canvas.DrawSprite(Vector2.Zero, Vector2.DoubleUnit, GhostEyes, ColorU.White);
			canvas.DrawSprite(Direction * 0.08f, Vector2.DoubleUnit, GhostPupils, ColorU.White);
		}

		#endregion

		#region Methods

		public override void Update(float timeDelta) {
			//update ghost animation
			_ghostBodyAnimation.Update(timeDelta);

			//all of stuff can be invoked if world is enabled
			if (World.IsEnabled) {
				//update state controller
				_controller.Update(timeDelta);

				//process ghost movement
				ProcessMovement(timeDelta);
				//checks world bounds
				CheckWorldBounds();
			}
		}

		private void ProcessMovement(float timeDelta) {
			Vector2 roundedLocation = Location.Rounded;
			//comming point is when direction to closest point is the same as direction of unit or point is reached
			bool comingPoint = (roundedLocation - Location).Normalized == Direction || roundedLocation == Location;

			if (Location.Distance(Location.Rounded) < TURN_THRESOLD && comingPoint) {
				//obtains all of movements
				List<Vector2> movementOptions = GetMovementOptions(Location.Rounded);
				//check do we have wall forward
				bool wallForward = !movementOptions.Contains(Direction);
				//remove opposite direction if there are no wall forward || more than one options we have
				if (!wallForward || movementOptions.Count > 1) movementOptions.Remove(Direction.Negative);
				
				//remove up option if ghost is waiting
				if (_controller.State == GhostState.Waiting) {
					movementOptions.Remove(Vector2.Up);
				}

				if (movementOptions.Count > 0) {
					//lets choose random direction :)
					SnapCell();
					Direction = movementOptions[Vortex.Utils.RandomUtils.NextInt(movementOptions.Count)];
				} else {
					Direction = Vector2.Zero;
				}
			}

			Vector2 newLocation = Location + Speed * Direction * timeDelta;
			Location = newLocation;
		}

		/// <summary>
		/// Counts the number of movement options for that point.
		/// </summary>
		/// <param name="fromPoint">From point.</param>
		/// <returns></returns>
		private Vector2[] DIRECTION_OPTIONS = new Vector2[] { Vector2.Right, Vector2.Left, Vector2.Up, Vector2.Down };
		private List<Vector2> GetMovementOptions(Vector2 fromPoint) {
			List<Vector2> options = new List<Vector2>();
			foreach (Vector2 dir in DIRECTION_OPTIONS) {
				if (CanGoTo(fromPoint, dir)) {
					options.Add(dir);
				}
			}
			return options;
		}

		/// <summary>
		/// Process ghost unit loading
		/// </summary>
		/// <param name="resources">The domain resource collection.</param>
		public override void ComponentLoad(Vortex.Resources.IResourceCollection resources) {
			//create animation of ghost background
			_ghostBodyAnimation = new SimpleSpriteAnimation(GhostBodyList, 12f, true);
		}

		/// <summary>
		/// Initializes the ghost unit.
		/// </summary>
		/// <param name="location">The start location of unit.</param>
		public void Initialize(Vector2 location, float waitTime) {
			_initLocation = location;
			Location = location;
			Direction = Vector2.Left;
			_controller.SwitchState(GhostState.Waiting, waitTime + 0.01f, GhostState.Hunting);
		}

		/// <summary>
		/// Scares this ghost, make it escaping from pacman.
		/// </summary>
		public void Scare(float scareDuration) {
			//just a switch state for a some period
			_controller.SwitchState(GhostState.Escaping, scareDuration, GhostState.Hunting); 
		}

		///<summary>Kills the ghost</summary>
		public void Kill() {
			//just make ghost returning home
			_controller.State = GhostState.Returning;
		}


		#endregion
	}
}
