#region Using Statements
using System;
using System.Threading;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.GamerServices;
#endregion

namespace GhostArena3D
{
   class GameplayScreen : GameScreen
   {
      #region Fields

      #region General

      ContentManager content = null;
      SpriteFont gameFont;
      
      Random random = new Random();
      
      #endregion
      
      #region Networking Related
      
      NetworkSession networkSession;
      PacketReader packetReader;
      PacketWriter packetWriter;
      const float sendPacketAfter = 0.12f;
      float timeBetweenNetworkSends;
      
      #endregion

      #region Game world
      
      Model gameWorld = null;
      Matrix[] gameWorldTransforms;
      BoundingBox gameWorldCollisionBox;
      
      #endregion
      
      #region General Gameplay
      
      HighScoreList highScoreList;
      
      GameType gameType;

      bool gameOver;      
      int gameDifficultyLevel;
      int gameLevel;

      float gameTimeRemaining;
      const float sessionTime = 180.0f;

      float gameShotTime;
      const float timeBetweenShots = 0.2f;

      float gameRespawnTime;
      float timeBetweenRespawn;
      List<Spawn> spawnLocations = null;
      
      #endregion
      
      #region Game Audio
      
      Cue musicCue = null;
      
      #endregion
      
      #region Enemies
      
      float ghostSpeed;
      float ghostSpeedDelta;
      int ghostDamage;
      int pointsPerGhost;
      
      Model ghostModel = null;
      Matrix[] ghostWorldTransforms;
      
      List<Ghost> ghostEnemies = null;
      
      #endregion

      #region Players and Cameras

      bool localPlayerFired, localPlayerFireForNetwork;
      Player localPlayer = null;
      Player networkPlayer = null;
      Camera gameCam;
      float camMoveSpeed, camTurnSpeed;

      #endregion

      #region Dealing with the heads-up display.

      Rectangle hudRect;
      Rectangle crossHairHudRect;

      Texture2D hudBorder = null;
      Texture2D crossHairDecal = null;

      FrameRateCounter hudFrameRate = null;

      Vector2 fpsHudPos;
      Vector2 scoreP1HudPos;
      Vector2 scoreP2HudPos;
      Vector2 levelHudPos;
      Vector2 timerHudPos;
      Vector2 healthHudPos;
      Vector2 p1GamerPicHudPos;
      Vector2 p2GamerPicHudPos;
      
      #endregion

      #endregion

      #region Initialization


      public GameplayScreen(int difficultyLevel, GameType type)
      {
         TransitionOnTime = TimeSpan.FromSeconds(1.0);
         TransitionOffTime = TimeSpan.FromSeconds(0.5);
         
         gameDifficultyLevel = difficultyLevel;
         gameType = type;
         
         if(gameType != GameType.SINGLE_PLAYER)
         {
            packetReader = new PacketReader();
            packetWriter = new PacketWriter();
         }
         
         timeBetweenNetworkSends = sendPacketAfter;
         
         if(gameType == GameType.MULTI_PLAYER_CLIENT)
            gameDifficultyLevel = -1;
      }


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

         if(gameType != GameType.SINGLE_PLAYER)
            networkSession = ((GhostArena3D)ScreenManager.Game).NetworkSession;

         // Load the previous high scores if any exist.
         highScoreList = new HighScoreList();
         highScoreList.Load();

         // Initialize game variables.
         InitializeGamePlayVariables();

         // Pre-calculate the HUD element positions.
         InitializeHUD();

         // Game content to load.
         InitializeGameWorld();

         // Create the players and enemies.
         InitializeGameActors();
         
         // 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();

         musicCue = ((GhostArena3D)ScreenManager.Game).SoundBank.GetCue("title");
         
         if(musicCue.IsPlaying == false)
            musicCue.Play();

