using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
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.GamerServices;
using StrategicCombat.PlayerProfiles;
using StrategicCombat.CombatUnits;
using StrategicCombat.Teams;
using StrategicCombat.Maps;
using StrategicCombat.PowerUps;

namespace StrategicCombat.GameLogic
{
    /// <summary>
    /// This class represent the game logic for a local 2 player game.
    /// </summary>
    public class LocalMultiplayerGameLogic : GamePlayLogic
    {

        #region Variables

        public HumanPlayerProfile hostPlayer;
        public HumanPlayerProfile otherPlayer;

        #endregion

        #region Constructors

        /// <summary>
        /// Create new LocalMultiplayerGameLogic.
        /// </summary>
        /// <param name="content"></param>
        public LocalMultiplayerGameLogic(ContentManager content, NetworkSession networkSession)
            : base(networkSession, content)
        {

            // Create player profiles.
            foreach (LocalNetworkGamer gamer in networkSession.LocalGamers)
            {
                // Assign players to a team and set the reference variable for player profile.
                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;
                    }

                    this.hostPlayer = gamer.Tag as HumanPlayerProfile;
                }
                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;
                    }

                    this.otherPlayer = gamer.Tag as HumanPlayerProfile;
                }
            }
        }

        #endregion

        #region Methods

        /// <summary>
        /// Handles player input on the board.
        /// </summary>
        /// <param name="gamer"></param>
        /// <param name="playerIndex"></param>
        /// <param name="currentMap"></param>
        public override void PlayerInputOnBoard(LocalNetworkGamer gamer, PlayerIndex playerIndex, Map currentMap)
        {
            // Look up the human player profile for the gamer.
            HumanPlayerProfile player = gamer.Tag as HumanPlayerProfile;

            // Call the method for the movement.
            if(player.hasTurn == true)
            {
                if(gamer.IsHost)
                    player.PlayerMovementOnBoard(playerIndex, currentMap, this.otherPlayer);
                else
                    player.PlayerMovementOnBoard(playerIndex, currentMap, this.hostPlayer);
            }

        } // End of the PlayerInputOnBoard method.

        /// <summary>
        /// Handles player input in 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)
        {
            // Look up the human player profile for the gamer.
            HumanPlayerProfile player = gamer.Tag as HumanPlayerProfile;

            // Call the method for player movement in a combat.
            if(gamer.IsHost)
                player.PlayerMovementInCombat(viewPort, playerIndex, this.otherPlayer, currentCombatScene);
            else
                player.PlayerMovementInCombat(viewPort, playerIndex, this.hostPlayer, currentCombatScene);

        } // 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)
        {
            // Update each gamer on the board.
            foreach (LocalNetworkGamer networkGamer in networkSession.LocalGamers)
            {
                // Look up the human player profile for the gamer.
                HumanPlayerProfile player = networkGamer.Tag as HumanPlayerProfile;

                player.UpdateOnBoard(gameTime, currentMap);
            }

        } // End of the UpdateOnBoard method

        /// <summary>
        /// Handles updates in a combat.
        /// </summary>
        /// <param name="gameTime"></param>
        /// <param name="combatScene"></param>
        public override void UpdateInCombat(GameTime gameTime, CombatScene combatScene)
        {

            // Check for a collision between shots and the player.
            this.hostPlayer.CollisionCheckShotsAndPlayer(this.otherPlayer);
            this.otherPlayer.CollisionCheckShotsAndPlayer(this.hostPlayer);

            // Update the player profiles
            this.hostPlayer.combatUnitInCombat.UpdateInCombat(gameTime, combatScene);
            this.otherPlayer.combatUnitInCombat.UpdateInCombat(gameTime, combatScene);
   
            // 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 gamer on the board.
            foreach (LocalNetworkGamer networkGamer in networkSession.LocalGamers)
            {
                // 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)
        {
            // Draw power ups.
            foreach (PowerUp powerUp in this.powerUps)
            {
                powerUp.Draw(spriteBatch);
            }

            // Draw each gamer in a combat,
            foreach (LocalNetworkGamer networkGamer in networkSession.LocalGamers)
            {
                // 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.hostPlayer.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.hostPlayer.combatUnitInCombat = combatUnit;
                        this.otherPlayer.combatUnitInCombat = otherCombatUnit;

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

                    }
                }
            }

            // Return the collision variable.
            return collision;

        } // End of the CollisionBetweenCombatUnits method.

        /// <summary>
        /// This method is used to check if a combat has ended.
        /// </summary>
        /// <returns></returns>
        public override bool CheckIfCombatHasEnded()
        {
            bool hasCombatEnded = false;

            // Check if the host combat unit is dead.
            if (this.hostPlayer.combatUnitInCombat.health <= 0)
            {
                // Remove the combat unit
                this.hostPlayer.team.combatUnits.Remove(this.hostPlayer.combatUnitInCombat);

                // Reset the rotation and remove shots for the surviving combat unit.
                this.otherPlayer.combatUnitInCombat.rotation = 0;
                this.otherPlayer.combatUnitInCombat.combatDirection = Vector2.Zero;

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

                // Set the mark that the combat has ended.
                hasCombatEnded = true;
            }

            // Check if the other combat unit is dead.
            if (this.otherPlayer.combatUnitInCombat.health <= 0)
            {
                // Remove the combat unit
                this.otherPlayer.team.combatUnits.Remove(this.otherPlayer.combatUnitInCombat);

                // Reset the rotation and remove shots for the surviving combat unit.
                this.hostPlayer.combatUnitInCombat.rotation = 0;
                this.hostPlayer.combatUnitInCombat.combatDirection = Vector2.Zero;
                
                // Remove all shots.
                while (this.hostPlayer.combatUnitInCombat.shots.Count > 0)
                {
                    this.hostPlayer.combatUnitInCombat.shots.RemoveAt(0);
                }

                // Set the mark.
                hasCombatEnded = true;
            }

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

        } // End of the CheckIfCombatHasEnded.

        /// <summary>
        /// Check if a game has ended.
        /// </summary>
        public override void CheckIfGameHasEnded()
        {
            
            // Check if the host player profile is dead.
            if (this.hostPlayer.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.hostPlayer.playerName;
                this.hasGameEnded = true;
            }

        } // End of the CheckIfGamHasEnded 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 hostPlayer.
                if (this.powerUps[i].CheckForCollision(this.hostPlayer.combatUnitInCombat) == true)
                {
                    // Add the power up to the player.
                    if (this.powerUps[i].isDurationInfinite == false)
                    {
                        this.powerUps[i].durationCounter = this.powerUps[i].durationFrames;
                        this.hostPlayer.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 hostplayer.
            for (int i = 0; i < this.hostPlayer.combatUnitInCombat.powerUps.Count; i++)
            {
                if (this.hostPlayer.combatUnitInCombat.powerUps[i].durationCounter <= 0)
                {
                    // Reset the power up.
                    this.hostPlayer.combatUnitInCombat.powerUps[i].ResetPowerUp(this.hostPlayer.combatUnitInCombat);

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

                }
                else
                {
                    this.hostPlayer.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.
            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);

                // 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

    } // End of class

} // End of namespace
