#region File Description
//-----------------------------------------------------------------------------
// GameplayScreen.cs
//
// Microsoft XNA Community Game Platform
// Copyright (C) Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
#endregion

#region Using Statements
using System;
using System.Threading;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
#endregion

namespace GhostArena3D
{
   class GameplayScreen : GameScreen
   {
      #region Fields

      ContentManager content = null;
      SpriteFont gameFont;

      // Dealing with the game and world.
      Model gameWorld = null;
      Matrix[] gameWorldTransforms;
      BoundingBox gameWorldCollisionBox;
      
      int gameScore = 0;
      int gameLevel = 0;
      float gameTimeRemaining = 0;

      // Players and cameras
      Player player1;
      Camera player1Cam;
      float camMoveSpeed = 0.0f,
            camTurnSpeed = 0.0f;

      // Dealing with the heads-up display.
      Rectangle hudRect;
      Texture2D hudBorder = null;
      FrameRateCounter hudFrameRate = null;
      Vector2 fpsHudPos;
      Vector2 scoreHudPos;
      Vector2 levelHudPos;
      Vector2 timerHudPos;

      Random random = new Random();

      #endregion

      #region Initialization


      public GameplayScreen()
      {
         TransitionOnTime = TimeSpan.FromSeconds(1.0);
         TransitionOffTime = TimeSpan.FromSeconds(0.5);
      }


      public override void LoadContent()
      {
         if(content == null)
            content = new ContentManager(ScreenManager.Game.Services, "Content");

         // Initialize game variables.
         gameScore = 0;
         gameLevel = 1;
         gameTimeRemaining = 180.0f;


         // Pre-calculate the HUD element positions.
         int w = ScreenManager.GraphicsDevice.Viewport.Width;
         int h = ScreenManager.GraphicsDevice.Viewport.Height;

         hudRect = new Rectangle(0, 0, w, (int)(h * 0.10f));
         fpsHudPos = new Vector2((int)(w * 0.05f), (int)(h * 0.01f));
         scoreHudPos = new Vector2(w - (int)(w * 0.2f), (int)(h * 0.04f));
         levelHudPos = new Vector2((int)(w * 0.05f), (int)(h * 0.04f));
         timerHudPos = new Vector2((int)(w * 0.4f), (int)(h * 0.04f));

         // Framerate code from the 2D game.
         hudFrameRate = new FrameRateCounter(ScreenManager.Game, fpsHudPos);
         ScreenManager.Game.Components.Add(hudFrameRate);


         // Game content to load.
         hudBorder = content.Load<Texture2D>("Images/black");
         gameFont = content.Load<SpriteFont>("Fonts/data");
         gameWorld = content.Load<Model>("Levels/Level1/Level1");
         gameWorldTransforms = new Matrix[gameWorld.Bones.Count];

         // Calculate the bounding box of the game world for
         // collision detection against the walls.  We can
         // hardcode it since we know the size of the level.
         Vector3 min = new Vector3(-600, 0, -600);
         Vector3 max = new Vector3(600, 216, 600);
         gameWorldCollisionBox = new BoundingBox(min, max);


         // Set an initial starting position for the camera.
         // Create the player and the camera.
         player1 = new Player();
         player1.Health = 100;

         player1Cam = new Camera(new Vector3(0, 75, 0),
                                 new Vector3(0, 75, -1),
                                 Vector3.UnitY);

         
         // Set up input action mapping.
         IInputSystem input = ((IInputSystem)ScreenManager.Game.Services.GetService(typeof(IInputSystem)));

         input.SetActionFunction("Move", Move);
         input.SetActionFunction("Move Foward", MoveForward);
         input.SetActionFunction("Move Backward", MoveBackward);
         input.SetActionFunction("Move Left", MoveLeft);
         input.SetActionFunction("Move Right", MoveRight);
         input.SetActionFunction("Fire", Fire);
         input.SetActionFunction("Rotate", Rotate);
         input.Enable();

         ScreenManager.Game.ResetElapsedTime();
      }


      public override void UnloadContent()
      {
         ScreenManager.Game.Components.Remove(hudFrameRate);

         content.Unload();
      }


      #endregion

      #region Update and Draw


