using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;
using StrategicCombat.Maps;
using StrategicCombat.Teams;
using StrategicCombat.PlayerProfiles;
using StrategicCombat.CombatUnits;
using StrategicCombat.PowerUps;
using StrategicCombat.GamerStatistics;

namespace StrategicCombat.GameLogic
{
    /// <summary>
    /// This class represent game logic for a network game, system link or live.
    /// </summary>
    public class NetworkGameLogic : GamePlayLogic
    {
        #region Variables

        // Player profiles.
        public HumanPlayerProfile mainPlayer;
        public HumanPlayerProfile otherPlayer;

        // Player index of the main player
        public PlayerIndex mainPlayerIndex;

        // Variables for network communication.
        public PacketWriter packetWriter = new PacketWriter();
        public PacketReader packetReader = new PacketReader();

        #endregion

        #region Constructors

        /// <summary>
        /// Create new game logic for a network game.
        /// </summary>
        /// <param name="content"></param>
        /// <param name="networkSession"></param>
        public NetworkGameLogic(ContentManager content, NetworkSession networkSession)
            : base(networkSession, content)
        {

            // Create player profiles.
            foreach (NetworkGamer gamer in networkSession.AllGamers)
            {
                // Assign teams to gamers.
                if (gamer.IsHost)
                {
                    if (Program.hostTeam == 0)
                    {
                        gamer.Tag = new HumanPlayerProfile(gamer.Id, gamer.Gamertag, true, new LeftTeam(content), content);
                        this.leftGamerTag = gamer.Gamertag;
                    }
                    else if (Program.hostTeam == 1)
                    {
                        gamer.Tag = new HumanPlayerProfile(gamer.Id, gamer.Gamertag, true, new RightTeam(content), content);
                        this.rightGamerTag = gamer.Gamertag;
                    }
                }
                else
                {
                    if (Program.hostTeam == 0)
                    {
                        gamer.Tag = new HumanPlayerProfile(gamer.Id, gamer.Gamertag, false, new RightTeam(content), content);
                        this.rightGamerTag = gamer.Gamertag;
                    }
                    else if (Program.hostTeam == 1)
                    {
                        gamer.Tag = new HumanPlayerProfile(gamer.Id, gamer.Gamertag, false, new LeftTeam(content), content);
                        this.leftGamerTag = gamer.Gamertag;
                    }

                }
            }

            // Assign player profiles to player profile reference variables.
            foreach (NetworkGamer networkGamer in networkSession.AllGamers)
            {
                if (networkGamer.IsLocal)
                {
                    this.mainPlayer = networkGamer.Tag as HumanPlayerProfile;
                    LocalNetworkGamer localGamer = (LocalNetworkGamer)networkGamer;
                    this.mainPlayerIndex = localGamer.SignedInGamer.PlayerIndex;
                }
                else
                {
                    this.otherPlayer = networkGamer.Tag as HumanPlayerProfile;
                }
            }
        }

        #endregion

        #region Methods

        /// <summary>
        /// Handles player input on the board.
        /// </summary>
        /// <param name="playerIndex"></param>
        public override void PlayerInputOnBoard(LocalNetworkGamer gamer, PlayerIndex playerIndex, Map currentMap)
        {     
            // Get the player profile that corresponds to the gamer.
            HumanPlayerProfile localGamer = gamer.Tag as HumanPlayerProfile;

            // Check if the main player has the turn.
            if (localGamer.hasTurn == true)
            {
                // Execute the player movement.
                bool isMoved = localGamer.PlayerMovementOnBoard(playerIndex, currentMap, this.otherPlayer);

                // Send board movement data.
                if (isMoved == false)
                    SendBoardMovementData(gamer);
                else if (isMoved == true)
                    SendMovedToPositionOnBoardReliable(gamer);
            }

        } // End of the PlayerInputOnBoard method.

        /// <summary>
        /// Handles player input in a combat.
        /// </summary>
        /// <param name="gamer"></param>
        /// <param name="viewPort"></param>
        /// <param name="playerIndex"></param>
        /// <param name="currentCombatScene"></param>
        public override void PlayerInputInCombat(LocalNetworkGamer gamer, Viewport viewPort, PlayerIndex playerIndex, CombatScene currentCombatScene)
        {
            // Get the player profile that corresponds to the gamer.
            HumanPlayerProfile localGamer = gamer.Tag as HumanPlayerProfile;

            // Call the method for the movement.
            localGamer.PlayerMovementInCombat(viewPort, playerIndex, this.otherPlayer, currentCombatScene);

            // Send combat movement data.
            SendCombatMovementData(gamer);

        } // End of the PlayerInputInCombat method.

