using System;
using System.Threading;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Net;
using System.Collections.Generic;
using Audio3D;

namespace FredriksdalAssault2
{
    /// <summary>
    /// This screen implements the actual game logic for a split screen game.
    /// </summary>
    public class SplitScreenGame : GameScreen, IAudioEmitter
    {
        #region Variables

        NetworkSession networkSession;

        ContentManager content;
        SpriteFont gameFont;
        float pauseAlpha;

        // Game time
        GameTime storedGameTime;

        // Default camera and original viewport
        Camera defaultCamera;
        Viewport originalViewport;

        // Textures
        Texture2D crosshairTexture;
        Texture2D redPoint;
        Texture2D redBackground;
        Texture2D zoomCrosshair;
        Texture2D alertSign;

        // 4 viewports and a map.
        List<Viewport> viewPorts = new List<Viewport>(4);
        IMap currentMap;

        // Total game time in milliseconds and maximum number of kills.
        int totalGameTime = 300000;
        int totalNumberOfKills = 15;

        // Blink delay and blink countdown to make the screen blink red. 
        int blinkDelay = 1000;
        int blinkCountdown = 0;

        // Has game ended
        bool hasGameEnded = false;
        String winningTeam;
        String winningPlayer = "All players";
        int maxKills = 0;

        // Random generator
        Random rndGenerator = new Random();

        // Audio
        public AudioManager audioManager;
        private Vector3 position;
        private Vector3 forward;
        private Vector3 up;
        private Vector3 velocity;

        // Number of gamers
        byte numberOfReadyGamers;

        #endregion

        #region Properties

        /// <summary>
        /// The logic for deciding whether the game is paused depends on whether
        /// this is a networked or single player game. If we are in a network session,
        /// we should go on updating the game even when the user tabs away from us or
        /// brings up the pause menu, because even though the local player is not
        /// responding to input, other remote players may not be paused. In single
        /// player modes, however, we want everything to pause if the game loses focus.
        /// </summary>
        public new bool IsActive
        {
            get
            {
                // Pause behavior for a split screen game.
                return base.IsActive;
            }
        }

        /// <summary>
        /// Gets or sets the 3D position of the entity.
        /// </summary>
        public Vector3 Position
        {
            get { return position; }
            set { position = value; }
        }

        /// <summary>
        /// Gets or sets which way the entity is facing.
        /// </summary>
        public Vector3 Forward
        {
            get { return forward; }
            set { forward = value; }
        }

        /// <summary>
        /// Gets or sets the orientation of this entity.
        /// </summary>
        public Vector3 Up
        {
            get { return up; }
            set { up = value; }
        }

        /// <summary>
        /// Gets or sets how fast this entity is moving.
        /// </summary>
        public Vector3 Velocity
        {
            get { return velocity; }
            protected set { velocity = value; }
        }

        #endregion

        #region Constructors

        /// <summary>
        /// Create a new split screen game.
        /// </summary>
        /// <param name="networkSession"></param>
        public SplitScreenGame(NetworkSession networkSession)
        {

            this.networkSession = networkSession;

            // Set total game time and max kills
            this.totalGameTime = Program.totalGameTimeMilliseconds;
            this.totalNumberOfKills = Program.maxKills;

            // Calculate the number of gamers.
            foreach (LocalNetworkGamer localGamer in networkSession.LocalGamers)
            {
                if (localGamer.IsReady == true)
                {
                    this.numberOfReadyGamers++;
                }
            }
            
            TransitionOnTime = TimeSpan.FromSeconds(1.5);
            TransitionOffTime = TimeSpan.FromSeconds(0.5);
        }

        #endregion

        #region Initialization

