using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;
using Audio3D;

namespace FredriksdalAssault2
{
    /// <summary>
    /// This class is used to store data about each player in a multiplayer game so that we can
    /// draw a soldier for each player and more. Implements IComparable so that we can sort player data
    /// on number of kills.
    /// </summary>
    public class PlayerProfile : IComparable<PlayerProfile>, IAudioEmitter
    {
        #region Instance Variables

        // Gamer Id and name
        public byte playerId;
        public String playerName;
        
        // Soldier model
        public SimpleSoldier soldier;

        // Variables to assign players to different teams and to set start position.
        public bool isAttacking;

        // Collision for death
        public bool isDead = false;
        public Int16 numberOfKills = 0;
        public bool isWounded = false;

        // Shooting
        public bool isShooting = false;
        public EnemyShotParticles enemyShotParticles;

        // Spawn
        public int spawnCountDown = 10000;

        // Barrels
        public byte collectedBarrels;

        // Audio
        private Vector3 position;
        private Vector3 forward;
        private Vector3 up;
        private Vector3 velocity;

        // Walk sound delay and index
        protected int walkDelay = 470;
        protected int walkCountdown = 0;
        protected int walkSoundIndex = 0;

        #endregion

        #region Properties
        
        /// <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 PlayerProfile
        /// </summary>
        /// <param name="content"></param>
        /// <param name="gamerId"></param>
        /// <param name="gamerName"></param>
        /// <param name="attacker"></param>
        /// <param name="graphicsDevice"></param>
        public PlayerProfile(ContentManager content, byte gamerId, String gamerName, bool attacker, GraphicsDevice graphicsDevice)
        {
            // Set the id, name and attacking value for the player.
            this.playerId = gamerId;
            this.playerName = gamerName;
            this.isAttacking = attacker;

            // A string for red or black soldier
            String redOrBlack;

            // Set start position for player
            if (this.isAttacking == true)
            {
                redOrBlack = "Black";
            }
            else
            {
                redOrBlack = "Red";
            }

            // Create a new simple soldier
            this.soldier = new SimpleSoldier(content, new Vector3(0, 8000, 0), new Vector3(0, 0, 0), redOrBlack);

            // Create a new enemy shot effect.
            this.enemyShotParticles = new EnemyShotParticles(content, graphicsDevice);

        }

        #endregion

        #region Methods
        /// <summary>
        /// Update the player profile, must be implemented of classes that inherits from this class.
        /// </summary>
        /// <param name="gameTime"></param>
        public virtual void UpdatePlayerProfile(GameTime gameTime, AudioManager audioManager) { }

        /// <summary>
        /// Check if a player should be marked as alive (not dead)
        /// </summary>
        /// <param name="gameTime"></param>
        public virtual void UpdateSpawnTime(GameTime gameTime)
        {

            // Decrease the spawn time counter
            if (this.isDead == true)
            {
                this.spawnCountDown -= gameTime.ElapsedGameTime.Milliseconds;
            }

            // Check if we should set the player as alive.
            if (this.spawnCountDown <= 0)
            {
                // The player is alive and not wounded any more.
                this.isDead = false;
                this.isWounded = false;

                // Reset the spawn countdown (10 seconds)
                this.spawnCountDown = 10000;
            }

        } // End of the UpdateSpawnTime method

        /// <summary>
        /// Draw player data.
        /// </summary>
        /// <param name="view"></param>
        /// <param name="projection"></param>
        public virtual void DrawPlayerProfile(Matrix view, Matrix projection)
        {
            // Draw the simple soldier.
            this.soldier.DrawSimpleSoldier(view, projection);

        } // End of the DrawPlayerData method

        /// <summary>
        /// Draw the enemy shot effect.
        /// </summary>
        /// <param name="gameTime"></param>
        /// <param name="view"></param>
        /// <param name="projection"></param>
        public virtual void DrawShotEffect(GameTime gameTime, Matrix view, Matrix projection){}

