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;

namespace PickupTheCrew
{
	public class Player : Sprite
	{
		float facingAngle = 0;
		//float facingAngle = (float)Math.PI;
		float turningSpeed = (float)(Math.PI / 500);
		float movementSpeed = (float)0.2;

		Vector2 offset = Vector2.Zero;

		public bool isMovingForwards = false;

		/// Controls ///
		bool invertedControls = false;
		KeyboardState currentKeyboardState;
		//MouseState currentMouseState;

		/// <summary>
		/// Makes a new player.
		/// </summary>
		/// <param name="inPosition">The position at which the player should start.</param>
		public Player(Vector2 inPosition) : base(PickupTheCrew.textures["player"], new Vector2(16, 21), inPosition)
		{
			Size = new Vector2(32, 42);
			offset.X = Size.X / 2;
			offset.Y = (Size.Y * 2) / 3;

			if(invertedControls)
			{
				turningSpeed = -turningSpeed;
			}
		}

		/// <summary>
		/// Updates the players state ready for the next frame.
		/// </summary>
		/// <param name="gameTime">The time that has passed since the last frame.</param>
		/// <param name="game">A reference to the game this player is part of.</param>
		public override void Update(GameTime gameTime, PickupTheCrew game)
		{
			currentKeyboardState = Keyboard.GetState();
			float ticksSinceFrame = gameTime.ElapsedGameTime.Milliseconds;

			switch(game.State)
			{
				case GameState.Playing:
					if(currentKeyboardState.IsKeyDown(Keys.Up))
					{
						isMovingForwards = true;
						Speed.X = (float)(movementSpeed * Math.Sin(facingAngle + Math.PI));
						Speed.Y = (float)(movementSpeed * Math.Cos(facingAngle + Math.PI));
					}
					else
					{
						isMovingForwards = false;
						Speed.X = 0;
						Speed.Y = 0;
					}

					if(currentKeyboardState.IsKeyDown(Keys.Left))
					{
						facingAngle += ticksSinceFrame * turningSpeed;
					}
					if(currentKeyboardState.IsKeyDown(Keys.Right))
					{
						facingAngle -= ticksSinceFrame * turningSpeed;
					}
					break;

				case GameState.Attract:
					CrewMember closestCrewMember = null;
					float minDistance = 999999;
					// Find the closest crew member who hasn't been rescued
					foreach(CrewMember nextCrewMember in game.CrewMemberObjects)
					{
						// Don't bother with this crew member if they have already been rescued
						if (nextCrewMember.State != CrewMemberState.AwaitingRescue)
							continue;
						
						float nextDistance = Utilities.CalculateDistance(Position, nextCrewMember.Position);
						if(nextDistance < minDistance)
						{
							closestCrewMember = nextCrewMember;
							minDistance = nextDistance;
						}
					}

					// If we couldn't find a crew member to chase it means that they have all been rescued
					// We pick any old crew member to follow at this point because the level up code will handle resetting the attract screen
					if (closestCrewMember == null)
						closestCrewMember = game.CrewMemberObjects[0];
					
					// Calculate the distance betwene us and the crew member we picked above
					Vector2 diffDistance = Utilities.CalculateDistanceDifference(Position, closestCrewMember.Position);

					// Set the speed
					Speed.X = (diffDistance.X / minDistance) * movementSpeed;
					Speed.Y = (diffDistance.Y / minDistance) * movementSpeed;

					// Calculate and set the angle
					facingAngle = (float)(-Math.Atan2(diffDistance.Y, diffDistance.X) + ((3 * Math.PI) / 2));

					break;
			}
			

			// 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;
					}
				}
			}

			//Position.X += offset.X;
			//Position.Y += offset.Y;
			
			base.Update(gameTime, game); // update rendering information etc.

			//Position.X -= offset.X;
			//Position.Y -= offset.Y;
		}

		/// <summary>
		/// Draws the player to the screen. Note that this method assumes you have called SpriteBatch.Begin() already.
		/// </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)
		{

			//ActualArea.X += (int)offset.X;
			//ActualArea.X += (int)offset.Y;

			Rectangle ActualDrawArea = DrawArea;
			ActualDrawArea.Offset((int)TextureOrigin.X, (int)TextureOrigin.Y);

#if DEBUG
			Utilities.DrawRectangle(spriteBatch, DrawArea, Color.Blue, 2);
			Utilities.DrawRectangle(spriteBatch, ActualDrawArea, Color.LightBlue, 2);
#endif
			//spriteBatch.Draw(Texture, ActualDrawArea, null, Colour, -(facingAngle + (float)Math.PI), TextureOrigin, SpriteEffects.None, 0f);
			spriteBatch.Draw(Texture, ActualDrawArea, null, Colour, -facingAngle, TextureOrigin, SpriteEffects.None, 0f);
		}

		/// <summary>
		/// Resets the player.
		/// </summary>
		/// <param name="game">A reference to the game this player is part of.</param>
		public override void Reset(PickupTheCrew game)
		{
			//facingAngle = (float)Math.PI;
			facingAngle = 0;

			base.Reset(game);

			// Reposition ourselves
			Position.X = game.GraphicsDevice.Viewport.Width / 2;
			Position.Y = game.GraphicsDevice.Viewport.Height / 2;

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

		/// <summary>
		/// Saves the player's data to the given StreamWriter.
		/// </summary>
		/// <param name="destination"></param>
		public override void Save(StreamWriter destination)
		{
			base.Save(destination);

			destination.Write("facingAngle:{0}|turningSpeed:{1}|", facingAngle, turningSpeed);
			destination.Write("movementSpeed:{0}|invertedControls:{1}", movementSpeed, invertedControls);

			destination.WriteLine();
		}

		/// <summary>
		/// Special constructor for loading pllayers back from files that takes a StreamReader as input.
		/// </summary>
		/// <param name="source"></param>
		public Player(StreamReader source) : base(source)
		{
			Dictionary<string, string> saveData = Utilities.DecodeSaveString(source.ReadLine());

			facingAngle = float.Parse(saveData["facingAngle"]);
			turningSpeed = float.Parse(saveData["turningSpeed"]);
			movementSpeed = float.Parse(saveData["movementSpeed"]);

			if (saveData["invertedControls"] == "true")
				invertedControls = true;
			else
				invertedControls = false;
		}
	}
}
