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

namespace MegaManRipoff.MainGameClasses
{
    /// <summary>
    /// A Pipi baby. It homes in on the player, and its small hitbox
    /// makes it a very annoying foe. However, they're very cute.
    /// </summary>
    class PipiBaby : Enemy
    {
        #region Member Variables

        /// <summary>
        /// The texture for Pipi babies.
        /// </summary>
        static Texture2D _texture;

        /// <summary>
        /// The Pipi babies's maximum health.
        /// </summary>
        static int _maxHealth;

        /// <summary>
        /// The amount of damage Pipi babies deal.
        /// </summary>
        static int _damage;

        /// <summary>
        /// The Pipi baby's animation.
        /// </summary>
        Animation _animation;

        /// <summary>
        /// The Pipi baby's current rate of change of velocity.
        /// </summary>
        float _accel;

        /// <summary>
        /// The rate of change of velocity for Pipi babies that is used for a
        /// brief time after they've spawned.
        /// </summary>
        static float _initialAccel;

        /// <summary>
        /// The rate of change of velocity for Pipi babies once they've flown for
        /// a while.
        /// </summary>
        static float _finalAccel;

        /// <summary>
        /// Keeps track of how long the Pipi baby should remain using the initial
        /// acceleration value for in ticks.
        /// </summary>
        int _initialAccelTimer;

        /// <summary>
        /// The amount of time to use the intial acceleration value for, in ticks.
        /// </summary>
        const int InitialAccelDuration = 20;

        /// <summary>
        /// The Pipi baby's maximum speed.
        /// </summary>
        static float _maxSpeed;

        /// <summary>
        /// Determines how close the Pipi baby can get to the edge of the screen
        /// before trying to accelerate away. By using large negative numbers, the
        /// Pipi baby won't try to keep itself in the view.
        /// </summary>
        static int _keepInViewThreshold;

        /// <summary>
        /// Keeps track of the time, in ticks, to wait before homing in
        /// on the player.
        /// </summary>
        int _accelTimer;

        #endregion

        #region Properites

        /// <summary>
        /// Gets the amount of damage Pipi babies deal.
        /// </summary>
        public override int Damage
        {
            get { return _damage; }
        }

        /// <summary>
        /// The Pipi baby's hitbox.
        /// </summary>
        public override Rectangle Hitbox
        {
            get { return new Rectangle((int)_position.X - 7, (int)_position.Y - 5, 15, 10); }
        }

        #endregion

        /// <summary>
        /// Creates a new Pipi baby.
        /// </summary>
        /// <param name="room">The current room.</param>
        /// <param name="position">The position to create the Pipi baby at.</param>
        /// <param name="speed">The Pipi baby's speed vector.</param>
        public PipiBaby(Room room, Vector2 position, Vector2 speed, int accelTimer)
            : base(room, position)
        {
            _speed = speed;
            _accelTimer = accelTimer;

            _health = _maxHealth;
            _direction = Direction.Left;
            _initialAccelTimer = InitialAccelDuration;

            //Set an invincibility timer to stop the babies being killed immediately
            //as they spawn from the egg (as they all spawn in roughly the same place).
            //This is what you call a _dick move_.
            _invincibiltyTimer = 8;
        }

        #region Methods

        /// <summary>
        /// Initialises fields for the Pipi baby, dependent on the game's difficulty.
        /// </summary>
        /// <param name="gameDifficulty">The game's difficulty setting.</param>
        new public static void Initialise(GameDifficulty gameDifficulty)
        {
            switch (gameDifficulty)
            {
                case GameDifficulty.Easy:
                    _initialAccel = 0.12f;
                    _finalAccel = 0.005f;
                    _maxSpeed = 3;
                    _keepInViewThreshold = -100;
                    break;
                case GameDifficulty.Hard:
                    _initialAccel = 0.12f;
                    _finalAccel = 0.08f;
                    _maxSpeed = 5.5f;
                    _keepInViewThreshold = 24;
                    break;
                default:
                    _initialAccel = 0.1f;
                    _finalAccel = 0.03f;
                    _maxSpeed = 4;
                    _keepInViewThreshold = 24;
                    break;
            }
            _maxHealth = 1;
            _damage = 1;
        }

        /// <summary>
        /// Initialises the animation for the Pipi baby.
        /// </summary>
        protected override void InitialiseAnimations()
        {
            //The only animation.
            _animation = new Animation(new Tuple<Rectangle, Vector2, int>(
                new Rectangle(0, 0, 15, 10), new Vector2(-7, -5), 5), 0);
            _animation.AddFrame(new Tuple<Rectangle, Vector2, int>(
                new Rectangle(15, 0, 18, 10), new Vector2(-7, -5), 5));
            _animation.AddFrame(new Tuple<Rectangle, Vector2, int>(
                new Rectangle(33, 0, 17, 10), new Vector2(-7, -5), 5));
        }