        /// <summary>
        /// Draw a gamer tag above a 3d model, this method must be called inside spriteBatch.Begin() and spriteBatch.End().
        /// </summary>
        /// <param name="viewPort"></param>
        /// <param name="view"></param>
        /// <param name="projection"></param>
        /// <param name="spriteBatch"></param>
        /// <param name="font"></param>
        /// <param name="cameraPosition"></param>
        /// <param name="currentMap"></param>
        public virtual void DrawGamerTag(Viewport viewPort, Matrix view, Matrix projection, SpriteBatch spriteBatch,
            SpriteFont font, Vector3 cameraPosition, Vector3 cameraDirection, IMap currentMap)
        {

            // Variables
            float scale = 0;
            Vector2 textPosition = Vector2.Zero;
            BoundingSphere boundingSphere = this.soldier.boundingSpheres[1];
            Color textColor = Color.White;

            // Set the text color to black if it is a attacking player.
            if (this.isAttacking == true)
                textColor = Color.Black;

            // Create a new direction variable that should be normalized.
            Vector3 rayDirection = cameraDirection;
            rayDirection.Normalize();

            // Get the distance from the camera position to the bounding sphere of the enemy soldier.
            float? distanceToSoldier = boundingSphere.Intersects(new Ray(cameraPosition, rayDirection));

            // Draw the gamer tag, if the enemy soldier is in front of you and not behind a building.
            if (distanceToSoldier != null && currentMap.CollisionWithBuildings(new Ray(cameraPosition, rayDirection)) >= distanceToSoldier)
            {

                // Project the center of the 3D object to the screen
                Vector3 center = viewPort.Project(boundingSphere.Center + new Vector3(0, 10, 0),
                    projection, view, Matrix.Identity);

                // Set the x and y value for the text position.
                textPosition.X = center.X;
                textPosition.Y = center.Y;

                // Create a bounding box from the bounding sphere and declare variables
                // used to calculate the maxdistance.
                BoundingBox extents = BoundingBox.CreateFromSphere(boundingSphere);
                float maxdistance = 0;
                float distance;
                Vector3 screencorner;

                // Loop through each corner in the bounding box
                foreach (Vector3 corner in extents.GetCorners())
                {
                    // Project the corner to the screen.
                    screencorner = viewPort.Project(corner, projection, view, Matrix.Identity);

                    // Get the distance
                    distance = Vector3.Distance(screencorner, center);

                    if (distance > maxdistance)
                        maxdistance = distance;
                }

                // Scale the text using the max distance.
                scale = maxdistance / 120;

                // Draw the gamer tag.
                spriteBatch.DrawString(font, this.playerName, new Vector2(textPosition.X - viewPort.X, textPosition.Y - viewPort.Y), textColor, 0, font.MeasureString(this.playerName) / 2,
                    scale, SpriteEffects.None, 0);
            }

        } // End of the DrawGamerTag method

        /// <summary>
        /// Compare one player against another on number of kills.
        /// </summary>
        /// <param name="other"></param>
        /// <returns>An int -1, 0 or 1</returns>
        public virtual int CompareTo(PlayerProfile other)
        {
            // Sort in descending order.
            return other.numberOfKills.CompareTo(this.numberOfKills);
        }

        /// <summary>
        /// A method to get a random start position for a player.
        /// </summary>
        /// <returns name="startPosition">A 3D position</returns>
        public virtual Vector3 getAssaultStartPosition()
        {
            // Variables
            Vector3 startPosition = new Vector3(0, 0, 0);
            Random rndGenerator = new Random();
            int randomValueX = 0;
            int randomValueZ = 0;

            if (this.isAttacking == false)
            {
                randomValueX = (int)(rndGenerator.NextDouble() * 11800 - 5900);
                randomValueZ = (int)(rndGenerator.NextDouble() * 2900 + 3000);

                // Set the start position.
                startPosition = new Vector3(randomValueX, 0, randomValueZ);
            }
            if (this.isAttacking == true)
            {
                randomValueX = (int)(rndGenerator.NextDouble() * 11800 - 5900);
                randomValueZ = (int)(rndGenerator.NextDouble() * -2900 - 3000);

                // Set the start position.
                startPosition = new Vector3(randomValueX, 0, randomValueZ);
            }

            // Return the startposition.
            return startPosition;

        } // End of the getAssaultStartPosition method

        /// <summary>
        /// A method to get a random start position for a player in the deathmatch mode.
        /// </summary>
        /// <returns name="startPosition">A 3D position</returns>
        public virtual Vector3 getDeathMatchStartPosition()
        {
            // Variables
            Vector3 startPosition = new Vector3(0, 0, 0);
            Random rndGenerator = new Random();
            int randomValueX = 0;
            int randomValueZ = 0;

            // The startposition can be anywhere on the map.
            randomValueX = (int)(rndGenerator.NextDouble() * 11800 - 5900);
            randomValueZ = (int)(rndGenerator.NextDouble() * 11800 - 5900);

            // Set the start position.
            startPosition = new Vector3(randomValueX, 0, randomValueZ);

            // Return the startposition.
            return startPosition;

        } // End of the getDeathMatchStartPosition

        #endregion

    } // End of class

} // End of namespace
