using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using StrategicCombat.Teams;
using StrategicCombat.CombatUnits;
using StrategicCombat.Maps;

namespace StrategicCombat.PlayerProfiles
{
    /// <summary>
    /// This class represent a computer player profile
    /// </summary>
    public class ComputerPlayerProfile : PlayerProfile
    {
        #region Variables

        // A random generator.
        private Random randomGenerator;
        
        // Movement variables
        public Point selectedMovementPosition;
        public CombatUnit selectedCombatUnit;
        public Vector2 movementVector;

        // Difficulty counter (number of frames)
        public int difficultyCounter = 0;
        public int difficultyDelay;

        // Computer on board movement delay (number of frames).
        public int boardDelayCounter = 120;
        public int boardMovementDelay = 120;

        #endregion

        #region Constructors

        /// <summary>
        /// Create a new human player profile
        /// </summary>
        /// <param name="playerId"></param>
        /// <param name="playerName"></param>
        /// <param name="hasTurn"></param>
        /// <param name="team"></param>
        public ComputerPlayerProfile(int playerId, String playerName, bool hasTurn, Team team)
            : base(playerId, playerName, hasTurn, team)
        {
            // Create a new random generator.
            this.randomGenerator = new Random();

            // Generate a random x and y value for the movement vector.
            float xValue = (float)this.randomGenerator.NextDouble();
            float yValue = (float)this.randomGenerator.NextDouble();

            // Set the movement vector and normalize it.
            this.movementVector = new Vector2(xValue, yValue);
            this.movementVector.Normalize();

            // Set the difficulty shot delay.
            if (Program.difficulty == 0)
                this.difficultyDelay = 30;
            else if (Program.difficulty == 1)
                this.difficultyDelay = 15;
            else if (Program.difficulty == 2)
                this.difficultyDelay = 0;

        }
        
        #endregion

        #region Methods

        /// <summary>
        /// Computer player movement on the board.
        /// </summary>
        /// <param name="playerIndex"></param>
        /// <param name="currentMap"></param>
        /// <param name="otherPlayer"></param>
        public override bool PlayerMovementOnBoard(PlayerIndex playerIndex, Map currentMap, PlayerProfile otherPlayer)
        {

            // Variables.
            bool canBeMoved = true;
            bool oneMovementPossible = false;
            
            // Possible positions to move to.
            List<Vector2> possiblePositions = new List<Vector2>(currentMap.numberOfxTiles * currentMap.numberOfyTiles);
            
            // Have a movement delay for the computer
            if (this.boardDelayCounter <= 0)
            {
                while (oneMovementPossible == false)
                {
                    // Select the combat unit to move at random.
                    int choosenCombatUnit = this.randomGenerator.Next(this.team.combatUnits.Count);

                    // Set the selected combat unit.
                    this.selectedCombatUnit = this.team.combatUnits[choosenCombatUnit];

                    // Can the selected combat unit be moved.
                    for (int x = 0 - (int)this.selectedCombatUnit.movementRestriction.X; x <= this.selectedCombatUnit.movementRestriction.X; x++)
                    {
                        for (int y = 0 - (int)this.selectedCombatUnit.movementRestriction.Y; y <= this.selectedCombatUnit.movementRestriction.Y; y++)
                        {
                            // Reset the can be moved variable.
                            canBeMoved = true;

                            // If the movement is inside of the map, and there not is a collision with a combat unit in the same team.
                            Vector2 possibleMovement = new Vector2(this.selectedCombatUnit.currentPosition.X + x, this.selectedCombatUnit.currentPosition.Y + y);

                            // Check if the movement is inside of the map.
                            if (possibleMovement.X >= 0 && possibleMovement.X <= currentMap.numberOfxTiles - 1 && possibleMovement.Y >= 0 && possibleMovement.Y <= currentMap.numberOfyTiles - 1)
                            {
                                // The movement is inside of the map, check for a collision with combat units in the same team.
                                foreach (CombatUnit combatUnit in this.team.combatUnits)
                                {
                                    if (combatUnit.boardCollisionRectangle.Intersects(currentMap.GetMapRectangle(possibleMovement)))
                                    {
                                        canBeMoved = false;
                                    }
                                }

                                // There was no collision with a combat unit in the same team.
                                if (canBeMoved == true)
                                {
                                    possiblePositions.Add(possibleMovement);
                                    oneMovementPossible = true;
                                }

                            }
                        }
                    }

                } // End of the while block.

                // Set the new position
                int newPosition = this.randomGenerator.Next(possiblePositions.Count);

                // Move the combat unit to the new location
                this.selectedCombatUnit.currentPosition = possiblePositions[newPosition];

                // Reset the selection of combat unit to null.
                this.selectedCombatUnit = null;

                // Set the delay counter to the movement delay.
                this.boardDelayCounter = this.boardMovementDelay;

                // Change the turn, give the other player the right to move.
                this.hasTurn = false;
                otherPlayer.hasTurn = true;

            }
            else
            {
                // Decrease one frame on the board delay counter.
                this.boardDelayCounter--;
            }

            // Just return a value to satisfy the methods return type.
            return false;

        } // End of the PlayerMovementOnBoard method