        /// <summary>
        /// Loads the assets for the Pipi egg.
        /// </summary>
        /// <param name="content">The game's content manager.</param>
        new public static void LoadContent(ContentManager content)
        {
            _texture = content.Load<Texture2D>("Images\\PipiBaby");
        }

        /// <summary>
        /// Changes the use of the initial acceleration to the final acceleration if
        /// the time for using the initial acceleration has expired.
        /// </summary>
        private void ChangeAcceleration()
        {
            //Decrement the timer; if it is now zero, use the final acceleration. Otherwise,
            //use the intial acceleration.
            if (--_initialAccelTimer <= 0)
                _accel = _finalAccel;
            else
                _accel = _initialAccel;
        }

        /// <summary>
        /// Detects the edges of the camera and attempts to accelerate away from
        /// them, back into the center of the camera.
        /// </summary>
        private void KeepInView()
        {
            //If too far to the left, accelerate rightwards.
            if (_position.X < _room.Camera.Area.Left + _keepInViewThreshold)
                _speed.X += _finalAccel;

            //If too far to the right, accelerate leftwards.
            if (_position.X > _room.Camera.Area.Right - _keepInViewThreshold)
                _speed.X -= _finalAccel;

            //I think you get the idea.
            if (_position.Y < _room.Camera.Area.Top + _keepInViewThreshold)
                _speed.Y += _finalAccel;

            if (_position.Y > _room.Camera.Area.Bottom - _keepInViewThreshold)
                _speed.Y -= _finalAccel;
        }

        /// <summary>
        /// Causes the Pipi baby to fly towards the player.
        /// </summary>
        private void AttackPlayer()
        {
            //Hold the position to attack.
            Vector2 target;

            //If the player is alive, attack him.
            if (_room.PlayerOne.CurrentState != Player.State.Dead
                && _room.PlayerOne.CurrentState != Player.State.Dying)
            {
                target = new Vector2(_room.PlayerOne.Hitbox.Center.X,
                                             _room.PlayerOne.Hitbox.Center.Y);
            }
            //Otherwise, "attack" a point to the left and upwards. This will make
            //it look like the birds are flying away after their victorious bout.
            else
            {
                target = new Vector2(-1000, -1000);
            }

            //If the target is to the right, accelerate the speed vector to the left. Otherwise,
            //accelerate it to the left.
            if (_position.X < target.X)
                _speed.X += _accel;
            else
                _speed.X -= _accel;

            //Do a similar thing for the y-axis.
            if (_position.Y < target.Y)
                _speed.Y += _accel;
            else
                _speed.Y -= _accel;

            //Clamp the speed vector to the maximum speed.
            if (Math.Abs(_speed.X) > Math.Abs(Vector2.Normalize(_speed).X * _maxSpeed))
                _speed.X = Vector2.Normalize(_speed).X * _maxSpeed;
            if (Math.Abs(_speed.Y) > Math.Abs(Vector2.Normalize(_speed).Y * _maxSpeed))
                _speed.Y = Vector2.Normalize(_speed).Y * _maxSpeed;
        }

        /// <summary>
        /// Updates the Pipi baby.
        /// </summary>
        /// <param name="gameTime">The current snapshot of time.</param>
        public override void Update(GameTime gameTime)
        {
            //Accelerate if the timer has expired.
            if (--_accelTimer <= 0)
            {
                ChangeAcceleration();
                AttackPlayer();
            }

            //Attempt to keep in the bounds of the camera.
            if (_room.PlayerOne.CurrentState != Player.State.Dead)
                KeepInView();

            //Apply speed.
            _position += _speed;

            //Change the direction according to the speed.
            if (_speed.X > 0)
                _direction = Direction.Right;
            else
                _direction = Direction.Left;

            //Remove this object if it moves outside the camera view.
            if (!_room.Camera.IsInView(Hitbox))
                _room.Remove(this);

            //Update animation and call the base class update method.
            _animation.Update();
            base.Update(gameTime);
        }

        /// <summary>
        /// Draws the Pipi egg to the screen.
        /// </summary>
        /// <param name="spriteBatch">The sprite batch to draw to.</param>
        public override void Draw(SpriteBatch spriteBatch)
        {
            //Account for post-hit invincibility flashing.
            if ((_invincibiltyTimer / 2) % 2 == 0)
            {
                //Flip the sprite as needed.
                SpriteEffects directionEffects;

                if (_direction == Direction.Left)
                    directionEffects = SpriteEffects.None;
                else
                    directionEffects = SpriteEffects.FlipHorizontally;

                //Get the necessary details from the current animation frame.
                Rectangle currentFrameSource = _animation.CurrentFrame.Item1;
                Vector2 currentFrameOffset = _animation.CurrentFrame.Item2;

                spriteBatch.Draw(_texture,
                                 _position + currentFrameOffset,
                                 currentFrameSource,
                                 Color.White,
                                 0,
                                 Vector2.Zero,
                                 1,
                                 directionEffects,
                                 0);
            }
        }

        #endregion
    }
}
