using System;
using System.Collections.Generic;
using Library2D;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;

namespace GraveyardGraph
{
	/// <summary>
	/// This is the main type for your game.
	/// </summary>
	public class Game1 : Game
	{
		public const int TILE_SIZE = 64;

		GraphicsDeviceManager graphics;
		private RenderTarget2D wholescene;
		private RenderTarget2D waveEffect;

		SpriteBatch spriteBatch;
		private KeyboardState keyState;

		private static List<Wave> waves = new List<Wave>();
		private static List<Wave> deadWaves = new List<Wave> ();

		private static Map map;

		//letter bools Q Z P M
		//Q - top left
		private static bool IsQListening = false;
		//Z - bottom left
		private static bool IsZListening = false;
		//P - top right
		private static bool IsPListening = false;
		//M - bottom right
		private static bool IsMListening = false;		
		/// <summary>
		/// this is a list of available screens
		/// </summary>
		public enum screen
		{
			Menu,
			Play,
			Win,
			Lose
		};
		/// <summary>
		/// The game launches displaying this screen.
		/// </summary>
		private static screen currentScreen = screen.Play;
		public static screen CurrentScreen
		{
			get { return currentScreen; }
			set { currentScreen = value; }
		}

		private Effect m_Effect;

		public Game1()
		{
			graphics = new GraphicsDeviceManager(this);
			Content.RootDirectory = "Content";
		}

		/// <summary>
		/// Allows the game to perform any initialization it needs to before starting to run.
		/// This is where it can query for any required services and load any non-graphic
		/// related content.  Calling base.Initialize will enumerate through any components
		/// and initialize them as well.
		/// </summary>
		protected override void Initialize()
		{
			// TODO: Add your initialization logic here
			this.IsMouseVisible = false;
			base.Initialize();

			graphics.PreferredBackBufferHeight = 768;
			graphics.PreferredBackBufferWidth = 1024;
			graphics.ApplyChanges();

			m_Effect = Content.Load<Effect>("DistortionMap");
			wholescene = new RenderTarget2D (GraphicsDevice,
				GraphicsDevice.PresentationParameters.BackBufferWidth,
				GraphicsDevice.PresentationParameters.BackBufferHeight,
				false, GraphicsDevice.PresentationParameters.BackBufferFormat,
				DepthFormat.Depth24);
			waveEffect = new RenderTarget2D (GraphicsDevice,
				GraphicsDevice.PresentationParameters.BackBufferWidth,
				GraphicsDevice.PresentationParameters.BackBufferHeight,
				false, GraphicsDevice.PresentationParameters.BackBufferFormat,
				DepthFormat.Depth24);
		}

		/// <summary>
		/// LoadContent will be called once per game and is the place to load
		/// all of your content.
		/// </summary>
		protected override void LoadContent()
		{
			// Create a new SpriteBatch, which can be used to draw textures.
			spriteBatch = new SpriteBatch(GraphicsDevice);

			//TODO: use this.Content to load your game content here 

			ArtFetcher.Initialize(Content);
			SoundFetcher.Initialize(Content);
			map = Map.LoadMap("Tutorial.txt");
		}

		/// <summary>
		/// Allows the game to run logic such as updating the world,
		/// checking for collisions, gathering input, and playing audio.
		/// </summary>
		/// <param name="gameTime">Provides a snapshot of timing values.</param>
		protected override void Update(GameTime gameTime)
		{
			// For Mobile devices, this logic will close the Game when the Back button is pressed
			// Exit() is obsolete on iOS
#if !__IOS__ && !__TVOS__
			if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || Keyboard.GetState().IsKeyDown(Keys.Escape))
				Exit();
#endif

			keyState = Keyboard.GetState();

			if (Keyboard.GetState().IsKeyDown(Keys.A))
			{
				int breakhere = 20;
				breakhere--;
			}

			//menu is ORANGE
			if (CurrentScreen == screen.Menu) 
			{
				if (keyState.IsKeyDown(Keys.Space))
				{
					currentScreen = screen.Play;
				}
			}