      public override void Update(GameTime gameTime, bool otherScreenHasFocus,
                                  bool coveredByOtherScreen)
      {
         base.Update(gameTime, otherScreenHasFocus, coveredByOtherScreen);

         hudFrameRate.Update(gameTime);

         float time = (float)gameTime.ElapsedGameTime.TotalSeconds;
         camMoveSpeed = 75.0f * time;
         camTurnSpeed = 2.0f * time;

         if(IsActive)
         {
            gameTimeRemaining -= (float)gameTime.ElapsedGameTime.TotalSeconds;

            if(gameTimeRemaining <= 0)
            {
               gameTimeRemaining = 0;

               // Next Level.
            }
         }
      }


      public override void Draw(GameTime gameTime)
      {
         // Must reset these states or else the render will not be
         // correct.  The sprite rendering is at fault in the
         // Game State Management sample.
         ScreenManager.GraphicsDevice.RenderState.DepthBufferEnable = true;
         ScreenManager.GraphicsDevice.RenderState.AlphaBlendEnable = false;
         ScreenManager.GraphicsDevice.RenderState.AlphaTestEnable = false;
         ScreenManager.GraphicsDevice.SamplerStates[0].AddressU = TextureAddressMode.Wrap;
         ScreenManager.GraphicsDevice.SamplerStates[0].AddressV = TextureAddressMode.Wrap;
         ScreenManager.GraphicsDevice.SamplerStates[0].AddressW = TextureAddressMode.Wrap;
         ScreenManager.GraphicsDevice.SamplerStates[0].MagFilter = TextureFilter.Anisotropic;
         ScreenManager.GraphicsDevice.SamplerStates[0].MinFilter = TextureFilter.Anisotropic;
         ScreenManager.GraphicsDevice.SamplerStates[0].MipFilter = TextureFilter.Linear;

         gameWorld.CopyAbsoluteBoneTransformsTo(gameWorldTransforms);

         // Draw the game world based on the player's camera.
         foreach(ModelMesh mesh in gameWorld.Meshes)
         {
            foreach(BasicEffect effect in mesh.Effects)
            {
               effect.World = gameWorldTransforms[mesh.ParentBone.Index];
               effect.View = player1Cam.LookAtMatrix;
               effect.Projection = player1Cam.ProjectionMatrix;

               mesh.Draw();
            }
         }

         // Draw the heads-up display on top of the scene.
         DrawHUD();

         // If the game is transitioning on or off, fade it out to black.
         if(TransitionPosition > 0)
            ScreenManager.FadeBackBufferToBlack(255 - TransitionAlpha);
      }
      
      
      public void DrawHUD()
      {
         // Note: The FPS is a game component and will render automatically.
      
         ScreenManager.SpriteBatch.Begin(SpriteBlendMode.AlphaBlend,
                                         SpriteSortMode.Immediate,
                                         SaveStateMode.SaveState);
         
         int w = ScreenManager.GraphicsDevice.Viewport.Width;
         int h = ScreenManager.GraphicsDevice.Viewport.Height;
         Rectangle rect = new Rectangle(0, 0, w, (int)(h * 0.10f));

         string displayString;

         ScreenManager.SpriteBatch.Draw(hudBorder, rect, Color.Black);

         displayString = "score: " + gameScore;
         gameFont.MeasureString(displayString);
         ScreenManager.SpriteBatch.DrawString(gameFont, displayString,
                                              scoreHudPos, Color.White);

         displayString = "Level: " + gameLevel;
         ScreenManager.SpriteBatch.DrawString(gameFont, displayString,
                                              levelHudPos, Color.White);
         
         displayString = gameTimeRemaining >= 60 ? ((int)(gameTimeRemaining / 60)).ToString() : "";
         ScreenManager.SpriteBatch.DrawString(gameFont, "Time Remaining: " + displayString +
                                              ":" + ((int)(gameTimeRemaining % 60)).ToString("00"),
                                              timerHudPos, Color.White);
         
         ScreenManager.SpriteBatch.End();
      }

      #endregion
      
      #region Input and Movement
      