        /// <summary>
        /// Load graphics content for the game.
        /// </summary>
        public override void LoadContent()
        {
            // Create a content manager if it is null.
            if (content == null)
                this.content = new ContentManager(ScreenManager.Game.Services, "Content");

            // Set the original viewport
            this.originalViewport = ScreenManager.GraphicsDevice.Viewport;

            // Load the game font
            gameFont = content.Load<SpriteFont>("Fonts/gamefont");

            // Load the crosshair
            crosshairTexture = content.Load<Texture2D>("Textures/crosshair");
            redPoint = content.Load<Texture2D>("Textures/red-arrow");
            redBackground = content.Load<Texture2D>("Textures/redbackground");
            zoomCrosshair = content.Load<Texture2D>("Textures/zoomCrosshair");
            alertSign = content.Load<Texture2D>("Textures/alert");

            // Create a new audio manager
            this.audioManager = new AudioManager(content, 1500f, 0.5f);

            // Create viewports
            switch (this.numberOfReadyGamers)
            {
                case 1:
                    viewPorts.Add(this.originalViewport);
                    break;
                case 2:
                    viewPorts.Add(new Viewport(0, 0, this.originalViewport.Width / 2, this.originalViewport.Height));
                    viewPorts.Add(new Viewport(this.originalViewport.Width / 2, 0, this.originalViewport.Width / 2, this.originalViewport.Height));
                    break;
                case 3:
                    viewPorts.Add(new Viewport(0, 0, this.originalViewport.Width / 2, this.originalViewport.Height / 2));
                    viewPorts.Add(new Viewport(this.originalViewport.Width / 2, 0, this.originalViewport.Width / 2, this.originalViewport.Height / 2));
                    viewPorts.Add(new Viewport(0, this.originalViewport.Height / 2, this.originalViewport.Width, this.originalViewport.Height / 2));
                    break;
                case 4:
                    viewPorts.Add(new Viewport(0, 0, this.originalViewport.Width / 2, this.originalViewport.Height / 2));
                    viewPorts.Add(new Viewport(this.originalViewport.Width / 2, 0, this.originalViewport.Width / 2, this.originalViewport.Height / 2));
                    viewPorts.Add(new Viewport(0, this.originalViewport.Height / 2, this.originalViewport.Width / 2, this.originalViewport.Height / 2));
                    viewPorts.Add(new Viewport(this.originalViewport.Width / 2, this.originalViewport.Height / 2, this.originalViewport.Width / 2, this.originalViewport.Height / 2));
                    break;
            }

            // Create resources that are common to all players.
            this.defaultCamera = new Camera(content, new Vector3(0, 0, 0), new Vector3(0, 0, -1), new Vector3(0, 1, 0), ScreenManager.GraphicsDevice);
            
            // Select the map
            switch (Program.currentMap)
            {
                case 0:
                    this.currentMap = new Valley(content, ScreenManager.GraphicsDevice);
                    break;
                case 1:
                    this.currentMap = new Hills(content, ScreenManager.GraphicsDevice);
                    break;
                case 2:
                    this.currentMap = new Fortrest(content, ScreenManager.GraphicsDevice);
                    break;
            }

            // Set the initial attacking value from the program file
            bool isAttacking = Program.isAttackingTeam;
            byte gamerCounter = 0;

            // Create player models for each local gamer.
            foreach (LocalNetworkGamer gamer in networkSession.LocalGamers)
            {
                // Check if the gamer is ready.
                if (gamer.IsReady == true)
                {
                    // Change the team to get all players on different teams.
                    if (isAttacking == false)
                        isAttacking = true;
                    else
                        isAttacking = false;

                    // Create a new local player profile object for each gamer, with default values
                    gamer.Tag = new LocalPlayerProfile(content, gamer.Id, gamer.Gamertag, isAttacking, ScreenManager.GraphicsDevice,
                        this.defaultCamera, this.originalViewport);

                    // Get the gamer profile
                    LocalPlayerProfile localPlayer = gamer.Tag as LocalPlayerProfile;

                    // Get the start position, for an assault mode or a deathmatch..
                    Vector3 startPosition = new Vector3(0, 0, 0);
                    if (Program.gameMode == 0)
                        startPosition = localPlayer.getAssaultStartPosition();
                    else if (Program.gameMode == 1)
                        startPosition = localPlayer.getDeathMatchStartPosition();

                    // Create a camera for the player.
                    if (localPlayer.isAttacking == true)
                    {
                        localPlayer.playerCamera = new Camera(content, startPosition, new Vector3(0, 0, 1), new Vector3(0, 1, 0), ScreenManager.GraphicsDevice);
                        localPlayer.playerCamera.playerModel.rotationMatrix = Matrix.CreateRotationY(MathHelper.ToRadians(180));
                    }
                    else
                    {
                        localPlayer.playerCamera = new Camera(content, startPosition, new Vector3(0, 0, -1), new Vector3(0, 1, 0), ScreenManager.GraphicsDevice);
                    }

                    // Set other reference variables for the player.
                    localPlayer.playerShots = new DynamicResourcesManager(content, ScreenManager.GraphicsDevice, networkSession);
                    localPlayer.playerViewPort = viewPorts[gamerCounter++];
                    localPlayer.bloodEffects = new BloodParticles(content, ScreenManager.GraphicsDevice);
                }
            }

            // Change the start team so that players get on different teams from one game to the other.
            if (Program.isAttackingTeam == false)
                Program.isAttackingTeam = true;
            else
                Program.isAttackingTeam = false;

            // Start the sound clip
            this.audioManager.Play3DSound("summer", true, this);

            // once the load has finished, we use ResetElapsedTime to tell the game's
            // timing mechanism that we have just finished a very long frame, and that
            // it should not try to catch up.
            ScreenManager.Game.ResetElapsedTime();
        }