			//win is RED
			if (CurrentScreen == screen.Win)
			{
				if (keyState.IsKeyDown(Keys.G))
				{
					currentScreen = screen.Menu;
				}
			}

			//lose is WHEAT
			if (CurrentScreen == screen.Lose)
			{
				if (keyState.IsKeyDown(Keys.G))
				{
					currentScreen = screen.Menu;
				}
			}

			//play is BLUE
			if (CurrentScreen == screen.Play) 
			{
				if (IsQListening && keyState.IsKeyDown(Keys.Q))
				{
					IsQListening = false;
					waves.Add(map.TopLWaveGen.MakeWave());
				}
				else if (!keyState.IsKeyDown(Keys.Q)){
					IsQListening = true;
				}
				if (IsZListening && keyState.IsKeyDown(Keys.Z))
				{
					IsZListening = false;
					waves.Add(map.BotLWaveGen.MakeWave());
					SoundFetcher.Bottle.Play();
				}
				else if (!keyState.IsKeyDown(Keys.Z)){
					IsZListening = true;
				}
				if (IsPListening && keyState.IsKeyDown(Keys.P))
				{
					IsPListening = false;
					waves.Add(map.TopRWaveGen.MakeWave());
					SoundFetcher.Bottle.Play();
				}
				else if (!keyState.IsKeyDown(Keys.P)){
					IsPListening = true;
				}
				if (IsMListening && keyState.IsKeyDown(Keys.M))
				{
					IsMListening = false;
					waves.Add(map.BotRWaveGen.MakeWave());
					SoundFetcher.Pebbles.Play();

				}
				else if (!keyState.IsKeyDown(Keys.M)){
					IsMListening = true;
				}

				foreach (Wave wave in waves)
				{
					wave.update(gameTime);
					if (wave.dead)
					{
						deadWaves.Add(wave);
					}
					else {
						if (wave.isCollidingWith(map.Player))
						{
							map.Player.CalculateNewVel(wave);
						}
					}
				}
				foreach (Wave wave in deadWaves)
				{
					waves.Remove(wave);
				}
				deadWaves.Clear();

				map.Player.AttemptedMotion = new Vector2(map.Player.Velocity.X, map.Player.Velocity.Y);
				if (map.Player.Velocity.Length() > 0)
				{
					int br = 0;
					br++;
				}
				while (map.Player.MoveAStep(map.ColliderWalls, new List<CircleCollidable>())) { }
				if (map.Player.isCollidingWith(map.Goal))
				{
					CurrentScreen = screen.Win;
				}
			}

			base.Update(gameTime);
		}