         ScreenManager.Game.ResetElapsedTime();
      }
      
      
      public void InitializeGamePlayVariables()
      {
         if(gameDifficultyLevel != -1)
         {
            ((GhostArena3D)ScreenManager.Game).SetDifficulty(gameDifficultyLevel);

            ghostSpeed = ((GhostArena3D)ScreenManager.Game).Difficulty.GhostSpeed;
            ghostDamage = ((GhostArena3D)ScreenManager.Game).Difficulty.HealthDrain;
            pointsPerGhost = ((GhostArena3D)ScreenManager.Game).Difficulty.GhostScore;
            timeBetweenRespawn = ((GhostArena3D)ScreenManager.Game).Difficulty.GhostSpawnTime;
         }

         ghostSpeedDelta = 15.0f;

         gameOver = false;
         gameLevel = 1;
         gameTimeRemaining = sessionTime;
         gameRespawnTime = timeBetweenRespawn;
         gameShotTime = 0.0f;
         
         spawnLocations = content.Load<List<Spawn> >("Levels/Level1/Level1Spawns");
      }
      
      
      public void InitializeHUD()
      {
         hudBorder = content.Load<Texture2D>("Images/black");
         crossHairDecal = content.Load<Texture2D>("Images/crosshair");

         int w = ScreenManager.GraphicsDevice.Viewport.Width;
         int h = ScreenManager.GraphicsDevice.Viewport.Height;

         float percent = 1.0f;
         Vector2 safeAreaStart = Vector2.Zero;
         Vector2 safeAreaEnd = new Vector2(w, h);
         
#if XBOX
         percent = 0.9f;
#endif

         int w_adjust = (int)(w * ((1.0f - percent) / 2));
         int h_adjust = (int)(h * ((1.0f - percent) / 2));
         
         safeAreaStart.X += w_adjust;
         safeAreaStart.Y += h_adjust;
         safeAreaEnd.X = (safeAreaEnd.X - w_adjust) - safeAreaStart.X;
         safeAreaEnd.Y = (safeAreaEnd.Y - h_adjust) - safeAreaStart.Y;

         hudRect = new Rectangle((int)safeAreaStart.X,
                                 (int)safeAreaStart.Y,
                                 (int)safeAreaEnd.X,
                                 (int)(safeAreaEnd.Y * 0.10f));

         fpsHudPos = safeAreaStart;
         fpsHudPos.X += safeAreaEnd.X * 0.05f;
         fpsHudPos.Y += safeAreaEnd.Y * 0.01f;

         levelHudPos = safeAreaStart;
         levelHudPos.X += safeAreaEnd.X * 0.05f;
         levelHudPos.Y += safeAreaEnd.Y * 0.04f;

         timerHudPos = safeAreaStart;
         timerHudPos.X += safeAreaEnd.X * 0.85f;
         timerHudPos.Y += safeAreaEnd.Y * 0.04f;

         healthHudPos = safeAreaStart;
         healthHudPos.X += safeAreaEnd.X * 0.85f;
         healthHudPos.Y += safeAreaEnd.Y * 0.01f;

         scoreP1HudPos = safeAreaStart;
         scoreP1HudPos.X += safeAreaEnd.X * 0.30f;
         scoreP1HudPos.Y += safeAreaEnd.Y * 0.04f;
         
         p1GamerPicHudPos = safeAreaStart;
         p1GamerPicHudPos.X += safeAreaEnd.X * 0.20f;
         p1GamerPicHudPos.Y += safeAreaEnd.Y * 0.01f;
         
         scoreP2HudPos = safeAreaStart;
         scoreP2HudPos.X += safeAreaEnd.X * 0.70f;
         scoreP2HudPos.Y += safeAreaEnd.Y * 0.04f;
        
         p2GamerPicHudPos = safeAreaStart;
         p2GamerPicHudPos.X += safeAreaEnd.X * 0.60f;
         p2GamerPicHudPos.Y += safeAreaEnd.Y * 0.01f;

         crossHairHudRect = new Rectangle((int)((w / 2) - crossHairDecal.Width / 2),
                                          (int)((h / 2) - crossHairDecal.Height / 2),
                                          crossHairDecal.Width,
                                          crossHairDecal.Height);

         hudFrameRate = new FrameRateCounter(ScreenManager.Game, fpsHudPos);
         ScreenManager.Game.Components.Add(hudFrameRate);
      }
      
      
      public void InitializeGameWorld()
      {
         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.
         Vector3 min = new Vector3(-600, 0, -600);
         Vector3 max = new Vector3(600, 216, 600);
         gameWorldCollisionBox = new BoundingBox(min, max);
      }
      
      
      public void InitializeGameActors()
      {
         Vector3 p1Pos = new Vector3(200, 75, 0);
         Vector3 p2Pos = new Vector3(-200, 75, 0);

         localPlayerFired = false;

         localPlayer = ((GhostArena3D)ScreenManager.Game).GetSignedInPlayer(PlayerIndex.One);
         localPlayer.Score = 0;
         localPlayer.Health = 100;

         if(gameType != GameType.MULTI_PLAYER_CLIENT)
         {
            localPlayer.SetCollisionCenter(p1Pos);
            gameCam = new Camera(p1Pos, p1Pos + Vector3.UnitZ, Vector3.UnitY);
         }
         else
         {
            localPlayer.SetCollisionCenter(p2Pos);
            gameCam = new Camera(p2Pos, p2Pos + Vector3.UnitZ, Vector3.UnitY);
         }
         
         if(gameType != GameType.SINGLE_PLAYER)
         {
            networkPlayer = ((GhostArena3D)ScreenManager.Game).GetFirstNetworkPlayer();
            networkPlayer.Score = 0;
            networkPlayer.Health = 100;
            
            if(gameType != GameType.MULTI_PLAYER_CLIENT)
               networkPlayer.SetCollisionCenter(p2Pos);
            else
               networkPlayer.SetCollisionCenter(p1Pos);
         }
         
         ghostModel = content.Load<Model>("Models/ghost");
         ghostWorldTransforms = new Matrix[ghostModel.Bones.Count];
         float radius = ghostModel.Meshes[0].BoundingSphere.Radius;
         
         ghostEnemies = new List<Ghost>();
         ghostEnemies.Add(new Ghost(Vector3.Zero, -Vector3.UnitZ, radius));
         ghostEnemies.Add(new Ghost(Vector3.Zero, -Vector3.UnitZ, radius));
         ghostEnemies.Add(new Ghost(Vector3.Zero, -Vector3.UnitZ, radius));
         ghostEnemies.Add(new Ghost(Vector3.Zero, -Vector3.UnitZ, radius));
      }