        /// <summary>
        /// Update teams on the board.
        /// </summary>
        /// <param name="gameTime"></param>
        /// <param name="currentMap"></param>
        public override void UpdateOnBoard(GameTime gameTime, Map currentMap)
        {
            // Pump the underlying session object.
            networkSession.Update();

            // Make sure the session has not ended.
            if (networkSession.Host != null)
            {
                // Get network data.
                GetNetworkData((LocalNetworkGamer)networkSession.FindGamerById((byte)this.mainPlayer.playerId));
            }

            // Update player profiles.
            this.mainPlayer.UpdateOnBoard(gameTime, currentMap);
            this.otherPlayer.UpdateOnBoard(gameTime, currentMap);

        } // End of the UpdateOnBoard method.

        /// <summary>
        /// Update combat units in a combat
        /// </summary>
        /// <param name="gameTime"></param>
        /// <param name="combatScene"></param>
        public override void UpdateInCombat(GameTime gameTime, CombatScene combatScene)
        {
            // Pump the underlying session object.
            networkSession.Update();

            // Make sure the session has not ended.
            if (networkSession.Host != null)
            {
                // Get network data.
                GetNetworkData((LocalNetworkGamer)networkSession.FindGamerById((byte)this.mainPlayer.playerId));
            }

            // Update player profiles.
            if (this.mainPlayer.combatUnitInCombat != null && this.otherPlayer.combatUnitInCombat != null)
            {
                // Check for a collision between the main player and the other players shots.
                this.mainPlayer.CollisionPlayerAndOtherShots(this.otherPlayer);
                this.mainPlayer.CollisionShotsAndOtherPlayer(this.otherPlayer);

                // Update player profiles
                this.mainPlayer.combatUnitInCombat.UpdateInCombat(gameTime, combatScene);
                this.otherPlayer.combatUnitInCombat.UpdateInCombat(gameTime, combatScene);

                // Play a movement sound if the other player is moving.
                if (this.otherPlayer.combatUnitInCombat.movementDelayCounter <= 0)
                {
                    // Play movement sound.
                    if (this.otherPlayer.isMoving == true)
                    {
                        this.otherPlayer.combatUnitInCombat.PlayMovementSound();
                        this.otherPlayer.combatUnitInCombat.movementDelayCounter = this.otherPlayer.combatUnitInCombat.movementDelay;
                    }
                }
                else
                {
                    this.otherPlayer.combatUnitInCombat.movementDelayCounter--;
                }

                // Add shots for the other player.
                if (this.otherPlayer.combatUnitInCombat.shotDelayCounter <= 0)
                {
                    if (this.otherPlayer.isShooting == true)
                    {
                        this.otherPlayer.combatUnitInCombat.AddShot();
                        this.otherPlayer.combatUnitInCombat.shotDelayCounter = this.otherPlayer.combatUnitInCombat.shotDelay;
                        
                        // Play the shooting sound.
                        this.otherPlayer.combatUnitInCombat.PlayShootingSound();
                    }
                }
                else
                {
                    this.otherPlayer.combatUnitInCombat.shotDelayCounter--;
                }

                // Handle power ups.
                PowerUpManagement(gameTime, combatScene);

            }

        } // End of the UpdateInCombat method.