		/// <summary>
		/// This is called when the game should draw itself.
		/// </summary>
		/// <param name="gameTime">Provides a snapshot of timing values.</param>
		protected override void Draw(GameTime gameTime)
		{
			graphics.GraphicsDevice.Clear(Color.Orange);

			if (CurrentScreen == screen.Menu)
			{
				spriteBatch.Begin();
				graphics.GraphicsDevice.Clear(Color.Orange);
				//this is shitty programmer art
				spriteBatch.Draw(ArtFetcher.pMenuScreen, new Vector2(200, 100), Color.White);
				spriteBatch.End();
			}
			else if (CurrentScreen == screen.Play)
			{
				//private RenderTarget2D wholescene;
				//private RenderTarget2D waveEffect;
				GraphicsDevice.SetRenderTarget(wholescene);
				spriteBatch.Begin();
				graphics.GraphicsDevice.Clear(Color.Blue);
				for (int i = 0; i < 1024; i += 64)
				{
					for (int j = 0; j < 768; j += 64)
					{
						spriteBatch.Draw(ArtFetcher.BG, new Vector2((float)i, (float)j));
					}
				}
				for (int i = 0; i < map.width/TILE_SIZE; i++)
				{
					for (int j = 0; j < map.height/TILE_SIZE; j++)
					{
						spriteBatch.Draw(ArtFetcher.Floor, Map.MAP_START + new Vector2((float)(i * TILE_SIZE), (float)(j * TILE_SIZE)));
					}
				}


				//this is quality art
				map.Goal.Draw(new Rectangle(), spriteBatch, gameTime);
				map.Player.Draw(new Rectangle(), spriteBatch, gameTime);
				foreach (TileWall wall in map.TileWalls)
				{
					wall.Draw(spriteBatch);
				}
				foreach (ColliderWall wall in map.ColliderWalls)
				{
					DrawLine(spriteBatch, wall.A, wall.B, Color.Black);
				}
				map.TopLWaveGen.Draw(spriteBatch);
				map.TopRWaveGen.Draw(spriteBatch);
				map.BotLWaveGen.Draw(spriteBatch);
				map.BotRWaveGen.Draw(spriteBatch);
				spriteBatch.Draw(ArtFetcher.Overlay, Vector2.Zero, null, null, null, 0, null, Color.White * 0.33f);
				spriteBatch.Draw(ArtFetcher.Vignette, Vector2.Zero, null, null, null, 0, null, Color.White * (float)((Math.Abs((gameTime.TotalGameTime.TotalMilliseconds % 6000)-3000)/3750.0f)+0.1f));
				spriteBatch.End();

				GraphicsDevice.SetRenderTarget(waveEffect);
				graphics.GraphicsDevice.Clear(Color.Gray);
				spriteBatch.Begin();
				foreach(Wave wave in waves)
				{
					Texture2D curWave = wave.makeMap(GraphicsDevice);
					spriteBatch.Draw(curWave, wave.Center, null, null, new Vector2(curWave.Width / 2));
				}
				spriteBatch.End();

				GraphicsDevice.SetRenderTarget(null);
				if (Keyboard.GetState().IsKeyDown(Keys.NumPad1))
				{
					spriteBatch.Begin();
					spriteBatch.Draw(waveEffect, new Vector2(0, 0));
					spriteBatch.End();
				}
				else if (Keyboard.GetState().IsKeyDown(Keys.NumPad2))
				{
					spriteBatch.Begin();
					spriteBatch.Draw(wholescene, new Vector2(0, 0));
					spriteBatch.End();
				}
				else {
					m_Effect.Parameters["InputTexture"].SetValue(waveEffect);
					m_Effect.Parameters["LastTexture"].SetValue(wholescene);
					spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, SamplerState.PointClamp, DepthStencilState.Default, RasterizerState.CullNone, m_Effect);
					spriteBatch.Draw(waveEffect, new Vector2(0, 0));
					spriteBatch.End();
				}			}
			else if (CurrentScreen == screen.Win)
			{
				spriteBatch.Begin();
				graphics.GraphicsDevice.Clear(Color.Red);
				//this is shitty programmer art
				spriteBatch.Draw(ArtFetcher.pReturnScreen, new Vector2(200, 100), Color.White);
				spriteBatch.End();

			}
			else if (CurrentScreen == screen.Lose)
			{
				spriteBatch.Begin();
				graphics.GraphicsDevice.Clear(Color.Wheat);
				//this is shitty programmer art
				spriteBatch.Draw(ArtFetcher.pReturnScreen, new Vector2(200, 100), Color.White);
				spriteBatch.End();
			}

			base.Draw(gameTime);
		}

		public static Vector2 CenterToUpperLeft(Vector2 Center)
		{
			return new Vector2 (Center.X - TILE_SIZE/2, Center.Y - TILE_SIZE/2);
		}

		public static void DrawLine(SpriteBatch sb, Vector2 start, Vector2 end, Color color)
		{
			Vector2 edge = end - start;
			// calculate angle to rotate line
			float angle = (float)Math.Atan2(edge.Y, edge.X);

			sb.Draw(ArtFetcher.Dot,
				new Rectangle(// rectangle defines shape of line and position of start of line
					(int)start.X,
					(int)start.Y,
					(int)edge.Length(), //sb will strech the texture to fill this rectangle
					1), //width of line, change this to make thicker line
				null,
				color, //colour of line
				angle,     //angle of line (calulated above)
				new Vector2(0, 0), // point in line about which to rotate
				SpriteEffects.None,
				0);
		}
	}
}
