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

namespace MegaManRipoff.MainGameClasses
{
    /// <summary>
    /// A helicopter shooty dude that oscillates in place, occasionally shooting.
    /// </summary>
    class Tripropellan : Enemy
    {
        #region Member Variables

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

        /// <summary>
        /// The maximum health that Tripropellans can have.
        /// </summary>
        static int _maxHealth;

        /// <summary>
        /// The amount of damage that Tripropellans deal.
        /// </summary>
        static int _damage;

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

        #region Oscillation Fields

        /// <summary>
        /// The variable used to make the Pit Lurker oscillate when in the
        /// falling state.
        /// </summary>
        float _theta;

        /// <summary>
        /// Determines the amplitude of the falling oscillation.
        /// </summary>
        static float _oscillationAmplitude;

        /// <summary>
        /// Determines the frequency of the falling oscillation, as a
        /// divisor of 1 radian (2 * pi).
        /// </summary>
        static float _oscillationFrequency;

        #endregion

        #region Shooting Fields

        /// <summary>
        /// A list that holds the position vector to create the bullets, relative to the current
        /// position, and the speed vector of the bullets to be shot.
        /// </summary>
        static List<Tuple<Vector2, Vector2>> _bulletOrigins;

        /// <summary>
        /// The position through the bullet origins list.
        /// </summary>
        int _index;

        /// <summary>
        /// Keeps track of how long the Tripropellan should wait before shooting, in ticks.
        /// </summary>
        int _timer;

        /// <summary>
        /// The length of time to wait between each bullet shot, in ticks.
        /// </summary>
        static int _betweenShotsDuration;

        /// <summary>
        /// The length of time to wait between each round of bullets, in ticks.
        /// </summary>
        static int _betweenRoundsDuration;

        /// <summary>
        /// The speed of the bullets created.
        /// </summary>
        static int _bulletSpeed;

        /// <summary>
        /// The damage of the bullets created.
        /// </summary>
        static int _bulletDamage;

        #endregion

        #endregion

        #region Properties

        /// <summary>
        /// Gets this Tripropellan's hitbox.
        /// </summary>
        public override Rectangle Hitbox
        {
            get { return new Rectangle((int)_position.X - 16, (int)_position.Y, 32, 49); }
        }

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

        #endregion

        public Tripropellan(Room room, Vector2 position)
            : base(room, position)
        {
            _health = _maxHealth;
            _timer = _betweenShotsDuration;
        }

        #region Methods

        /// <summary>
        /// Initialises the Tripropellan's static properties according to the difficulty setting.
        /// </summary>
        /// <param name="gameDifficulty">The game's current difficulty setting.</param>
        new public static void Initialise(GameDifficulty gameDifficulty)
        {
            switch (gameDifficulty)
            {
                case GameDifficulty.Easy:
                    _oscillationAmplitude = 2;
                    _oscillationFrequency = 110;
                    _betweenShotsDuration = 45;
                    _betweenRoundsDuration = 100;
                    _bulletSpeed = 3;
                    _bulletDamage = 2;
                    break;
                case GameDifficulty.Hard:
                    _oscillationAmplitude = 3;
                    _oscillationFrequency = 60;
                    _betweenShotsDuration = 20;
                    _betweenRoundsDuration = 80;
                    _bulletSpeed = 4;
                    _bulletDamage = 1;
                    break;
                default:
                    _oscillationAmplitude = 3;
                    _oscillationFrequency = 70;
                    _betweenShotsDuration = 30;
                    _betweenRoundsDuration = 80;
                    _bulletSpeed = 4;
                    _bulletDamage = 2;
                    break;
            }
            //Initialise and add to the list of bullet origins.
            _bulletOrigins = new List<Tuple<Vector2, Vector2>>();
            _bulletOrigins.Add(new Tuple<Vector2, Vector2>(
                new Vector2(-12, 26),
                EntityHelper.RadialToVector(_bulletSpeed, MathHelper.ToRadians(180))));
            _bulletOrigins.Add(new Tuple<Vector2, Vector2>(
                new Vector2(-11, 38),
                EntityHelper.RadialToVector(_bulletSpeed, MathHelper.ToRadians(146))));
            _bulletOrigins.Add(new Tuple<Vector2, Vector2>(
                new Vector2(-7, 46),
                EntityHelper.RadialToVector(_bulletSpeed, MathHelper.ToRadians(112))));

            _maxHealth = 3;
            _damage = 3;
        }