        /// <summary>
        /// Unload graphics content used by the game.
        /// </summary>
        public override void UnloadContent()
        {
            // Dispose the audio manager to stop all sounds.
            this.audioManager.Dispose(true);

            // Reset the viewport to the original viewport.
            ScreenManager.GraphicsDevice.Viewport = this.originalViewport;

            // Unload the content
            content.Unload();
        }

        #endregion

        #region Update and Draw

        /// <summary>
        /// Updates the state of the game.
        /// </summary>
        public override void Update(GameTime gameTime, bool otherScreenHasFocus,
                                                       bool coveredByOtherScreen)
        {
            base.Update(gameTime, otherScreenHasFocus, false);

            // Update the game time variable with the current game time.
            this.storedGameTime = gameTime;

            // Gradually fade in or out depending on whether we are covered by the pause screen.
            if (coveredByOtherScreen)
                pauseAlpha = Math.Min(pauseAlpha + 1f / 32, 1);
            else
                pauseAlpha = Math.Max(pauseAlpha - 1f / 32, 0);

            if (IsActive == true)
            {

                // Do updates that are common to all players.
                this.currentMap.Update(gameTime);
                this.audioManager.Update(gameTime);

                // Decrease total game time if the game not has ended.
                if (this.totalGameTime > -1)
                {
                    // Decrease total gametime
                    this.totalGameTime -= gameTime.ElapsedGameTime.Milliseconds;
                }

                // Check if the game has ended
                if (Program.gameMode == 0)
                {
                    if (this.currentMap.numberOfBarrels == 0 || this.totalGameTime <= 0)
                    {
                        // Set the game as ended.
                        this.hasGameEnded = true;

                        if (this.currentMap.numberOfBarrels == 0)
                            this.winningTeam = "Attackers";
                        else
                            this.winningTeam = "Defenders";
                    }
                }
                else if (Program.gameMode == 1)
                {
                    // Check if the game has ended, totalNumberOfKills or the end of the game time.
                    if (this.maxKills >= this.totalNumberOfKills || this.totalGameTime <= 0)
                    {
                        // Set the game as ended.
                        this.hasGameEnded = true;
                    }
                }

                // Updates for each local gamer.
                foreach (LocalNetworkGamer gamer in networkSession.LocalGamers)
                {
                    // Check to see if the gamer is ready
                    if (gamer.IsReady == true)
                    {
                        // Look up what player is associated with this local player.
                        LocalPlayerProfile player = gamer.Tag as LocalPlayerProfile;

                        // Update the listener and the audio emitter for the host
                        if (gamer.IsHost == true)
                        {
                            // Update the audio listener
                            this.audioManager.listener.Position = player.playerCamera.cameraPosition;
                            this.audioManager.listener.Forward = player.playerCamera.cameraDirection;
                            this.audioManager.listener.Up = Vector3.Up;
                            this.audioManager.listener.Velocity = player.playerCamera.cameraDirection * player.playerCamera.cameraSpeed;

                            // Update the audio emitter
                            this.Position = player.playerCamera.cameraPosition;
                            this.Forward = player.playerCamera.cameraDirection;
                            this.Up = Vector3.Up;
                            this.Velocity = Vector3.Zero;
                        }

                        // Only do updates when the game is running, otherwise will the sound play forever.
                        if (this.hasGameEnded == false)
                        {
                            // Update each player model
                            player.UpdatePlayerProfile(gameTime, this.audioManager);

                            // Check for barrel collision.
                            this.currentMap.CollisionCheckItemSplitScreen(player, this.audioManager);
                        }

                        // If a player is dead, check to see if he should be alive
                        if (player.isDead == true)
                        {
                            // Check if the player should be alive.
                            player.UpdateSpawnTime(storedGameTime);

                            // If the player have become alive, get him back to the game.
                            if (player.isDead == false)
                            {
                                //Get back to the game.
                                if (Program.gameMode == 0)
                                {
                                    player.playerCamera.cameraPosition = player.getAssaultStartPosition();
                                }
                                else if (Program.gameMode == 1)
                                {
                                    player.playerCamera.cameraPosition = player.getDeathMatchStartPosition();
                                }

                                // Set the look up and down variable
                                player.playerCamera.cameraLookUpDown = new Vector3(0, 0, 0);

                                if (player.isAttacking == true)
                                {
                                    player.playerCamera.cameraDirection = new Vector3(0, 0, 1);

                                    // Reset the rotation matrix for the player soldier so that he is turned in the right direction.
                                    player.playerCamera.playerModel.rotationMatrix = Matrix.Identity * Matrix.CreateRotationY(MathHelper.ToRadians(180));
                                }
                                else
                                {
                                    player.playerCamera.cameraDirection = new Vector3(0, 0, -1);

                                    // Reset the rotation matrix for the player soldier so that he is turned in the right direction.
                                    player.playerCamera.playerModel.rotationMatrix = Matrix.Identity;
                                }

                            }
                        }

                        // Calculate the maximum number of kills in a deathmatch
                        if (Program.gameMode == 1)
                        {
                            if (player.numberOfKills > this.maxKills)
                            {
                                this.maxKills = player.numberOfKills;
                                this.winningPlayer = player.playerName;
                            }
                        }
                    }
                }

            } // End of IsActive

            // If we are in a network game, check if we should return to the lobby.
            if ((networkSession != null) && !IsExiting)
            {
                if (networkSession.SessionState == NetworkSessionState.Lobby)
                {
                    LoadingScreen.Load(ScreenManager, true, null,
                                       new BackgroundScreen(),
                                       new SplitScreenLobby(networkSession));
                }
            }

        } // End of the Update method.