        /// <summary>
        /// Draw teams on the board.
        /// </summary>
        /// <param name="spriteBatch"></param>
        /// <param name="currentMap"></param>
        public override void DrawOnBoard(SpriteBatch spriteBatch, SpriteFont font, Map currentMap)
        {
            // Draw each network gamer on the board.
            foreach (NetworkGamer networkGamer in networkSession.AllGamers)
            {
                // Look up the human player profile for the gamer.
                HumanPlayerProfile player = networkGamer.Tag as HumanPlayerProfile;

                // Draw the player profile
                player.DrawOnBoard(spriteBatch, font, currentMap);
            }

            // Draw left gamer tag.
            spriteBatch.Draw(this.blueCircleTexture, new Vector2(currentMap.xOffset - 2, currentMap.yOffset - 28), new Rectangle(0, 0, 32, 32), Color.White, 0, Vector2.Zero, 0.7f, SpriteEffects.None, 0);
            spriteBatch.DrawString(font, leftGamerTag, new Vector2(currentMap.xOffset + 26, currentMap.yOffset - 30), Color.DarkGray, 0, Vector2.Zero, 1.1f, SpriteEffects.None, 0);
            spriteBatch.DrawString(font, leftGamerTag, new Vector2(currentMap.xOffset + 25, currentMap.yOffset - 31), Color.White, 0, Vector2.Zero, 1.1f, SpriteEffects.None, 0);

            // Draw right gamer tag.
            spriteBatch.Draw(this.redCircleTexture, new Vector2(currentMap.xOffset + 503, currentMap.yOffset - 28), new Rectangle(0, 0, 32, 32), Color.White, 0, Vector2.Zero, 0.7f, SpriteEffects.None, 0);
            spriteBatch.DrawString(font, rightGamerTag, new Vector2(currentMap.xOffset + 499 - font.MeasureString(rightGamerTag).X * 1.1f, currentMap.yOffset - 30), Color.DarkGray, 0, Vector2.Zero, 1.1f, SpriteEffects.None, 0);
            spriteBatch.DrawString(font, rightGamerTag, new Vector2(currentMap.xOffset + 498 - font.MeasureString(rightGamerTag).X * 1.1f, currentMap.yOffset - 31), Color.White, 0, Vector2.Zero, 1.1f, SpriteEffects.None, 0);

        } // End of the DrawOnBoard method.

        /// <summary>
        /// Draw combat units in a combat.
        /// </summary>
        /// <param name="spriteBatch"></param>
        public override void DrawInCombat(SpriteBatch spriteBatch)
        {
            if (this.mainPlayer.combatUnitInCombat != null && this.otherPlayer.combatUnitInCombat != null)
            {
                // Draw power ups.
                foreach (PowerUp powerUp in this.powerUps)
                {
                    powerUp.Draw(spriteBatch);
                }

                // Draw each network gamer in a combat,
                foreach (NetworkGamer networkGamer in networkSession.AllGamers)
                {
                    // Look up the human player profile for the gamer.
                    HumanPlayerProfile player = networkGamer.Tag as HumanPlayerProfile;

                    // Draw the player profile
                    player.DrawInCombat(spriteBatch);
                }
            }

        } // End of the DrawInCombat method

        /// <summary>
        /// Check if there is a collision between combat units on the board.
        /// </summary>
        /// <returns>A boolean that indicates if there is a collision.</returns>
        public override bool CollisionBetweenCombatUnits()
        {
            // A flag to mark if there is a collision.
            bool collision = false;

            // Loop through each combat unit.
            foreach (CombatUnit combatUnit in this.mainPlayer.team.combatUnits)
            {
                foreach (CombatUnit otherCombatUnit in this.otherPlayer.team.combatUnits)
                {
                    if (combatUnit.boardCollisionRectangle.Intersects(otherCombatUnit.boardCollisionRectangle))
                    {
                        // There is a collision.
                        collision = true;

                        // Set combatUnits for player profiles.
                        this.mainPlayer.combatUnitInCombat = combatUnit;
                        this.otherPlayer.combatUnitInCombat = otherCombatUnit;

                        // Set the startpositions for combat units
                        this.mainPlayer.combatUnitInCombat.combatPosition = this.mainPlayer.combatUnitInCombat.combatStartPosition;
                        this.otherPlayer.combatUnitInCombat.combatPosition = this.otherPlayer.combatUnitInCombat.combatStartPosition;

                    }
                }
            }

            // Return the collision variable.
            return collision;

        } // End of the CollisionBetweenCombatUnits method.