        /// <summary>
        /// Computer player movement in a combat.
        /// </summary>
        /// <param name="viewPort"></param>
        /// <param name="playerIndex"></param>
        /// <param name="otherPlayer"></param>
        /// <param name="currentCombatScene"></param>
        public override void PlayerMovementInCombat(Viewport viewPort, PlayerIndex playerIndex, PlayerProfile otherPlayer, CombatScene currentCombatScene)
        {
            // ******************************************************************************
            // Free movement
            // Chasing movement (stop when close to enemy) (Plane, flame thrower)
            // Evading movement (President, Tank)
            // ******************************************************************************

            // Changes the movement vector depending on difficulty.
            if (Program.difficulty == 0)
            {
                // Easy difficulty (number of frames)
                if (this.difficultyCounter >= 1200)
                {
                    // Generate a random x and y value for the movement vector.
                    float xValue = (float)this.randomGenerator.NextDouble();
                    float yValue = (float)this.randomGenerator.NextDouble();

                    // Set the movement vector and normalize it.
                    this.movementVector = new Vector2(xValue, yValue);
                    this.movementVector.Normalize();

                    // Reset the difficulty counter.
                    this.difficultyCounter = 0;
                }
                else
                {
                    this.difficultyCounter++;
                }
            }
            // Changes the movement vector depending on difficulty.
            if (Program.difficulty == 1)
            {
                // Medium difficulty (number of frames)
                if (this.difficultyCounter >= 600)
                {
                    // Generate a random x and y value for the movement vector.
                    float xValue = (float)this.randomGenerator.NextDouble();
                    float yValue = (float)this.randomGenerator.NextDouble();

                    // Set the movement vector and normalize it.
                    this.movementVector = new Vector2(xValue, yValue);
                    this.movementVector.Normalize();

                    // Reset the difficulty counter.
                    this.difficultyCounter = 0;
                }
                else
                {
                    this.difficultyCounter++;
                }
            }
            if (Program.difficulty == 2)
            {
                // Hard difficulty (number of frames)
                if (this.difficultyCounter >= 300)
                {
                    // Generate a random x and y value for the movement vector.
                    float xValue = (float)this.randomGenerator.NextDouble();
                    float yValue = (float)this.randomGenerator.NextDouble();

                    // Set the movement vector and normalize it.
                    this.movementVector = new Vector2(xValue, yValue);
                    this.movementVector.Normalize();

                    // Reset the difficulty counter.
                    this.difficultyCounter = 0;
                }
                else
                {
                    this.difficultyCounter++;
                }
            }

            // Get the direction to the human player combat unit.
            Vector2 direction = otherPlayer.combatUnitInCombat.combatPosition - this.combatUnitInCombat.combatPosition;

            // Normalize the direction.
            direction.Normalize();

            // Rotate the computer players combat unit.
            float perpDot = (this.combatUnitInCombat.startDirectionVector.X * direction.Y) - (this.combatUnitInCombat.startDirectionVector.Y * direction.X);

            // Set the rotation.
            this.combatUnitInCombat.rotation = (float)Math.Atan2(perpDot, Vector2.Dot(this.combatUnitInCombat.startDirectionVector, direction));
            
            // Set the direction.
            this.combatUnitInCombat.combatDirection = direction;

            // Make the movement
            if (this.combatUnitInCombat is RightPlane)
            {
                Vector2 otherPlayerPosition = otherPlayer.combatUnitInCombat.combatPosition;
                Rectangle otherPlayerCollisionRect = new Rectangle((int)otherPlayerPosition.X - 1, (int)otherPlayerPosition.Y - 1, 2, 2);
                Rectangle CollisiontRect = new Rectangle((int)this.combatUnitInCombat.combatPosition.X - 2, (int)this.combatUnitInCombat.combatPosition.Y - 2, 4, 4);

                // Move the combat unit to the enemy
                if (!CollisiontRect.Intersects(otherPlayerCollisionRect))
                {
                    if (this.combatUnitInCombat.shotDelayCounter <= 0)
                    {
                        this.combatUnitInCombat.combatPosition += direction * this.combatUnitInCombat.movementSpeed;
                    }
                    else
                    {
                        this.combatUnitInCombat.combatPosition += this.movementVector * this.combatUnitInCombat.movementSpeed;
                        this.combatUnitInCombat.shotDelayCounter--;

                        // Check if the combat unit is outside of the map, make adjustments to the movement.
                        if (this.combatUnitInCombat.combatPosition.X - (this.combatUnitInCombat.frameSizeCombat.X / 2) < currentCombatScene.combatSceneRectangle.Left ||
                            this.combatUnitInCombat.combatPosition.X + (this.combatUnitInCombat.frameSizeCombat.X / 2) > currentCombatScene.combatSceneRectangle.Right)
                            this.movementVector.X *= -1;
                        if (this.combatUnitInCombat.combatPosition.Y - (this.combatUnitInCombat.frameSizeCombat.Y / 2) < currentCombatScene.combatSceneRectangle.Top ||
                            this.combatUnitInCombat.combatPosition.Y + (this.combatUnitInCombat.frameSizeCombat.Y / 2) > currentCombatScene.combatSceneRectangle.Bottom)
                            this.movementVector.Y *= -1;
                    }
                }
                else
                {
                    // Make the computer combat unit shot.
                    if (this.combatUnitInCombat.shotDelayCounter <= 0)
                    {
                        this.combatUnitInCombat.AddShot();
                        this.combatUnitInCombat.shotDelayCounter = this.combatUnitInCombat.shotDelay;
                        
                        // Play the shot sound
                        this.combatUnitInCombat.PlayShootingSound();
                    }
                    else
                    {
                        this.combatUnitInCombat.combatPosition += this.movementVector * this.combatUnitInCombat.movementSpeed;
                        this.combatUnitInCombat.shotDelayCounter--;
                    }
                }
            }
            else if (this.combatUnitInCombat is LeftFlameThrower)
            {

                // Move the combat unit to the enemy
                if (!this.combatUnitInCombat.combatCollisionRectangle.Intersects(otherPlayer.combatUnitInCombat.combatCollisionRectangle))
                {
                    if (this.combatUnitInCombat.shotDelayCounter <= 0)
                    {
                        this.combatUnitInCombat.combatPosition += direction * this.combatUnitInCombat.movementSpeed;
                    }
                    else
                    {
                        this.combatUnitInCombat.combatPosition += this.movementVector * this.combatUnitInCombat.movementSpeed;
                        this.combatUnitInCombat.shotDelayCounter--;

                        // Check if the combat unit is outside of the map, make adjustments to the movement.
                        if (this.combatUnitInCombat.combatPosition.X - (this.combatUnitInCombat.frameSizeCombat.X / 2) < currentCombatScene.combatSceneRectangle.Left ||
                            this.combatUnitInCombat.combatPosition.X + (this.combatUnitInCombat.frameSizeCombat.X / 2) > currentCombatScene.combatSceneRectangle.Right)
                            this.movementVector.X *= -1;
                        if (this.combatUnitInCombat.combatPosition.Y - (this.combatUnitInCombat.frameSizeCombat.Y / 2) < currentCombatScene.combatSceneRectangle.Top ||
                            this.combatUnitInCombat.combatPosition.Y + (this.combatUnitInCombat.frameSizeCombat.Y / 2) > currentCombatScene.combatSceneRectangle.Bottom)
                            this.movementVector.Y *= -1;
                    }

                    // Check if the combat unit collides with a barrier.
                    foreach (Rectangle rectangle in currentCombatScene.barriers)
                    {
                        if (this.combatUnitInCombat.combatCollisionRectangle.Intersects(rectangle))
                        {
                            // Check if the collision is with the left, right, top or bottom side of the rectangle.
                            if (this.combatUnitInCombat.combatPosition.X < rectangle.Left)
                            {
                                this.combatUnitInCombat.combatPosition.X = rectangle.Left - (this.combatUnitInCombat.frameSizeCombat.X / 2);
                            }
                            else if (this.combatUnitInCombat.combatPosition.X > rectangle.Right)
                            {
                                this.combatUnitInCombat.combatPosition.X = rectangle.Right + (this.combatUnitInCombat.frameSizeCombat.X / 2);
                            }
                            else if (this.combatUnitInCombat.combatPosition.Y < rectangle.Top)
                            {
                                this.combatUnitInCombat.combatPosition.Y = rectangle.Top - (this.combatUnitInCombat.frameSizeCombat.X / 2);
                            }
                            else if (this.combatUnitInCombat.combatPosition.Y > rectangle.Bottom)
                            {
                                this.combatUnitInCombat.combatPosition.Y = rectangle.Bottom + (this.combatUnitInCombat.frameSizeCombat.X / 2);
                            }

                            // Make the computer combat unit shot.
                            if (this.combatUnitInCombat.shotDelayCounter <= 0)
                            {
                                this.combatUnitInCombat.AddShot();
                                this.combatUnitInCombat.shotDelayCounter = this.combatUnitInCombat.shotDelay;

                                // Play the shot sound
                                this.combatUnitInCombat.PlayShootingSound();
                            }

                            // Break out from the loop
                            break;
                        }
                    }
                }
                else
                {
                    // Make the computer combat unit shot.
                    if (this.combatUnitInCombat.shotDelayCounter <= 0)
                    {
                        this.combatUnitInCombat.AddShot();
                        this.combatUnitInCombat.shotDelayCounter = this.combatUnitInCombat.shotDelay;

                        // Play the shot sound
                        this.combatUnitInCombat.PlayShootingSound();
                    }
                    else
                    {
                        this.combatUnitInCombat.combatPosition += this.movementVector * this.combatUnitInCombat.movementSpeed;
                        this.combatUnitInCombat.shotDelayCounter--;
                    }
                }
            }
            else
            {
                // Move the combat unit
                this.combatUnitInCombat.combatPosition += this.movementVector * this.combatUnitInCombat.movementSpeed;

                // Check if the combat unit is outside of the map, make adjustments to the movement.
                if (this.combatUnitInCombat.combatPosition.X - (this.combatUnitInCombat.frameSizeCombat.X / 2) < currentCombatScene.combatSceneRectangle.Left ||
                    this.combatUnitInCombat.combatPosition.X + (this.combatUnitInCombat.frameSizeCombat.X / 2) > currentCombatScene.combatSceneRectangle.Right)
                    this.movementVector.X *= -1;
                if (this.combatUnitInCombat.combatPosition.Y - (this.combatUnitInCombat.frameSizeCombat.Y / 2) < currentCombatScene.combatSceneRectangle.Top ||
                    this.combatUnitInCombat.combatPosition.Y + (this.combatUnitInCombat.frameSizeCombat.Y / 2) > currentCombatScene.combatSceneRectangle.Bottom)
                    this.movementVector.Y *= -1;

                // Check if the combat unit collides with a x barrier.
                if (!(this.combatUnitInCombat is LeftPlane))
                {
                    foreach (Rectangle rectangle in currentCombatScene.barriers)
                    {
                        if (this.combatUnitInCombat.combatCollisionRectangle.Intersects(rectangle))
                        {
                            // Check if the collision is with the left, right, top or bottom side of the rectangle.
                            if (this.combatUnitInCombat.combatPosition.X < rectangle.Left)
                            {
                                this.combatUnitInCombat.combatPosition.X = rectangle.Left - (this.combatUnitInCombat.frameSizeCombat.X / 2);
                            }
                            else if (this.combatUnitInCombat.combatPosition.X > rectangle.Right)
                            {
                                this.combatUnitInCombat.combatPosition.X = rectangle.Right + (this.combatUnitInCombat.frameSizeCombat.X / 2);
                            }
                            else if (this.combatUnitInCombat.combatPosition.Y < rectangle.Top)
                            {
                                this.combatUnitInCombat.combatPosition.Y = rectangle.Top - (this.combatUnitInCombat.frameSizeCombat.X / 2);
                            }
                            else if (this.combatUnitInCombat.combatPosition.Y > rectangle.Bottom)
                            {
                                this.combatUnitInCombat.combatPosition.Y = rectangle.Bottom + (this.combatUnitInCombat.frameSizeCombat.X / 2);
                            }

                            // Break out from the loop
                            break;
                        }
                    }
                }

                // Make the computer combat unit shot.
                if (this.combatUnitInCombat.shotDelayCounter <= 0)
                {

                    this.combatUnitInCombat.AddShot();
                    this.combatUnitInCombat.shotDelayCounter = this.combatUnitInCombat.shotDelay + this.difficultyDelay;

                    // Play the shot sound
                    this.combatUnitInCombat.PlayShootingSound();
                }
                else
                {
                    this.combatUnitInCombat.shotDelayCounter--;
                }
            }

            // Check if we should play a movement sound.
            if (this.combatUnitInCombat.movementDelayCounter <= 0)
            {
                // Play movement sound.
                this.combatUnitInCombat.PlayMovementSound();
                this.combatUnitInCombat.movementDelayCounter = this.combatUnitInCombat.movementDelay;

            }
            else
            {
                this.combatUnitInCombat.movementDelayCounter--;
            }


        } // End of the PlayerMovementInCombat method.

        #endregion

    } // End of class

} // End of namespace