        /// <summary>
        /// Lets the game respond to player input. Unlike the Update method,
        /// this will only be called when the gameplay screen is active.
        /// </summary>
        public override void HandleInput(InputState input)
        {
            if (input == null)
                throw new ArgumentNullException("input");

            // Handle input for all the local network gamers.
            foreach (LocalNetworkGamer gamer in networkSession.LocalGamers)
            {
                // Check to see if the gamer is ready
                if (gamer.IsReady == true)
                {
                    if (!HandlePlayerInput(input, gamer.SignedInGamer.PlayerIndex, gamer))
                        break;
                }
            }

        } // End of the HandleInput method.

        /// <summary>
        /// Handles input for the specified player.
        /// </summary>
        bool HandlePlayerInput(InputState input, PlayerIndex playerIndex, LocalNetworkGamer gamer)
        {
            // Look up inputs for the specified player profile.
            KeyboardState keyboardState = input.CurrentKeyboardStates[(int)playerIndex];
            GamePadState gamePadState = input.CurrentGamePadStates[(int)playerIndex];

            // Look up the player profile for this local gamer.
            LocalPlayerProfile localPlayer = gamer.Tag as LocalPlayerProfile;

            // The game pauses either if the user presses the pause button, or if
            // they unplug the active gamepad.
            bool gamePadDisconnected = !gamePadState.IsConnected && input.GamePadWasConnected[(int)playerIndex];

            // Set the viewport to the players view port.
            ScreenManager.GraphicsDevice.Viewport = localPlayer.playerViewPort;

            // Handle player input, if the player is alive.
            if (localPlayer.isDead == false)
            {
                localPlayer.playerCamera.PlayerInput(storedGameTime, this.currentMap, playerIndex);
                localPlayer.playerShots.FireBullets(storedGameTime, localPlayer.playerCamera.cameraPosition, localPlayer.playerCamera.cameraDirection,
                    localPlayer.playerCamera.cameraLookUpDown, localPlayer.isDead, this.hasGameEnded, playerIndex);

                // Set the isShooting variable.
                localPlayer.isShooting = localPlayer.playerShots.isShooting;

            }

            // Update bullets
            localPlayer.playerShots.Update(storedGameTime, this.currentMap);

            // Check for collision with bullets.
            foreach (LocalNetworkGamer networkGamer in networkSession.LocalGamers)
            {
                // Check to see if the gamer is ready
                if (networkGamer.IsReady == true)
                {
                    // Do not do this collision check for the local gamer
                    if (gamer.Id != networkGamer.Id)
                    {
                        // Look up what player is associated with this network player.
                        LocalPlayerProfile networkPlayer = networkGamer.Tag as LocalPlayerProfile;

                        // Do a collision check, returns true or false.
                        bool collision = localPlayer.playerShots.BulletCollisionOnClient(gamer, networkPlayer, this.currentMap);

                        // Update death data if there was a collision.
                        if (collision == true)
                        {
                            // Create a new random number
                            double randomValue = rndGenerator.NextDouble();

                            // The player is dead 50 % of the time and wounded 50 % of the time.
                            if (randomValue < 0.5)
                            {
                                // Set data for the network gamer that is dead.
                                networkPlayer.isDead = true;
                                networkPlayer.killedByGamerTag = gamer.Gamertag;

                                // Start the blood effect
                                localPlayer.bloodEffectCounter = localPlayer.bloodEffectTime;
                                localPlayer.bloodEffectStartPosition = networkPlayer.soldier.boundingSpheres[1].Center;
                                localPlayer.bloodEffects.AddParticles(80, localPlayer.bloodEffectStartPosition);

                                // Set position data and play the choke sound.
                                this.Position = networkPlayer.playerCamera.cameraPosition;
                                this.Forward = networkPlayer.playerCamera.cameraDirection;
                                this.Up = Vector3.Up;
                                this.Velocity = Vector3.Zero;
                                audioManager.Play3DSound("choke", false, this);

                                // Add one kill to the record
                                if (Program.gameMode == 0)
                                {
                                    if (networkPlayer.isAttacking != localPlayer.isAttacking)
                                        localPlayer.numberOfKills++;
                                    else
                                        localPlayer.numberOfKills--;
                                }
                                else if (Program.gameMode == 1)
                                    localPlayer.numberOfKills++;
                            }
                            else
                            {
                                // Mark the player as wounded.
                                networkPlayer.isWounded = true;
                            }
                        }
                    }
                }
            }

            // Reset the viewport to the original viewport.
            ScreenManager.GraphicsDevice.Viewport = this.originalViewport;

            // Pause the game.
            if (input.IsPauseGame(playerIndex) || gamePadDisconnected)
            {
                ScreenManager.AddScreen(new PauseMenuScreen(networkSession), playerIndex);
                return false;
            }

            // Add the end game screen if the game has ended.
            if (this.hasGameEnded == true)
            {
                // Add the end game screen
                ScreenManager.AddScreen(new EndSplitScreenGame(networkSession, this.winningTeam, this.originalViewport), null);
                return false;
            }

            return true;
        }