        /// <summary>
        /// Check if a combat has ended.
        /// </summary>
        /// <returns></returns>
        public override bool CheckIfCombatHasEnded()
        {
            bool hasCombatEnded = false;
            
            // Check if the main players combat unit is dead.
            if (this.mainPlayer.combatUnitInCombat != null)
            {
                // Check if the main players combat unit is dead.
                if (this.mainPlayer.combatUnitInCombat.health <= 0)
                {
                    // Send data to the other player that the combat has ended.
                    SendCombatUnitIsDeadData((LocalNetworkGamer)networkSession.FindGamerById((byte)this.mainPlayer.playerId));

                    // Remove the combat unit for the main player.
                    this.mainPlayer.team.combatUnits.Remove(this.mainPlayer.combatUnitInCombat);
                    this.mainPlayer.combatUnitInCombat = null;

                    // Reset the rotation for the surviving combat unit.
                    this.otherPlayer.combatUnitInCombat.rotation = 0;
                    this.otherPlayer.combatUnitInCombat.combatDirection = Vector2.Zero;
                    this.otherPlayer.isShooting = false;

                    // Remove all shots for the other player.
                    while (this.otherPlayer.combatUnitInCombat.shots.Count > 0)
                    {
                        this.otherPlayer.combatUnitInCombat.shots.RemoveAt(0);
                    }
                }
            }

            // Check if the combat has ended.
            if (this.mainPlayer.combatUnitInCombat == null || this.otherPlayer.combatUnitInCombat == null)
            {
                hasCombatEnded = true;
            }

            // Return a value indicating if the combat has ended.
            return hasCombatEnded;

        } // End of the CheckIfCombatHasEnded method

        /// <summary>
        /// Check if a game has ended.
        /// </summary>
        public override void CheckIfGameHasEnded()
        {
            // Check if the main player profile is dead.
            if (this.mainPlayer.team.combatUnits.Count == 0)
            {
                this.winningPlayer = this.otherPlayer.playerName;
                this.hasGameEnded = true;
            }

            // Check if the other player profile is dead.
            if (this.otherPlayer.team.combatUnits.Count == 0)
            {
                this.winningPlayer = this.mainPlayer.playerName;
                this.hasGameEnded = true;

                // Add to gamer statistics.
                if (this.mainPlayer.team is LeftTeam)
                    Program.mainPlayerStatistics.blueTeamWins++;
                else if (this.mainPlayer.team is RightTeam)
                    Program.mainPlayerStatistics.redTeamWins++;

                // Get the storage device.
                StorageDevice storageDevice = Program.storageDevices[(int)this.mainPlayerIndex];

                // Save gamer statistics for the gamer.
                if (storageDevice != null && storageDevice.IsConnected)
                {
                    // Save the statistics.
                    FileHandler.SaveGamerStatsToFile(Program.mainPlayerStatistics, storageDevice);
                }

            }

        } // End of the CheckIfGameHasEnded method.