      public override void HandleInput(InputState input)
      {
         if(input == null)
            throw new ArgumentNullException("input");

         if(input.PauseGame)
         {
            // If they pressed pause, bring up the pause menu screen.
            ScreenManager.AddScreen(new PauseMenuScreen());
         }
      }
      
      
      private void Move(object value, GameTime gameTime)
      {
         // Move using the left joystick.

         if(IsActive == false)
            return;
         
         Vector2 delta = (Vector2)value;
         Vector3 oldPosition = player1Cam.Position;
         Vector3 oldLookAtPosition = player1Cam.LookAt;
         
         // Move left if negative else right if positive.
         if(delta.X < 0.0f)
            player1Cam.MoveLeftXZ(delta.X * -1 * camMoveSpeed);
         else if(delta.X > 0.0f)
            player1Cam.MoveRightXZ(delta.X * camMoveSpeed);

         // Move backward if negative else forward if positive.
         if(delta.Y < 0.0f)
            player1Cam.MoveBackwardXZ(delta.Y * -1 * camMoveSpeed);
         else if(delta.Y > 0.0f)
            player1Cam.MoveForwardXZ(delta.Y * camMoveSpeed);

         player1.SetCollisionCenter(player1Cam.Position);

         if(!gameWorldCollisionBox.Intersects(player1.CollisionSphere))
         {
            // On collision reset to last valid position.
            player1Cam.Position = oldPosition;
            player1Cam.LookAt = oldLookAtPosition;
         }
            
      }

      private void MoveForward(object value, GameTime gameTime)
      {
         if(IsActive == false)
            return;

         if(value != null)
         {
            if((ButtonState)value == ButtonState.Pressed)
            {
               Vector3 oldPosition = player1Cam.Position;
               Vector3 oldLookAtPosition = player1Cam.LookAt;

               player1Cam.MoveForwardXZ(camMoveSpeed);
               
               player1.SetCollisionCenter(player1Cam.Position);

               if(!gameWorldCollisionBox.Intersects(player1.CollisionSphere))
               {
                  // On collision reset to last valid position.
                  player1Cam.Position = oldPosition;
                  player1Cam.LookAt = oldLookAtPosition;
               }
            }
         }
      }

      private void MoveBackward(object value, GameTime gameTime)
      {
         if(IsActive == false)
            return;

         if(value != null)
         {
            if((ButtonState)value == ButtonState.Pressed)
            {
               Vector3 oldPosition = player1Cam.Position;
               Vector3 oldLookAtPosition = player1Cam.LookAt;

               player1Cam.MoveBackwardXZ(camMoveSpeed);
               
               if(!gameWorldCollisionBox.Intersects(player1.CollisionSphere))
               {
                  // On collision reset to last valid position.
                  player1Cam.Position = oldPosition;
                  player1Cam.LookAt = oldLookAtPosition;
               }
            }
         }
      }

      private void MoveLeft(object value, GameTime gameTime)
      {
         if(IsActive == false)
            return;

         if(value != null)
         {
            if((ButtonState)value == ButtonState.Pressed)
            {
               Vector3 oldPosition = player1Cam.Position;
               Vector3 oldLookAtPosition = player1Cam.LookAt;

               player1Cam.MoveLeftXZ(camMoveSpeed);
               
               if(!gameWorldCollisionBox.Intersects(player1.CollisionSphere))
               {
                  // On collision reset to last valid position.
                  player1Cam.Position = oldPosition;
                  player1Cam.LookAt = oldLookAtPosition;
               }
            }
         }
      }

      private void MoveRight(object value, GameTime gameTime)
      {
         if(IsActive == false)
            return;

         if(value != null)
         {
            if((ButtonState)value == ButtonState.Pressed)
            {
               Vector3 oldPosition = player1Cam.Position;
               Vector3 oldLookAtPosition = player1Cam.LookAt;

               player1Cam.MoveRightXZ(camMoveSpeed);
               
               if(!gameWorldCollisionBox.Intersects(player1.CollisionSphere))
               {
                  // On collision reset to last valid position.
                  player1Cam.Position = oldPosition;
                  player1Cam.LookAt = oldLookAtPosition;
               }
            }
         }
      }

      private void Rotate(object value, GameTime gameTime)
      {
         if(IsActive == false)
            return;

         if(value != null)
         {
            Vector2 delta = (Vector2)value;
            delta *= camTurnSpeed;

            player1Cam.RotateCamera(delta);
         }
      }


      private void Fire(object value, GameTime gameTime)
      {
         if(IsActive == false)
            return;

         // Nothing to shoot yet.
      }
      
      #endregion
   }
}