      public override void UnloadContent()
      {
         if(musicCue != null)
            musicCue.Stop(AudioStopOptions.Immediate);

         ScreenManager.Game.Components.Remove(hudFrameRate);
         
         content.Unload();
      }


      #endregion

      #region Gameplay Specific
      
      public void OnLevelChange()
      {
         gameTimeRemaining = sessionTime;
         gameLevel++;
         
         ghostSpeed += ghostSpeedDelta;
      }
      
      
      public void OnGameOver()
      {
         String message;

         if(gameType == GameType.SINGLE_PLAYER)
         {
            message = "Game Over.  Final score: " + localPlayer.Score;
         }
         else
         {
            if(localPlayer.Score > networkPlayer.Score)
            {
               message = "You Win! " + localPlayer.Score;
               message += " to " + networkPlayer.Score;
            }
            else if(localPlayer.Score < networkPlayer.Score)
            {
               message = "You Lose " + networkPlayer.Score;
               message += " to " + localPlayer.Score;
            }
            else
            {
               message = "The Game is a Tie!";
            }
         }

         MessageBoxScreen messageBox = new MessageBoxScreen(message);
         messageBox.Accepted += GameOverMessageBoxAccepted;

         ScreenManager.AddScreen(messageBox);
         
         if(networkSession != null)
            if(networkSession.IsHost == true)
               networkSession.EndGame();
      }
      
      
      void GameOverMessageBoxAccepted(object sender, EventArgs e)
      {
         highScoreList.AddScore(new HighScore(DateTime.Now.ToShortDateString(),
                                gameLevel, localPlayer.Score));

         highScoreList.Save();

         if(gameType == GameType.SINGLE_PLAYER)
         {
            LoadingScreen.Load(ScreenManager, true, new BackgroundScreen(),
                               new MainMenuScreen());
         }
         else
         {
            LoadingScreen.Load(ScreenManager, true,
                               new BackgroundScreen(),
                               new MainMenuScreen(),
                               new GameLobbyScreen(gameDifficultyLevel, gameType, true));
         }
      }
      
      
      public Vector3 GetRandomSpawnLocation()
      {
         int spawnLocationIndex = random.Next(0, spawnLocations.Count - 1);
         
         return spawnLocations[spawnLocationIndex].SpawnPosition;
      }
      
      #endregion

      #region Update and Draw