        /// <summary>
        /// Initialises this Tripropellan's animations.
        /// </summary>
        protected override void InitialiseAnimations()
        {
            int animationSpeed = 4;

            _animation = new Animation(new Tuple<Rectangle, Vector2, int>(
                new Rectangle(0, 0, 32, 49), new Vector2(-16, 0), animationSpeed), 0);
            _animation.AddFrame(new Tuple<Rectangle, Vector2, int>(
                new Rectangle(35, 0, 32, 49), new Vector2(-16, 0), animationSpeed));
            _animation.AddFrame(new Tuple<Rectangle, Vector2, int>(
                new Rectangle(70, 0, 32, 49), new Vector2(-16, 0), animationSpeed));
            _animation.AddFrame(new Tuple<Rectangle, Vector2, int>(
                new Rectangle(105, 0, 32, 49), new Vector2(-16, 0), animationSpeed));
            _animation.AddFrame(new Tuple<Rectangle, Vector2, int>(
                new Rectangle(139, 0, 32, 49), new Vector2(-16, 0), animationSpeed));
        }

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

        /// <summary>
        /// Causes the Tripropellan to bob up and down.
        /// </summary>
        private void Oscillate()
        {
            //Increase theta, and change the vertical position accordingly.
            _theta += (float)(2 * Math.PI) / _oscillationFrequency;
            _position.Y += _oscillationAmplitude * (float)Math.Sin(_theta);
        }

        /// <summary>
        /// Causes the Tripropellan to shoot.
        /// </summary>
        private void Shooting()
        {
            //If the Tripropellan is outside the camera view, reset.
            if (!_room.Camera.IsInView(Hitbox))
            {
                _timer = _betweenRoundsDuration;
                _index = 0;
            }
            else
            {
                //Decrement the timer; if it is now zero, shoot.
                if (--_timer <= 0)
                {
                    //Get the position vector and speed vector of the bullet from the current
                    //position in the bullets origin list.
                    Tuple<Vector2, Vector2> tuple = _bulletOrigins[_index];

                    //Create the bullet.
                    _room.Add(new BasicBullet(_room,
                                              _position + new Vector2((tuple.Item1.X - 4) * -(int)_direction,
                                                                      tuple.Item1.Y - 3),
                                              this,
                                              _bulletDamage,
                                              new Vector2(tuple.Item2.X * -(int)_direction,
                                                          tuple.Item2.Y),
                                              false));

                    //Play a sound effect.
                    ShootSound.Play();

                    //Increment the index, if it is now greater than the length of the list, reset
                    //back to the start.    
                    if (++_index >= _bulletOrigins.Count)
                    {
                        _index = 0;
                        _timer = _betweenRoundsDuration;
                    }
                    //Otherwise, be ready to shoot the next bullet.
                    else
                    {
                        _timer = _betweenShotsDuration;
                    }
                }
            }
        }

        /// <summary>
        /// Updates the Tripropellan.
        /// </summary>
        /// <param name="gameTime">The current snapshot of time.</param>
        public override void Update(GameTime gameTime)
        {
            Oscillate();
            Shooting();

            //Update visuals.
            _animation.Update();
            FacePlayer();

            base.Update(gameTime);
        }

        /// <summary>
        /// Draws the Tripropellan 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)
            {
                //Get the necessary details from the current animation frame.
                Rectangle currentFrameSource = _animation.CurrentFrame.Item1;
                Vector2 currentFrameOffset = _animation.CurrentFrame.Item2;

                //Decide how to flip the Metool based on its direction.
                SpriteEffects directionEffects;
                if (_direction == Direction.Right)
                    directionEffects = SpriteEffects.FlipHorizontally;
                else
                    directionEffects = SpriteEffects.None;

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

        #endregion
    }
}