        /// <summary>
        /// This method handles power ups.
        /// </summary>
        /// <param name="gameTime"></param>
        /// <param name="currentCombatScene"></param>
        public override void PowerUpManagement(GameTime gameTime, CombatScene currentCombatScene)
        {

            // Check for a collision with a power up.
            for (int i = 0; i < this.powerUps.Count; i++)
            {
                // Check for a collision with a power up for the main player.
                if (this.powerUps[i].CheckForCollision(this.mainPlayer.combatUnitInCombat) == true)
                {
                    // Add the power up to the player.
                    if (this.powerUps[i].isDurationInfinite == false)
                    {
                        this.powerUps[i].durationCounter = this.powerUps[i].durationFrames;
                        this.mainPlayer.combatUnitInCombat.powerUps.Add(this.powerUps[i]);

                    }

                    // Play a collision sound.
                    Program.soundBank.PlayCue("powerUpCollection");

                    // Remove the power up.
                    this.powerUps.RemoveAt(i);
                }
                else if (this.powerUps[i].CheckForCollision(this.otherPlayer.combatUnitInCombat) == true)
                {
                    // Make a mark that the player has a power up.
                    if (this.powerUps[i].isDurationInfinite == false)
                    {
                        this.powerUps[i].durationCounter = this.powerUps[i].durationFrames;
                        this.otherPlayer.combatUnitInCombat.powerUps.Add(this.powerUps[i]);
                    }

                    // Play a collision sound.
                    Program.soundBank.PlayCue("powerUpCollection");

                    // Remove the power up.
                    this.powerUps.RemoveAt(i);
                }
            }

            // Check if we should reset a power up for the main player.
            for (int i = 0; i < this.mainPlayer.combatUnitInCombat.powerUps.Count; i++)
            {
                if (this.mainPlayer.combatUnitInCombat.powerUps[i].durationCounter <= 0)
                {
                    // Reset the power up.
                    this.mainPlayer.combatUnitInCombat.powerUps[i].ResetPowerUp(this.mainPlayer.combatUnitInCombat);

                    // Remove the power up.
                    this.mainPlayer.combatUnitInCombat.powerUps.RemoveAt(i);

                }
                else
                {
                    this.mainPlayer.combatUnitInCombat.powerUps[i].durationCounter--;
                }
            }

            // Check if we should reset a power up for the other player.
            for (int i = 0; i < this.otherPlayer.combatUnitInCombat.powerUps.Count; i++)
            {
                if (this.otherPlayer.combatUnitInCombat.powerUps[i].durationCounter <= 0)
                {
                    // Reset the power up.
                    this.otherPlayer.combatUnitInCombat.powerUps[i].ResetPowerUp(this.otherPlayer.combatUnitInCombat);

                    // Remove the power up.
                    this.otherPlayer.combatUnitInCombat.powerUps.RemoveAt(i);

                }
                else
                {
                    this.otherPlayer.combatUnitInCombat.powerUps[i].durationCounter--;
                }
            }

            // Check if we should add a power up on the host
            if (this.networkSession.IsHost)
            {
                if (this.powerUpCounter >= 30000)
                {
                    // Get a random value between 0 and 1.
                    int randomValue = (int)(this.rnd.NextDouble() * 5);

                    // Get a random x value between 
                    int xValue = (int)(this.rnd.NextDouble() * (currentCombatScene.combatSceneRectangle.Width - 32)) + (currentCombatScene.combatSceneRectangle.Left + 16);
                    int yValue = (int)(this.rnd.NextDouble() * (currentCombatScene.combatSceneRectangle.Height - 32)) + (currentCombatScene.combatSceneRectangle.Top + 16);

                    // Create a new Vector 2
                    Vector2 powerUpPosition = new Vector2(xValue, yValue);

                    // Send a message that a powerUp has been added.
                    SendPowerUpData((LocalNetworkGamer)networkSession.FindGamerById((byte)this.mainPlayer.playerId), (byte)randomValue, powerUpPosition);

                    // Add the power up.
                    if (randomValue == 0)
                        this.powerUps.Add(new HealthPowerUp(powerUpPosition, 100, this.health100PowerUpTexture));
                    else if (randomValue == 1)
                        this.powerUps.Add(new DamagePowerUp(powerUpPosition, 2, this.damagePowerUpTexture));
                    else if (randomValue == 2)
                        this.powerUps.Add(new ShotSpeedPowerUp(powerUpPosition, 2, this.shotPowerUpTexture));
                    else if (randomValue == 3)
                        this.powerUps.Add(new ShotDelayPowerUp(powerUpPosition, 2, this.shotDelayPowerUpTexture));
                    else if (randomValue == 4)
                        this.powerUps.Add(new HealthPowerUp(powerUpPosition, 200, this.health200PowerUpTexture));

                    // Reset the powerUpCounter to 0
                    this.powerUpCounter = 0;

                }
                else
                {
                    this.powerUpCounter += gameTime.ElapsedGameTime.Milliseconds;
                }
            }

        } // End of the PowerUpManagement method.

        #endregion

        #region Networking

        /// <summary>
        /// Send board movement data to other network gamers.
        /// </summary>
        /// <param name="gamer"></param>
        private void SendBoardMovementData(LocalNetworkGamer gamer)
        {
            // Set the datatype variable, 0: Board movement data.
            byte dataType = 0;

            packetWriter.Write(dataType);
            packetWriter.Write(this.mainPlayer.selectionPosition); // The position of the selection rectangle. (8 bytes)

            // Send data in order to all network gamers in the session.
            gamer.SendData(packetWriter, SendDataOptions.InOrder);

        } // End of the SendBoardMovementData method.

        /// <summary>
        /// Send data reliable about the position that a combat unit has moved to on the board.
        /// </summary>
        /// <param name="gamer"></param>
        private void SendMovedToPositionOnBoardReliable(LocalNetworkGamer gamer)
        {
            // Set the datatype variable, 4: The position of a combat unit that has been moved.
            byte dataType = 4;

            packetWriter.Write(dataType);
            packetWriter.Write(this.mainPlayer.selectionPosition); // The position of the selection rectangle. (8 bytes)
            packetWriter.Write(this.mainPlayer.indexOfSelectedCombatUnit); // Then index of the combat unit that is moved. (1 byte)
            packetWriter.Write(this.otherPlayer.hasTurn); // Boolean telling if the turn has changed. (1 bit)

            // Send data in order to all network gamers in the session.
            gamer.SendData(packetWriter, SendDataOptions.Reliable);
        }