      private void UpdateNetworkAsClient(bool send)
      {
         foreach(LocalNetworkGamer gamer in networkSession.LocalGamers)
         {
            // Receive from the host...

            while(gamer.IsDataAvailable)
            {
               NetworkGamer sender;
               gamer.ReceiveData(packetReader, out sender);

               if(sender.IsLocal == false)
               {
                  gameLevel = packetReader.ReadInt32();
                  gameTimeRemaining = packetReader.ReadSingle();
                  bool hostFire = packetReader.ReadBoolean();
                  networkPlayer.Health = packetReader.ReadInt32();
                  networkPlayer.Score = packetReader.ReadInt32();
                  localPlayer.Health = packetReader.ReadInt32();
                  localPlayer.Score = packetReader.ReadInt32();

                  foreach(Ghost ghost in ghostEnemies)
                  {
                     ghost.Alive = packetReader.ReadBoolean();
                     ghost.Position = packetReader.ReadVector3();
                     ghost.Target = packetReader.ReadVector3();
                     ghost.UpdateFinalMatrix();
                  }
                  
                  if(hostFire == true)
                     ((GhostArena3D)ScreenManager.Game).SoundBank.PlayCue("zap");
               }
            }

            // Send to the host...

            if(send == true)
            {
               Vector3 yourPos = gameCam.Position;
               Vector3 yourDir = gameCam.LookAt - yourPos;
               yourDir.Normalize();

               packetWriter.Write(localPlayerFireForNetwork);
               packetWriter.Write(yourPos);
               packetWriter.Write(yourDir);
               
               gamer.SendData(packetWriter, SendDataOptions.None);
            }
         }
         
         if(send == true)
            localPlayerFireForNetwork = false;
      }
      
      
      private void UpdateNetworkAsHost(bool send)
      {
         foreach(LocalNetworkGamer gamer in networkSession.LocalGamers)
         {
            // Receive from this network gamer as host...

            while(gamer.IsDataAvailable)
            {
               NetworkGamer sender;
               gamer.ReceiveData(packetReader, out sender);

               if(sender.IsLocal == false)
               {
                  bool didShoot = packetReader.ReadBoolean();
                  Vector3 pos = packetReader.ReadVector3();
                  Vector3 dir = packetReader.ReadVector3();
                  
                  networkPlayer.SetCollisionCenter(pos);
                  
                  if(didShoot == true)
                  {
                     ((GhostArena3D)ScreenManager.Game).SoundBank.PlayCue("zap");
                     ShootAtGhost(networkPlayer, new Ray(pos, dir));
                  }
               }
            }
            
            // Send to this network gamer as host...
            
            if(send == true)
            {            
               packetWriter.Write(gameLevel);
               packetWriter.Write(gameTimeRemaining);
               packetWriter.Write(localPlayerFireForNetwork);
               packetWriter.Write(localPlayer.Health);
               packetWriter.Write(localPlayer.Score);
               packetWriter.Write(networkPlayer.Health);
               packetWriter.Write(networkPlayer.Score);

               foreach(Ghost ghost in ghostEnemies)
               {
                  packetWriter.Write(ghost.Alive);
                  packetWriter.Write(ghost.Position);
                  packetWriter.Write(ghost.Target);
               }

               gamer.SendData(packetWriter, SendDataOptions.None);
            }
         }
         
         if(send == true)
            localPlayerFireForNetwork = false;
      }


      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 = 3.0f * time;

         timeBetweenNetworkSends -= time;
         gameShotTime -= time;
         
         bool sendNetworkData = false;
         
         if(timeBetweenNetworkSends <= 0)
         {
            timeBetweenNetworkSends = sendPacketAfter;
            sendNetworkData = true;
         }

         if(gameType != GameType.SINGLE_PLAYER)
            if(networkSession.SessionState != NetworkSessionState.Playing)
               gameOver = true;

