using System;
using System.Collections.Generic;
using System.IO;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Storage;
using Microsoft.Xna.Framework.Utilities;
using Microsoft.Xna.Framework.Windows;
using Microsoft.Xna.Framework.Design;
//using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Media;

namespace PickupTheCrew
{
	/// <summary>
	/// All the possible crew member colours.
	/// </summary>
	public enum CrewMemberColour
	{
		LieutenantSky,			// blue
		MidshipmanVerde,		// green
		CabinboyCrimson,		// red
		StokerOrange,			// orange
		AbleSeamanShockingPink,	// pink
		CommanderCustard		// yellow
	};

	/// <summary>
	/// All the possible crew member states.
	/// </summary>
	public enum CrewMemberState
	{
		AwaitingRescue,	// we are awaitng rescue
		Rescued			// we have been rescued
	}

	/// <summary>
	/// A class that represents a single crew member.
	/// </summary>
	public class CrewMember: Sprite
	{
		public CrewMemberColour Type;
		public CrewMemberState State = CrewMemberState.AwaitingRescue;

		// The minimum distance that we should spawn away from the player when resetting
		static int minPlayerSpawnDistance = 80;

		// The minimum distance away from a shark that we should spawn.
		static int minSharkSpawnDistance = 100;

		// The maximum distance from the crewmember the player can be in order to rescue us
		int rescueDistance = 40;

		// The speed at which we move
		float movementSpeed = (float)0.3;

		// The sprite that we should follow when we are rescued
		public Sprite targetSprite;

		/// <summary>
		/// Makes a new crew member.
		/// </summary>
		/// <param name="inType">The colour that the new cfrew member should have.</param>
		/// <param name="inPosition">The starting position for the new crew member.</param>
		public CrewMember(CrewMemberColour inType, Vector2 inPosition): base(PickupTheCrew.textures[inType.ToString()], inPosition)
		{
			Type = inType;

			Size.X = Size.Y = 32;
			rescueDistance = (int)(Size.X);
		}

		/// <summary>
		/// Updates the crew member's state ready for the next frame.
		/// </summary>
		/// <param name="gameTime">The time that has passed since the last frame was drawn</param>
		/// <param name="game">A reference to the game that this crew member is part of.</param>
		public override void Update(GameTime gameTime, PickupTheCrew game)
		{
			if(State == CrewMemberState.AwaitingRescue && // If we are awaiting rescue....
				Utilities.CalculateDistance(Position, game.PlayerObject.Position) < rescueDistance) // ...and we are within the rescue distance...
			{
				// We should be rescued

				// Update our State
				State = CrewMemberState.Rescued;
				// Update the HUD
				game.DisplayMessage = Utilities.CamelCaseToReadable(Type.ToString()) + " Rescued";

				// Set our target that we should follow now that we have been rescued
				if(game.LastRescuedCrewMember == null)
					targetSprite = game.PlayerObject;
				else
					targetSprite = game.LastRescuedCrewMember;
				// We are now the last rescued CrewMember, so we should tell the game this
				game.LastRescuedCrewMember = this;

				// Play a sound
				//SoundEffectInstance sfxInstance = game.Sfx_PickupCrewMember.CreateInstance();
				//sfxInstance.Play();
				//game.Sfx_PickupCrewMember.Play(1.0f, 0f, 0f);
				if(game.State != GameState.Attract && MediaPlayer.State != MediaState.Playing)
					MediaPlayer.Play(game.Sounds["Pickup"]);
				// Increase the player's score relative to the level they are on
				game.Score += 10 * game.Level;
			}

			if (State == CrewMemberState.Rescued)
			{
				// We have been rescued, so we need to follow the target
				Vector2 target = targetSprite.Position;

				// Get the vector distance to the target
				Vector2 diff = Utilities.CalculateDistanceDifference(Position, target);

				// Distance to target as a number
				float length = (float)Math.Sqrt((diff.X * diff.X) + (diff.Y * diff.Y));

				// Update the speed
				if (length > targetSprite.Size.X) // todo make sure that we don't collide with any other sharks
				{
					Speed = new Vector2(
						(diff.X / length) * movementSpeed,
						(diff.Y / length) * movementSpeed
					);
				}
				else
				{
					Speed = Vector2.Zero;
				}

				// Don't move if the player isn't moving
				// We ignore this though is the AI is controlling the player
				if(!game.PlayerObject.isMovingForwards && game.State != GameState.Attract)
				{
					Speed = Vector2.Zero;
				}
			}

			// Make sure that we haven't hit any sharks
			foreach (Shark nextShark in game.SharkObjects)
			{
				if (DrawArea.Intersects(nextShark.DrawArea))
				{
					switch (game.State)
					{
						// End the game if the player was playing
						case GameState.Playing:
							game.GameOver();
							break;

						// Do a soft reset if the AI was playing
						case GameState.Attract:
							game.SoftReset();
							break;
					}
				}
			}

			base.Update(gameTime, game);
		}

		/// <summary>
		/// Draws the crew member to the screen.
		/// </summary>
		/// <param name="gameTime">The time that has passed since the last frame was drawn.</param>
		/// <param name="spriteBatch">The SpriteBatch that should be used for drawing.</param>
		public override void Draw(GameTime gameTime, SpriteBatch spriteBatch)
		{
#if DEBUG
			Color boxColour = Color.Silver;
			if (State == CrewMemberState.Rescued)
				boxColour = Color.Gold;

			Utilities.DrawRectangle(spriteBatch, DrawArea, boxColour, 2);
#endif
			base.Draw(gameTime, spriteBatch);
		}

		/// <summary>
		/// Resets the crew member.
		/// </summary>
		/// <param name="game">A reference to the game this crew member is part of.</param>
		public override void Reset(PickupTheCrew game)
		{
			State = CrewMemberState.AwaitingRescue;
			targetSprite = null;

			base.Reset(game);

			// Reposition ourselves in a suitable location
			float distanceToPlayer, closestSharkDistance;
			do
			{
				Position.X = game.randGenerator.Next((int)Size.X, (int)(game.GraphicsDevice.Viewport.Width - Size.X));
				Position.Y = game.randGenerator.Next((int)Size.Y, (int)(game.GraphicsDevice.Viewport.Height - Size.Y));

				distanceToPlayer = Utilities.CalculateDistance(Position, game.PlayerObject.Position);

				closestSharkDistance = 999999;
				foreach (Shark nextShark in game.SharkObjects)
				{
					float nextSharkDistance = Utilities.CalculateDistance(Position, nextShark.Position);
					if (nextSharkDistance < closestSharkDistance)
						closestSharkDistance = nextSharkDistance;
				}
			} while (distanceToPlayer < minPlayerSpawnDistance ||
				closestSharkDistance < minSharkSpawnDistance);

			// Update the DrawArea since the main update method isn't called during the LevelUp state
			UpdateDrawArea();
		}


		/// <summary>
		/// Saves the crew member to the given StreamWriter.
		/// </summary>
		/// <param name="destination">The StreamWriter to which we should save ourselves.</param>
		public override void Save(StreamWriter destination)
		{
			base.Save(destination);

			destination.Write("currentState:{0}", State.ToString());

			destination.WriteLine();
		}

		/// <summary>
		/// Special constructor for loading crew members back from files.
		/// </summary>
		/// <param name="source">The StreamReader that we should load a new crew member back from.</param>
		public CrewMember(StreamReader source) : base(source)
		{
			Dictionary<string, string> saveData = Utilities.DecodeSaveString(source.ReadLine());

			State = (CrewMemberState)Enum.Parse(typeof(CrewMemberState), saveData["currentState"]);
		}
	}
}