        /// <summary>
        /// Send combat movement data to other network gamers.
        /// </summary>
        /// <param name="gamer"></param>
        private void SendCombatMovementData(LocalNetworkGamer gamer)
        {
            // Set the datatype variable, 1: Combat movement data, (24,1 bytes).
            byte dataType = 1;

            packetWriter.Write(dataType);
            packetWriter.Write(this.mainPlayer.combatUnitInCombat.combatPosition); // The position of the combat unit. (8 bytes)
            packetWriter.Write(this.mainPlayer.combatUnitInCombat.rotation); // The rotation of the combat unit. (4 bytes)
            packetWriter.Write(this.mainPlayer.combatUnitInCombat.combatDirection); // The direction of the combat unit. (8 bytes)
            packetWriter.Write(this.mainPlayer.combatUnitInCombat.health); // The health of the combat unit. (4 bytes)
            packetWriter.Write(this.mainPlayer.isShooting); // A boolean marking if the combat unit is shooting. (1 bit)
            packetWriter.Write(this.mainPlayer.isMoving); // A boolean marking if the combat unit is moving. (1 bit)

            // Send data in order to all network gamers in the session.
            gamer.SendData(packetWriter, SendDataOptions.InOrder);

        } // End of the SendCombatMovementData method.

        /// <summary>
        /// Send combat unit is dead data to other network gamers.
        /// </summary>
        /// <param name="gamer"></param>
        public void SendCombatUnitIsDeadData(LocalNetworkGamer gamer)
        {
            // Set the datatype variable, 2: Combat unit is dead.
            byte dataType = 2;

            packetWriter.Write(dataType);

            // Send data in order to all network gamers in the session.
            gamer.SendData(packetWriter, SendDataOptions.Reliable);

        } // End of the SendCombatUnitIsDeadData method.

        /// <summary>
        /// Send power up data to other network gamers.
        /// </summary>
        /// <param name="gamer"></param>
        /// <param name="powerUpNumber"></param>
        /// <param name="powerUpPosition"></param>
        public void SendPowerUpData(LocalNetworkGamer gamer, byte powerUpNumber, Vector2 powerUpPosition)
        {
            // Set the datatype variable, 3: Power up data.
            byte dataType = 3;

            packetWriter.Write(dataType);
            packetWriter.Write(powerUpNumber);
            packetWriter.Write(powerUpPosition);

            // Send data reliable to all network gamers in the session.
            gamer.SendData(packetWriter, SendDataOptions.ReliableInOrder);

        } // End of the SendPowerUpData method.