        /// <summary>
        /// Draws the gameplay screen.
        /// </summary>
        public override void Draw(GameTime gameTime)
        {
            
            //Reset the viewport
            ScreenManager.GraphicsDevice.Viewport = this.originalViewport;

            // Clear the graphics device
            ScreenManager.GraphicsDevice.Clear(ClearOptions.Target | ClearOptions.DepthBuffer, Color.Black, 1.0f, 1);

            foreach (LocalNetworkGamer gamer in networkSession.LocalGamers)
            {     
                // Check to see if the gamer is ready
                if (gamer.IsReady == true)
                {
                    // Look up the player profile.
                    LocalPlayerProfile localPlayer = gamer.Tag as LocalPlayerProfile;

                    // Set the viewport
                    ScreenManager.GraphicsDevice.Viewport = localPlayer.playerViewPort;

                    // Draw the map.
                    this.currentMap.Draw(gameTime, localPlayer.playerCamera.view, localPlayer.playerCamera.projection, localPlayer.playerCamera.viewProj,
                        localPlayer.playerCamera.cameraPosition);
     
                    // Draw the weapon underneath the camera.
                    if(localPlayer.isDead == false)
                        localPlayer.playerCamera.DrawPlayerWeapon();

                    // Draw all the player models in the local network game.
                    if (hasGameEnded == false)
                    {
                        // For each person in the session...
                        foreach (LocalNetworkGamer networkGamer in networkSession.LocalGamers)
                        {
                            // Check to see if the gamer is ready
                            if (networkGamer.IsReady == true)
                            {
                                // Look up the player object belonging to this network gamer.
                                LocalPlayerProfile networkPlayer = networkGamer.Tag as LocalPlayerProfile;

                                // Do not draw other players when you are dead
                                if (!localPlayer.isDead)
                                {
                                    
                                    // Just draw playermodels and the shot effect for other gamers.
                                    if (localPlayer.playerId != networkPlayer.playerId)
                                    {
                                        // Draw each player model
                                        networkPlayer.DrawPlayerProfile(localPlayer.playerCamera.view, localPlayer.playerCamera.projection);

                                    }
                                }
                            }
                        }
                    }

                    // Draw vegetation
                    this.currentMap.DrawGrass(localPlayer.playerCamera.view, localPlayer.playerCamera.projection, gameTime);
                    this.currentMap.DrawTrees(localPlayer.playerCamera.view, localPlayer.playerCamera.projection, localPlayer.playerCamera.cameraPosition);

                    // Draw enemy shot effect.
                    if (hasGameEnded == false)
                    {
                        // For each person in the session...
                        foreach (LocalNetworkGamer networkGamer in networkSession.LocalGamers)
                        {
                            // Check to see if the gamer is ready
                            if (networkGamer.IsReady == true)
                            {
                                // Look up the player object belonging to this network gamer.
                                LocalPlayerProfile networkPlayer = networkGamer.Tag as LocalPlayerProfile;

                                // Do not draw other players when you are dead
                                if (!localPlayer.isDead)
                                {

                                    // Just draw playermodels and the shot effect for other gamers.
                                    if (localPlayer.playerId != networkPlayer.playerId)
                                    {
 
                                        // Draw the shot effect
                                        networkPlayer.DrawShotEffect(gameTime, localPlayer.playerCamera.view, localPlayer.playerCamera.projection);
                                    }
                                }
                            }
                        }
                    }

                    // Draw blood effects (Drawn late because of alpha blending)
                    if (localPlayer.bloodEffectCounter >= 0)
                    {
                        localPlayer.bloodEffects.DrawBloodEffect(gameTime, localPlayer.playerCamera.view, localPlayer.playerCamera.projection);

                        // Subract game time from the bloodEffect counter
                        localPlayer.bloodEffectCounter -= gameTime.ElapsedGameTime.Milliseconds;
                    }

                    // Draw player shots (Drawn late because of alpha blending)
                    localPlayer.playerShots.Draw(gameTime, localPlayer.playerCamera.view, localPlayer.playerCamera.projection);
                    
                    // A new spritebatch object
                    SpriteBatch spriteBatch = ScreenManager.SpriteBatch;
                    Viewport viewport = ScreenManager.GraphicsDevice.Viewport;

                    // Game mode string
                    String gameMode = "Deathmatch";

                    // Set a attacking and defender string
                    String leadingPlayerString = this.maxKills.ToString() + "/" + this.totalNumberOfKills + " kills";

                    // Kills in deathmatch
                    String killsInDeathMatch = "Kills: " + localPlayer.numberOfKills.ToString();

                    // Set a attacking and defender string
                    String attackerDefender = "Attacking team";

                    if (localPlayer.isAttacking == false)
                        attackerDefender = "Defending team";

                    // Number of kills string
                    String numberOfKills = "Kills: " + localPlayer.numberOfKills.ToString() + " (" +
                            localPlayer.collectedBarrels.ToString() + ")";

                    // Barrels left string
                    String barrelsLeft = this.currentMap.numberOfBarrels + " barrels left";

                    // Text scale
                    float textScale = 0.5f;
                    Color textColor = new Color(255, 255, 255, 255);

                    // Title safe area offset.
                    Int16 offset = 140;

                    // Set text color to black if the player is on the attacking team.
                    if (localPlayer.isAttacking && Program.gameMode == 0)
                        textColor = new Color(0, 0, 0, 255);

                    // Total game time string
                    int minutes = this.totalGameTime / 60000;
                    String gameTimeLeft = minutes + ":" + String.Format("{0:00}", (this.totalGameTime - (minutes * 60000)) / 1000);

                    // Do not draw this if the player is dead or the game has ended.
                    if (localPlayer.isDead == false && !this.hasGameEnded)
                    {
                        spriteBatch.Begin();

                        // Draw the crosshair
                        spriteBatch.Draw(crosshairTexture, new Vector2((viewport.Width / 2) - (crosshairTexture.Width / 2),
                        (viewport.Height / 2) - (crosshairTexture.Height / 2)), Color.White);

                        if (localPlayer.playerCamera.isZooming == true)
                        {
                            // Draw the zoom crosshair
                            spriteBatch.Draw(zoomCrosshair, new Vector2((viewport.Width / 2) - (zoomCrosshair.Width / 2),
                            (viewport.Height / 2) - (zoomCrosshair.Height / 2)), Color.White);
                        }

                        if (Program.gameMode == 0)
                        {
                            // Draw team information
                            spriteBatch.DrawString(gameFont, attackerDefender, new Vector2(viewport.Width
                            - (gameFont.MeasureString(attackerDefender).X * textScale) - offset, offset), textColor, 0,
                                                Vector2.Zero, textScale, SpriteEffects.None, 0);


                            // Draw number of kills and barrels collected.
                            spriteBatch.DrawString(gameFont, numberOfKills, new Vector2(viewport.Width
                            - (gameFont.MeasureString(numberOfKills).X * textScale) - offset, offset + 20), textColor, 0,
                                               Vector2.Zero, textScale, SpriteEffects.None, 0);

                            // Barrels left
                            spriteBatch.DrawString(gameFont, barrelsLeft, new Vector2(viewport.Width
                                - (gameFont.MeasureString(barrelsLeft).X * textScale) - offset, offset + 40), textColor, 0,
                                               Vector2.Zero, textScale, SpriteEffects.None, 0);
                        }
                        else if (Program.gameMode == 1)
                        {

                            // Draw information about the game mode
                            spriteBatch.DrawString(gameFont, gameMode, new Vector2(viewport.Width
                                - (gameFont.MeasureString(gameMode).X * textScale) - offset, offset), textColor, 0,
                                               Vector2.Zero, textScale, SpriteEffects.None, 0);

                            // Draw number of kills.
                            spriteBatch.DrawString(gameFont, killsInDeathMatch, new Vector2(viewport.Width
                                - (gameFont.MeasureString(killsInDeathMatch).X * textScale) - offset, offset + 20), textColor, 0,
                                               Vector2.Zero, textScale, SpriteEffects.None, 0);

                            // Number of max kills compared to total number of kills.
                            spriteBatch.DrawString(gameFont, leadingPlayerString, new Vector2(viewport.Width
                            - (gameFont.MeasureString(leadingPlayerString).X * textScale) - offset, offset + 40), textColor, 0,
                                               Vector2.Zero, textScale, SpriteEffects.None, 0);
                        }

                        // Draw total game time.
                        spriteBatch.DrawString(gameFont, gameTimeLeft, new Vector2(viewport.Width
                            - (gameFont.MeasureString(gameTimeLeft).X * textScale) - offset, offset + 60), textColor, 0, Vector2.Zero, textScale, SpriteEffects.None, 0);

                        // Draw the minimap
                        spriteBatch.Draw(this.currentMap.minimap, new Vector2(offset, offset), null, new Color(70, 70, 70, 10), 0, new Vector2(0, 0),
                            0.6f, SpriteEffects.None, 0);

                        int xValue = (int)(localPlayer.playerCamera.cameraPosition.X / 166.66666f);
                        int yValue = (int)(localPlayer.playerCamera.cameraPosition.Z / 166.66666f);

                        // Pulsate the size of the selected menu entry.
                        double time = gameTime.TotalGameTime.TotalSeconds;

                        float pulsate = (float)Math.Sin(time * 6) + 1;

                        float scale = 0.2f + pulsate * 0.30f;
                        float scaleBarrel = 0.05f + pulsate * 0.05f;

                        // Rotate the arrow.
                        Vector3 destinationRightVector = Vector3.Cross(localPlayer.playerCamera.cameraDirection, Vector3.Up);
                        float forwardDot = Vector3.Dot(new Vector3(0, 0, -1), localPlayer.playerCamera.cameraDirection);
                        float rightDot = Vector3.Dot(new Vector3(0, 0, -1), destinationRightVector);

                        // Make sure we stay in range no matter what, so Acos doesn't fail later 
                        forwardDot = MathHelper.Clamp(forwardDot, -1.0f, 1.0f);

                        float angleBetween = (float)Math.Acos((float)forwardDot);

                        if (rightDot < 0.0f)
                            angleBetween *= -1.0f;

                        // If assault
                        if (Program.gameMode == 0)
                        {
                            // Draw alert sign for barrels.
                            for (int b = 0; b < this.currentMap.items.Length; b++)
                            {
                                if (this.currentMap.items[b] != null)
                                {
                                    int barrelX = (int)(this.currentMap.items[b].boundingSpheres[0].Center.X / 166.66666f);
                                    int barrelY = (int)(this.currentMap.items[b].boundingSpheres[0].Center.Z / 166.66666f);

                                    spriteBatch.Draw(alertSign, new Vector2(offset + barrelX + 38.4f, offset + barrelY + 38.4f), null, Color.White, -angleBetween, new Vector2(8, 8),
                                    scaleBarrel, SpriteEffects.None, 0);
                                }
                            }
                        }

                        // Draw the red pointer
                        spriteBatch.Draw(redPoint, new Vector2(offset + xValue + 38.4f, offset + yValue + 38.4f), null, Color.White, -angleBetween, new Vector2(8, 8),
                            scale, SpriteEffects.None, 0);

                        // If the player is wounded, make the screen blink red
                        if (localPlayer.isWounded == true && localPlayer.isDead == false)
                        {
                            if (this.blinkCountdown <= 0)
                            {
                                spriteBatch.Draw(this.redBackground, new Rectangle(0, 0, ScreenManager.GraphicsDevice.Viewport.Width, ScreenManager.GraphicsDevice.Viewport.Height),
                                    new Color(255, 255, 255, 0.4f));

                                // Reset the blink countdown.
                                this.blinkCountdown = this.blinkDelay;
                            }
                            else
                            {
                                this.blinkCountdown -= gameTime.ElapsedGameTime.Milliseconds;
                            }
                        }

                        // Draw gamer tags
                        foreach (LocalNetworkGamer tagForGamer in networkSession.LocalGamers)
                        {
                            // Check if the gamer is ready.
                            if (tagForGamer.IsReady == true)
                            {
                                // Look up the player object belonging to this network gamer.
                                LocalPlayerProfile otherPlayer = tagForGamer.Tag as LocalPlayerProfile;

                                // Do not draw other players when you are dead
                                if (!localPlayer.isDead)
                                {
                                    // Do not draw our own gamer tag.
                                    if (localPlayer.playerId != otherPlayer.playerId)
                                    {
                                        otherPlayer.DrawGamerTag(ScreenManager.GraphicsDevice.Viewport, localPlayer.playerCamera.view, localPlayer.playerCamera.projection,
                                            ScreenManager.SpriteBatch, this.gameFont, localPlayer.playerCamera.cameraPosition, localPlayer.playerCamera.cameraDirection + 
                                            localPlayer.playerCamera.cameraLookUpDown, this.currentMap);
                                    }
                                }
                            }
                        }

                        spriteBatch.End();

                    } // End of if dead and this game not has ended.
                    else if (localPlayer.isDead == true && !this.hasGameEnded)
                    {

                        // Darken down the viewport under the text.
                        ScreenManager.FadeBackBufferToBlack(0.5f);

                        // Draw a spawn screen if the player is dead.
                        spriteBatch.Begin();

                        // Killed by string
                        String killedByString = "You were killed by " + localPlayer.killedByGamerTag;

                        // Time string
                        String timeString = "Respawn in " + (localPlayer.spawnCountDown / 1000) + " seconds";

                        // Draw the killed by name centered on the screen
                        SpriteFont killedByFont = ScreenManager.Font;
                        Vector2 textPosition = new Vector2(viewport.Width / 2, viewport.Height / 2);
                        Vector2 textOrigin = killedByFont.MeasureString(killedByString) / 2;
                        Color killedStringColor = new Color(200, 0, 0) * TransitionAlpha;
                        float killedByTextScale = 1.00f;

                        // Draw the killed by name
                        spriteBatch.DrawString(killedByFont, killedByString, textPosition, killedStringColor, 0,
                                               textOrigin, killedByTextScale, SpriteEffects.None, 0);

                        // Draw the respawn time
                        spriteBatch.DrawString(killedByFont, timeString, textPosition + new Vector2(0, 40), killedStringColor, 0,
                                                killedByFont.MeasureString(timeString) / 2, killedByTextScale, SpriteEffects.None, 0);

                        spriteBatch.End();

                    }


                    // If the game is transitioning on or off, fade it out to black.
                    if (TransitionPosition > 0 || pauseAlpha > 0)
                    {
                        float alpha = MathHelper.Lerp(1f - TransitionAlpha, 1f, pauseAlpha / 2);

                        ScreenManager.FadeBackBufferToBlack(alpha);
                    }

                    //Reset the viewport
                    ScreenManager.GraphicsDevice.Viewport = this.originalViewport;

                } // End of if gamer is ready

            } // End of foreach local gamer 
       
        } // End of draw method.

        #endregion
    
    } // End of class

} // End of namespace