         if(gameType == GameType.MULTI_PLAYER_CLIENT)
         {
            UpdateNetworkAsClient(sendNetworkData);
         }
         else
         {
            if(gameType != GameType.SINGLE_PLAYER)
               UpdateNetworkAsHost(sendNetworkData);

            bool update = true;

            // Allows us to pause the game in single player
            // or to pause if the window is minimized but
            // there is no pausing in multiplayer.
            if(gameType == GameType.SINGLE_PLAYER)
               if(IsActive == false)
                  update = false;

            if(update == true && gameOver == false)
            {
               gameTimeRemaining -= time;
               gameRespawnTime -= time;
            
               // Handle running out of time.
               if(gameTimeRemaining <= 0)
                  OnLevelChange();

               if(localPlayer.Health <= 0)
                  gameOver = true;

               if(gameType != GameType.SINGLE_PLAYER)
               {
                  if(networkPlayer.Health <= 0)
                     gameOver = true;
               }
               
               // Test if we attempted to shoot a ghost.
               if(localPlayerFired == true)
               {
                  Ray shotRay = new Ray();
                  
                  shotRay.Position = gameCam.Position;
                  shotRay.Direction = Vector3.Normalize(gameCam.LookAt -
                                                        gameCam.Position);

                  ShootAtGhost(localPlayer, shotRay);
                  
                  localPlayerFired = false;
               }

               // Only update this if single player or host.            
               if(gameOver == false)
               {
                  UpdateGhosts(time);
                  PlayerGhostCollision();
               }
            }
         }
         