        /// <summary>
        /// Get network data from packages sent by the other player.
        /// </summary>
        /// <param name="gamer"></param>
        private void GetNetworkData(LocalNetworkGamer gamer)
        {

            // Keep reading as long as incoming packets are available.
            while (gamer.IsDataAvailable)
            {
                // Declare a variable for the gamer that has sent the package.
                NetworkGamer sender;

                // Read a single packet from the network.
                gamer.ReceiveData(packetReader, out sender);

                // We do not need to accept packages from our self.
                if (!sender.IsLocal)
                {
                    // Check what type av package that has been received.
                    byte typeOfPackage = packetReader.ReadByte();

                    // Board movement data (0).
                    if (typeOfPackage == 0)
                    {
                        
                        // Read data from the packet.
                        Vector2 selectionPosition = packetReader.ReadVector2();

                        // Look up the player associated with whoever sent this packet, the remotePlayer variable is 
                        // refering to the same player profile as "this.otherPlayer" variable. (Reference variables).
                        HumanPlayerProfile remotePlayer = sender.Tag as HumanPlayerProfile;

                        // Update the selection position.
                        remotePlayer.selectionPosition = selectionPosition;

                    } // End of board movement data

                    // Combat movement data (1)
                    if (typeOfPackage == 1)
                    {
                        // Read data from the package
                        Vector2 combatPosition = packetReader.ReadVector2();
                        float combatRotation = packetReader.ReadSingle();
                        Vector2 combatDirection = packetReader.ReadVector2();
                        int health = packetReader.ReadInt32();
                        bool isShooting = packetReader.ReadBoolean();
                        bool isMoving = packetReader.ReadBoolean();

                        // Look up the player associated with whoever sent this packet, the remotePlayer variable is 
                        // refering to the same player profile as "this.otherPlayer" variable. (Reference variables).
                        HumanPlayerProfile remotePlayer = sender.Tag as HumanPlayerProfile;

                        // Read data from the packet and store the information in the other player profile.
                        if (remotePlayer.combatUnitInCombat != null && this.mainPlayer.combatUnitInCombat != null)
                        {
                            remotePlayer.combatUnitInCombat.combatPosition = combatPosition;
                            remotePlayer.combatUnitInCombat.rotation = combatRotation;
                            remotePlayer.combatUnitInCombat.combatDirection = combatDirection;
                            remotePlayer.combatUnitInCombat.health = health;
                            remotePlayer.isShooting = isShooting;
                            remotePlayer.isMoving = isMoving;
                            
                        }

                    } // End of combat movement data

                    // Combat unit is dead (2)
                    if (typeOfPackage == 2)
                    {
                        // Check if the combatUnit reference for the other player is different from null.
                        if (this.otherPlayer.combatUnitInCombat != null)
                        {
                            // Remove the combat unit of the other player
                            this.otherPlayer.team.combatUnits.Remove(this.otherPlayer.combatUnitInCombat);
                            this.otherPlayer.combatUnitInCombat = null;
                        }

                        // Check if the combatUnit reference for the main player is different from null.
                        if (this.mainPlayer.combatUnitInCombat != null)
                        {
                            // Reset the rotation for the surviving combat unit.
                            this.mainPlayer.combatUnitInCombat.rotation = 0;
                            this.mainPlayer.combatUnitInCombat.combatDirection = Vector2.Zero;
                            this.mainPlayer.isShooting = false;

                            // Remove all shots for the main player.
                            while (this.mainPlayer.combatUnitInCombat.shots.Count > 0)
                            {
                                this.mainPlayer.combatUnitInCombat.shots.RemoveAt(0);
                            }
                        }

                    } // End of combat unit is dead data.

                    // Power up data (3)
                    if (typeOfPackage == 3)
                    {
                        // Read data from the package
                        byte powerUpNumber = packetReader.ReadByte();
                        Vector2 powerUpPosition = packetReader.ReadVector2();

                        if (powerUpNumber == 0)
                            this.powerUps.Add(new HealthPowerUp(powerUpPosition, 100, this.health100PowerUpTexture));
                        else if (powerUpNumber == 1)
                            this.powerUps.Add(new DamagePowerUp(powerUpPosition, 2, this.damagePowerUpTexture));
                        else if (powerUpNumber == 2)
                            this.powerUps.Add(new ShotSpeedPowerUp(powerUpPosition, 2, this.shotPowerUpTexture));
                        else if (powerUpNumber == 3)
                            this.powerUps.Add(new ShotDelayPowerUp(powerUpPosition, 2, this.shotDelayPowerUpTexture));
                        else if (powerUpNumber == 4)
                            this.powerUps.Add(new HealthPowerUp(powerUpPosition, 200, this.health200PowerUpTexture));

                    } // End of power up data

                    // New position of combat unit (4).
                    if (typeOfPackage == 4)
                    {

                        // Read data from the packet.
                        Vector2 selectionPosition = packetReader.ReadVector2();
                        byte indexOfSelectedCombatUnit = packetReader.ReadByte();
                        bool mainPlayersTurn = packetReader.ReadBoolean();

                        // Look up the player associated with whoever sent this packet, the remotePlayer variable is 
                        // refering to the same player profile as "this.otherPlayer" variable. (Reference variables).
                        HumanPlayerProfile remotePlayer = sender.Tag as HumanPlayerProfile;

                        // Update the selection position.
                        remotePlayer.selectionPosition = selectionPosition;

                        // Check if the turn has changed.
                        if (mainPlayersTurn == true)
                        {
                            // Change the position of the other combat unit.
                            remotePlayer.team.combatUnits[indexOfSelectedCombatUnit].currentPosition = selectionPosition;

                            // Change turn values.
                            this.mainPlayer.hasTurn = true;
                            remotePlayer.hasTurn = false;

                        }

                    } // End of new position of combat unit data

                } // End of the if sender not is local

            } // End of the while block

        } // End of the GetNetworkData method.

        #endregion

    } // End of class

} // End of namespace