         if(IsActive == true)
         {
            if(gameOver)
            {
               ((GhostArena3D)ScreenManager.Game).SoundBank.PlayCue("dying");
               OnGameOver();
            }
         }
      }
      
      
      public void ShootAtGhost(Player player, Ray ray)
      {
         foreach(Ghost ghost in ghostEnemies)
         {
            if(ghost.Alive == true)
            {
               if(ray.Intersects(ghost.CollisionSphere) != null)
               {
                  player.Score += pointsPerGhost;               
                  ghost.Alive = false;
               }
            }
         }
      }
      
      
      public void UpdateGhosts(float time)
      {
         // Handle enemy respawn when its time.
         if(gameRespawnTime <= 0)
         {
            gameRespawnTime = timeBetweenRespawn;

            foreach(Ghost ghost in ghostEnemies)
            {
               if(ghost.Alive == false)
               {
                  ghost.Alive = true;
                  ghost.Position = GetRandomSpawnLocation();
                  
                  ((GhostArena3D)ScreenManager.Game).SoundBank.PlayCue("wail");
                  break;
               }
            }
         }

         // Handle moving the alive ghost towards the player.
         foreach(Ghost ghost in ghostEnemies)
         {
            if(ghost.Alive == true)
            {
               Vector3 posToMoveTo;
               
               if(gameType == GameType.SINGLE_PLAYER)
               {
                  posToMoveTo = gameCam.Position;
               }
               else
               {
                  float dist1, dist2;
                  
                  Vector3 networkPlayerPos = networkPlayer.CollisionSphere.Center;
                  
                  dist1 = Vector3.Distance(gameCam.Position, ghost.Position);
                  dist2 = Vector3.Distance(networkPlayerPos, ghost.Position);
                  
                  if(dist1 < dist2)
                     posToMoveTo = gameCam.Position;
                  else
                     posToMoveTo = networkPlayerPos;
               }
               
               Vector3 ghostToPlayer = posToMoveTo - ghost.Position;
               ghostToPlayer.Normalize();
               
               ghost.Move(ghostToPlayer, ghostSpeed * time);
               ghost.Target = posToMoveTo;
               ghost.UpdateFinalMatrix();
            }
         }
      }


      public void PlayerGhostCollision()
      {
         // Check for collision with ghost.

         foreach(Ghost ghost in ghostEnemies)
         {
            if(ghost.Alive == true)
            {
               if(ghost.CollisionSphere.Intersects(localPlayer.CollisionSphere))
               {
                  ghost.Alive = false;
                  
                  localPlayer.Health -= ghostDamage;
                  
                  if(localPlayer.Health < 0)
                     localPlayer.Health = 0;
               }
               
               if(gameType != GameType.SINGLE_PLAYER)
               {
                  if(ghost.CollisionSphere.Intersects(networkPlayer.CollisionSphere))
                  {
                     ghost.Alive = false;
                     
                     networkPlayer.Health -= ghostDamage;
                     
                     if(networkPlayer.Health < 0)
                        networkPlayer.Health = 0;
                  }
               }
            }
         }
      }


      public override void Draw(GameTime gameTime)
      {
         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 = gameCam.LookAtMatrix;
               effect.Projection = gameCam.ProjectionMatrix;

               mesh.Draw();
            }
         }

         // Draw ghosts.
         ghostModel.CopyAbsoluteBoneTransformsTo(ghostWorldTransforms);

         foreach(Ghost ghost in ghostEnemies)
         {
            if(ghost.Alive == true)
               DrawActor(ghostModel, ghostWorldTransforms, ghost.Transformation);
         }

         // 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 DrawActor(Model model, Matrix[] transforms, Matrix location)
      {
         foreach(ModelMesh mesh in model.Meshes)
         {
            foreach(BasicEffect effect in mesh.Effects)
            {
               effect.World = transforms[mesh.ParentBone.Index] * location;
               effect.View = gameCam.LookAtMatrix;
               effect.Projection = gameCam.ProjectionMatrix;

               mesh.Draw();
            }
         }
      }
      
      
      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;

         string displayString;

         ScreenManager.SpriteBatch.Draw(hudBorder, hudRect, Color.Black);
         ScreenManager.SpriteBatch.Draw(crossHairDecal, crossHairHudRect, Color.Red);

         Rectangle gamerPicRect;

         if(localPlayer.GamerPic != null)
         {
            gamerPicRect = new Rectangle((int)p1GamerPicHudPos.X,
                                         (int)p1GamerPicHudPos.Y,
                                         localPlayer.GamerPic.Width,
                                         localPlayer.GamerPic.Height);

            ScreenManager.SpriteBatch.Draw(localPlayer.GamerPic, gamerPicRect,
                                           Color.White);
         }
         
         displayString = "score: " + localPlayer.Score;
         gameFont.MeasureString(displayString);
         ScreenManager.SpriteBatch.DrawString(gameFont, displayString,
                                              scoreP1HudPos, Color.White);
         
         if(networkPlayer != null)
         {
            if(networkPlayer.GamerPic != null)
            {
               gamerPicRect = new Rectangle((int)p2GamerPicHudPos.X,
                                            (int)p2GamerPicHudPos.Y,
                                            networkPlayer.GamerPic.Width,
                                            networkPlayer.GamerPic.Height);

               ScreenManager.SpriteBatch.Draw(networkPlayer.GamerPic, gamerPicRect,
                                              Color.White);
            }

            displayString = "score: " + networkPlayer.Score;
            gameFont.MeasureString(displayString);
            ScreenManager.SpriteBatch.DrawString(gameFont, displayString,
                                                 scoreP2HudPos, Color.White);
         }

         displayString = "level: " + gameLevel;
         ScreenManager.SpriteBatch.DrawString(gameFont, displayString,
                                              levelHudPos, Color.White);
         
         displayString = "armor: " + localPlayer.Health;
         ScreenManager.SpriteBatch.DrawString(gameFont, displayString,
                                              healthHudPos, Color.White);
         
         displayString = gameTimeRemaining >= 60 ? ((int)(gameTimeRemaining / 60)).ToString() : "";
         ScreenManager.SpriteBatch.DrawString(gameFont, "Time: " + 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)
         {
            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 = gameCam.Position;
         Vector3 oldLookAtPosition = gameCam.LookAt;
         
         // Move left if negative else right if positive.
         if(delta.X < 0.0f)
            gameCam.MoveLeftXZ(delta.X * -1 * camMoveSpeed);
         else if(delta.X > 0.0f)
            gameCam.MoveRightXZ(delta.X * camMoveSpeed);

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

         localPlayer.SetCollisionCenter(gameCam.Position);

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

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

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

               gameCam.MoveForwardXZ(camMoveSpeed);
               
               localPlayer.SetCollisionCenter(gameCam.Position);

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

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

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

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

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

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

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

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

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

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

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

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

            gameCam.RotateCamera(delta);
         }
      }


      private void Fire(object value, GameTime gameTime)
      {
         localPlayerFired = false;

         if(IsActive == false)
            return;

         // This avoids extremely fast frame-based shots.
         if(gameShotTime > 0.0f)
            return;
         else
            gameShotTime = timeBetweenShots;

         ((GhostArena3D)ScreenManager.Game).SoundBank.PlayCue("zap");

         localPlayerFired = true;
         localPlayerFireForNetwork = true;
      }
      
      #endregion
   }
}
