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;
using Microsoft.Xna.Framework.Input;
using System.Diagnostics;
using MegaManRipoff.UI;

namespace MegaManRipoff.MainGameClasses
{
    /// <summary>
    /// A boss enemy that splits itself into pieces and travels across
    /// the room, where it reforms itself. Its eye is its weak point. This class
    /// manages everything the boss needs to function.
    /// </summary>
    class YellowDevil : Enemy, IBoss
    {
        #region Member Variables

        /// <summary>
        /// The Yellow Devil's eye's texture.
        /// </summary>
        static Texture2D _texture;

        /// <summary>
        /// Holds the states that the Yellow Devil's eye can be in.
        /// </summary>
        enum State
        {
            Closed, Travelling, Opening, Open, Shooting, Closing, Dying
        }

        /// <summary>
        /// The Yellow Devil's eye's current state.
        /// </summary>
        State _currentState;

        /// <summary>
        /// The Yellow Devil's eye's previous state.
        /// </summary>
        State _previousState;

        /// <summary>
        /// The Yellow Devil's eye's animations.
        /// </summary>
        Dictionary<State, Animation> _animationSet;

        /// <summary>
        /// The target forming position. If the Yellow Devil is facing left, this
        /// point will determine the position of the leftmost blobs, and vice versa.
        /// </summary>
        Vector2 _formingPosition;

        /// <summary>
        /// The list of this Yellow Devil's blobs.
        /// </summary>
        List<YellowDevilBlob> _blobs;

        /// <summary>
        /// Stores the order in which blobs are set to travel across the screen. The numbers
        /// stored refer to the blob's zero-based vertical index in the grid position, and the blob
        /// that is moved is the furthest on the given row.
        /// </summary>
        static readonly int[] _blobMovementOrder = { 3, 0, 1, 3, 2, 1, 1, 2, 0, 3,
                                                    2, 2, 1, 3, 1, 0, 2, 1, 2, 0 };

        /// <summary>
        /// The current index of the position in the blob movement order array.
        /// </summary>
        int _blobMovementOrderIndex;

        /// <summary>
        /// The maximum health for Yellow Devils.
        /// </summary>
        static int _maxHealth;

        /// <summary>
        /// An all-purpose timer. Its use differs depending on the current state.
        /// </summary>
        int _timer;

        /// <summary>
        /// The length of time to wait, in ticks, between each blob movement.
        /// </summary>
        static int _blobMovementDuration;

        /// <summary>
        /// Defines the area in which the eye could appear randomly.
        /// </summary>
        static Rectangle _eyePlacementArea;

        /// <summary>
        /// The amount of health the Yellow Devil must have less than before it uses
        /// the eye placement area.
        /// </summary>
        static int _eyePlacementAreaThreshold;

        /// <summary>
        /// The length of time to wait after opening the eye before it starts shooting, in ticks.
        /// </summary>
        static int _waitAfterOpenDuration;

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

        /// <summary>
        /// The number of shots the Yellow Devil has made in the current shooting phase.
        /// </summary>
        int _numberOfShots;

        /// <summary>
        /// The maximum number of shots the Yellow Devil can make per shooting phase.
        /// </summary>
        static int _numberOfShotsMax;

        /// <summary>
        /// The speed of bullets shot by the Yellow Devil.
        /// </summary>
        static float _bulletSpeed;

        /// <summary>
        /// The length of time to wait between each blob exploding upon death, in ticks.
        /// </summary>
        const int BlobExplodeDuration = 6;

        /// <summary>
        /// The sound effect used when the Yellow Devil is destroyed.
        /// </summary>
        static SoundEffect _death;

        /// <summary>
        /// A random number generator.
        /// </summary>
        Random _random;

        #endregion

        /// <summary>
        /// The event called when the Yellow Devil is destroyed.
        /// </summary>
        public event BossDestroy OnBossDestroy;

        #region Properties

        /// <summary>
        /// Gets the Yellow Devil's eye's hitbox.
        /// </summary>
        public override Rectangle Hitbox
        {
            get
            {
                if (_currentState == State.Open || _currentState == State.Shooting)
                    return new Rectangle((int)_position.X, (int)_position.Y, 31, 15);
                else
                    return new Rectangle();
            }
        }

        /// <summary>
        /// The target forming position. If the Yellow Devil is facing left, this
        /// point will determine the position of the leftmost blobs, and vice versa.
        /// </summary>
        public Vector2 FormingPosition
        {
            get { return _formingPosition; }
        }

        /// <summary>
        /// Gets the current amount of health.
        /// </summary>
        public override int Health
        {
            get { return _health; }
            set { _health = value; }
        }

        /// <summary>
        /// Gets the maximum amount of health that the Yellow Devil can have.
        /// </summary>
        public override int MaxHealth
        {
            get { return _maxHealth; }
        }

        #endregion

        /// <summary>
        /// Creates a new Yellow Devil boss. Use with caution.
        /// </summary>
        /// <param name="room">The current room.</param>
        /// <param name="position">The position to create the Yellow Devil.</param>
        public YellowDevil(Room room, Vector2 position)
            : base(room, position)
        {
            _direction = Direction.Right;
            _formingPosition = position;

            _health = _maxHealth;
            _currentState = State.Closed;

            //Initialise lists and random number generator.
            _blobs = new List<YellowDevilBlob>();
            _random = new Random();

            //Form ourselves!!
            Form();

            //Create a boss introduction instance.
            BossIntroduction bossIntroduction = new BossIntroduction(_room, this);
        }

        #region Methods

        /// <summary>
        /// Initialises the Yellow Devil's and its blob's static fields dependent on the difficulty.
        /// </summary>
        /// <param name="gameDifficulty">The game's current difficulty setting.</param>
        new public static void Initialise(GameDifficulty gameDifficulty)
        {
            switch (gameDifficulty)
            {
                case GameDifficulty.Easy:
                    _blobMovementDuration = 45;
                    _eyePlacementArea = new Rectangle();
                    _eyePlacementAreaThreshold = 0;
                    _waitAfterOpenDuration = 45;
                    _shootDuration = 75;
                    _numberOfShotsMax = 1;
                    _bulletSpeed = 3;
                    break;
                case GameDifficulty.Hard:
                    _blobMovementDuration = 30;
                    _eyePlacementArea = new Rectangle(-8, 0, 16, 40);
                    _eyePlacementAreaThreshold = 12;
                    _waitAfterOpenDuration = 20;
                    _shootDuration = 25;
                    _numberOfShotsMax = 3;
                    _bulletSpeed = 8;
                    break;
                default:
                    _blobMovementDuration = 35;
                    _eyePlacementArea = new Rectangle(-8, 0, 16, 40);
                    _eyePlacementAreaThreshold = 8;
                    _waitAfterOpenDuration = 30;
                    _shootDuration = 35;
                    _numberOfShotsMax = 3;
                    _bulletSpeed = 5;
                    break;
            }
            _maxHealth = 28;

            //Initialise the Yellow Devil blobs.
            YellowDevilBlob.Initialise(gameDifficulty);
        }

        /// <summary>
        /// Initialises the Yellow Devil's eye's animations.
        /// </summary>
        protected override void InitialiseAnimations()
        {
            //Eye opening/closing speed.
            int eyeSpeed = 4;

            //Initialise the animation set.
            _animationSet = new Dictionary<State, Animation>();

            //Closed and travelling.
            Animation closed = new Animation(new Tuple<Rectangle, Vector2, int>(
                new Rectangle(), Vector2.Zero, 0), 0);
            _animationSet.Add(State.Closed, closed);
            _animationSet.Add(State.Travelling, closed);

            //Opening.
            Animation opening = new Animation(new Tuple<Rectangle, Vector2, int>(
                new Rectangle(124, 0, 31, 16), Vector2.Zero, eyeSpeed), 0);
            opening.AddFrame(new Tuple<Rectangle, Vector2, int>(
                new Rectangle(93, 0, 31, 16), Vector2.Zero, eyeSpeed));
            opening.AddFrame(new Tuple<Rectangle, Vector2, int>(
                new Rectangle(62, 0, 31, 16), Vector2.Zero, eyeSpeed));
            opening.AddFrame(new Tuple<Rectangle, Vector2, int>(
                new Rectangle(31, 0, 31, 16), Vector2.Zero, eyeSpeed));
            opening.AddFrame(new Tuple<Rectangle, Vector2, int>(
                new Rectangle(0, 0, 31, 16), Vector2.Zero, 0));
            _animationSet.Add(State.Opening, opening);
            opening.OnAnimationEnd += new AnimationEnd(opening_OnAnimationEnd);

            //Open, shooting and dying.
            Animation open = new Animation(new Tuple<Rectangle, Vector2, int>(
                new Rectangle(0, 0, 31, 16), Vector2.Zero, 0), 0);
            _animationSet.Add(State.Open, open);
            _animationSet.Add(State.Shooting, open);
            _animationSet.Add(State.Dying, open);

            //Closing.
            Animation closing = new Animation(new Tuple<Rectangle, Vector2, int>(
                new Rectangle(0, 0, 31, 16), Vector2.Zero, eyeSpeed), 0);
            closing.AddFrame(new Tuple<Rectangle, Vector2, int>(
                new Rectangle(31, 0, 31, 16), Vector2.Zero, eyeSpeed));
            closing.AddFrame(new Tuple<Rectangle, Vector2, int>(
                new Rectangle(62, 0, 31, 16), Vector2.Zero, eyeSpeed));
            closing.AddFrame(new Tuple<Rectangle, Vector2, int>(
                new Rectangle(93, 0, 31, 16), Vector2.Zero, eyeSpeed));
            closing.AddFrame(new Tuple<Rectangle, Vector2, int>(
                new Rectangle(124, 0, 31, 16), Vector2.Zero, 0));
            _animationSet.Add(State.Closing, closing);
            closing.OnAnimationEnd += new AnimationEnd(closing_OnAnimationEnd);
        }

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

            _death = content.Load<SoundEffect>("Sounds\\PlayerDeath");

            //Load the blob's assets.
            YellowDevilBlob.LoadContent(content);
        }

        /// <summary>
        /// Returns the opposite direction to a given direction.
        /// </summary>
        /// <param name="direction">Returns the opposite direction of the given direction.
        /// If the given direction is Direction.None, this returns Direction.None</param>
        private Direction OppositeDirection(Direction direction)
        {
            switch (direction)
            {
                case Direction.Left:
                    return Direction.Right;
                case Direction.Right:
                    return Direction.Left;
                default:
                    return Direction.None;
            }
        }

        #region Blob Operations

        /// <summary>
        /// Creates a new blob and adds it to the list of blobs and the room.
        /// </summary>
        /// <param name="blob">The blob to add.</param>
        private void AddBlob(YellowDevilBlob blob)
        {
            _blobs.Add(blob);
            _room.Add(blob);
        }

        /// <summary>
        /// Finds the formed blob that is furthest to the left/right, given its vertical index on 
        /// the blob alignment grid.
        /// </summary>
        /// <param name="direction">The direction that the formed blob is facing in.</param>
        /// <param name="verticalIndex">The zero-based vertical index of the blob.</param>
        /// <returns>Returns the instance of the blob, if a matching one was found.</returns>
        private YellowDevilBlob FindFormedBlob(Direction direction, int verticalIndex)
        {
            //Store a reference to the blob to return so that we can return null in the event that
            //no blob matches the criteria.
            YellowDevilBlob returnedBlob = null;

            //Iterate through the list and query ones that are facing in the same direction, that
            //are formed, and on the matching vertical index.
            foreach (YellowDevilBlob blob in _blobs)
            {
                if (blob.CurrentDirection == direction && blob.CurrentState == YellowDevilBlob.State.Formed
                    && blob.GridPosition.Y == verticalIndex)
                {
                    //Decide if the current blob is further away from the centre of the screen than the
                    //stored blob, if so change our reference. ...Or just skip this condition entirely if the
                    //reference is still null.
                    if (returnedBlob == null || blob.GridPosition.X >= returnedBlob.GridPosition.X)
                    {
                        //This "if" statement works  even when the Yellow Devil appears flipped because the alignment grid
                        //itself not flipped, it only _appears_ flipped because the blobs change their position based on 
                        //the Yellow Devil's direction. It's _devilishly_ clever, ohohoh. Please don't mark me down for
                        //that one.
                        returnedBlob = blob;
                    }
                }
            }

            return returnedBlob;
        }

        #endregion

        /// <summary>
        /// Creates the Yellow Devil's blob in an already-formed fashion.
        /// </summary>
        private void Form()
        {
            //Here we go...
            AddBlob(new YellowDevilBlob(_room, this, new Point(1, 0)));
            AddBlob(new YellowDevilBlob(_room, this, new Point(2, 0)));
            AddBlob(new YellowDevilBlob(_room, this, new Point(3, 0)));
            AddBlob(new YellowDevilBlob(_room, this, new Point(4, 0), new Rectangle(140, 0, 35, 32),
                                                                      new Vector2(-2, 0)));
            AddBlob(new YellowDevilBlob(_room, this, new Point(0, 1)));
            AddBlob(new YellowDevilBlob(_room, this, new Point(1, 1)));
            AddBlob(new YellowDevilBlob(_room, this, new Point(2, 1)));
            AddBlob(new YellowDevilBlob(_room, this, new Point(3, 1)));
            AddBlob(new YellowDevilBlob(_room, this, new Point(4, 1)));
            AddBlob(new YellowDevilBlob(_room, this, new Point(5, 1), new Rectangle(172, 32, 33, 32),
                                                                      new Vector2(-1, 0)));
            AddBlob(new YellowDevilBlob(_room, this, new Point(0, 2), new Rectangle(0, 64, 44, 36),
                                                                      new Vector2(-12, 0)));
            AddBlob(new YellowDevilBlob(_room, this, new Point(1, 2)));
            AddBlob(new YellowDevilBlob(_room, this, new Point(2, 2)));
            AddBlob(new YellowDevilBlob(_room, this, new Point(3, 2)));
            AddBlob(new YellowDevilBlob(_room, this, new Point(4, 2)));
            AddBlob(new YellowDevilBlob(_room, this, new Point(5, 2), new Rectangle(172, 64, 35, 38),
                                                                      new Vector2(-3, 0)));
            AddBlob(new YellowDevilBlob(_room, this, new Point(1, 3)));
            AddBlob(new YellowDevilBlob(_room, this, new Point(2, 3)));
            AddBlob(new YellowDevilBlob(_room, this, new Point(3, 3)));
            AddBlob(new YellowDevilBlob(_room, this, new Point(4, 3)));
        }

        /// <summary>
        /// Sets the Yellow Devil up to start travelling.
        /// </summary>
        private void StartTravelling()
        {
            //Flip direction and set the new forming positon based on the new direction
            _direction = OppositeDirection(_direction);
            if (_direction == Direction.Left)
                _formingPosition.X = _room.Area.Right - 192;
            else
                _formingPosition.X = _room.Area.Left + 192;

            //Reset the blob movement index.
            _blobMovementOrderIndex = 0;

            //Set the state.
            _currentState = State.Travelling;
        }

        /// <summary>
        /// Moves a single blob across the screen, according to the current position
        /// in the blob movement order array and the current direction.
        /// </summary>
        private void MoveBlob()
        {
            //Get the vertical index from the array.
            int verticalIndex = _blobMovementOrder[_blobMovementOrderIndex];
            Debug.WriteLine("array index " + _blobMovementOrderIndex + "; moving at " + verticalIndex);

            //Find the furthest blob at the given vertical index and tell it to move.
            YellowDevilBlob blobToMove = FindFormedBlob(OppositeDirection(_direction), verticalIndex);

            if (blobToMove != null)
            {
                //Tell the blob to start travelling.
                blobToMove.StartTravelling(OppositeDirection(_direction));
            }
            //If the furthest blob at the current vertical index wasn't found, throw
            //an exception.
            else
            {
                throw new InvalidOperationException("No blob that is able travel was " +
                    "found; vertical index searched was " + verticalIndex + ".");
            }
        }

        /// <summary>
        /// Checks if all of the Yellow Devil's blobs have travelled across the screen yet.
        /// </summary>
        /// <returns>Returns true if all of the blobs are formed and facing in the same direction
        /// as the Yellow Devil's eye.</returns>
        private bool FullyTravelled()
        {
            foreach (YellowDevilBlob blob in _blobs)
            {
                //If a blob isn't formed or isn't facing in the same direction as the eye, return
                //false.
                if (blob.CurrentState != YellowDevilBlob.State.Formed
                    || blob.CurrentDirection != _direction)
                    return false;
            }

            //If we've successfully checked all of the blobs and reached this point in the
            //code, return true.
            return true;
        }

        /// <summary>
        /// Splits up and moves the Yellow Devil's blobs over time.
        /// </summary>
        private void Travel()
        {
            //If we haven't reached the end of an array yet...
            if (_blobMovementOrderIndex < _blobMovementOrder.Length)
            {
                //Decrement the timer; if it is now zero, move a blob.
                if (--_timer <= 0)
                {
                    MoveBlob();

                    //Reset the timer and increment the position in the array.
                    _timer = _blobMovementDuration;
                    _blobMovementOrderIndex++;
                }
            }
            else
            {
                //Wait until all of the blobs have moved before opening the eye.
                if (FullyTravelled())
                {
                    _currentState = State.Opening;
                    PlaceEye();
                }
            }
        }

        /// <summary>
        /// Positions the Yellow Devil's eye onto its body.
        /// </summary>
        private void PlaceEye()
        {
            Vector2 basePosition;
            if (_direction == Direction.Left)
                basePosition = _formingPosition + new Vector2(69, 19);
            else
                basePosition = _formingPosition + new Vector2(-101, 19);

            //Alter the position if we're below the threshold for using a random position within
            //the given area.
            if (_health <= _eyePlacementAreaThreshold)
            {
                basePosition.X += _random.Next(_eyePlacementArea.Left, _eyePlacementArea.Right);
                basePosition.Y += _random.Next(_eyePlacementArea.Top, _eyePlacementArea.Bottom);
            }

            _position = basePosition;
        }

        /// <summary>
        /// Causes the Yellow Devil to wait after the eye opens/closes before shooting/travelling.
        /// </summary>
        private void WaitAfterEyeOpens()
        {
            //Decrement the timer; if it is now zero, change the state.
            if (--_timer <= 0)
            {
                //If we were open, start shooting.
                if (_currentState == State.Open)
                    _currentState = State.Shooting;
                //If we were closed, start travelling.
                if (_currentState == State.Closed)
                    StartTravelling();

                //Reset the timer for next time.
                _timer = _waitAfterOpenDuration;
            }
        }

        /// <summary>
        /// Causes the Yellow Devil to shoot.
        /// </summary>
        private void ShootingPhase()
        {
            //Decrement the shoot timer; if it is now zero, shoot.
            if (--_timer <= 0)
            {
                //Increase the number of bullets; if it is now over to the maximum, change state.
                if (++_numberOfShots > _numberOfShotsMax)
                {
                    _numberOfShots = 0;
                    _currentState = State.Closing;
                }
                //Otherwise, shoot a bullet.
                else
                {
                    //Create a bullet that heads towards the player.
                    _room.Add(new BasicBullet(_room,
                                              _position + new Vector2(16, 8),
                                              this,
                                              3,
                                              EntityHelper.RadialToVector(_bulletSpeed,
                                                  EntityHelper.Direction(Hitbox.Center, _room.PlayerOne.Hitbox.Center)),
                                              false));

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

                    //Reset the timer for next time.
                    _timer = _shootDuration;
                }
            }
        }

        /// <summary>
        /// Causes the Yellow Devil to start exploding.
        /// </summary>
        protected override void Die()
        {
            //Set the state and cause the eye to keep flashing.
            _currentState = State.Dying;
            _invincibiltyTimer = 100000;

            //Call the boss destroyed event.
            if (OnBossDestroy != null)
                OnBossDestroy(this, new BossDestroyEventArgs(330));
        }

        /// <summary>
        /// Causes the Yellow Devil's blobs to gradually explode, and causes the level to be won once
        /// it has been completely destroyed.
        /// </summary>
        private void Dying()
        {
            //Decrement the timer; if it is now zero, cause something to explode.
            if (--_timer <= 0)
            {
                //If there are still blobs remaining in the list, destroy the first one.
                if (_blobs.Count != 0)
                {
                    _room.Remove(_blobs[0]);
                    _room.Add(new ExplosionEffect(_room, new Vector2(_blobs[0].Hitbox.Center.X,
                                                                     _blobs[0].Hitbox.Center.Y)));
                    _blobs.RemoveAt(0);

                    //Reset the timer. If the blob that was just destroyed is the last blob,
                    //wait even longer.
                    if (_blobs.Count != 0)
                        _timer = BlobExplodeDuration;
                    else
                        _timer = BlobExplodeDuration * 7;
                }
                //Otherwise, if there are no blobs left, cause the Yellow Devil's eye to explode.
                else
                {
                    //Create an explosion and remove this entity from the room.
                    EntityHelper.MegaManExplosion(_room, _position + new Vector2(16, 8));
                    _room.Remove(this);

                    //Make the sound effect.
                    _death.Play();
                }
            }
        }

        /// <summary>
        /// Updates the reference to the previous state, and restarts any animations.
        /// </summary>
        private void UpdatePreviousState()
        {
            //If the state is different to what it was last time, restart the
            //state's animation.
            if (_currentState != _previousState)
            {
                _animationSet[_currentState].ToFrame(0);
            }

            //Update the previous state.
            _previousState = _currentState;
        }
        
        /// <summary>
        /// Provides debugging controls.
        /// </summary>
        private void DebugControls()
        {
            //If we're not in debug mode, throw an exception.
            if (!Game1.DEBUG)
                throw new InvalidOperationException("The debug controls method " +
                    "was called when the game was not in debug mode.");

            //Travel.
            if (InputHelper.IsKeyPressed(Keys.D2))
            {
                MoveBlob();
            }

            //Travel.
            if (InputHelper.IsKeyPressed(Keys.D3))
            {
                _health = 1;
            }
        }

        /// <summary>
        /// Updates the Yellow Devil.
        /// </summary>
        /// <param name="gameTime">The current snapshot of time.</param>
        public override void Update(GameTime gameTime)
        {
            //Move dependent on the current state.
            switch (_currentState)
            {
                case State.Closed:
                    WaitAfterEyeOpens();
                    break;
                case State.Travelling:
                    Travel();
                    break;
                case State.Open:
                    WaitAfterEyeOpens();
                    break;
                case State.Shooting:
                    ShootingPhase();
                    break;
                case State.Dying:
                    Dying();
                    break;
            }

            //Update animations.
            _animationSet[_currentState].Update();

            if (Game1.DEBUG)
                DebugControls();

            UpdatePreviousState();

            base.Update(gameTime);
        }

        /// <summary>
        /// Draws the Yellow Devil's eye.
        /// </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 = _animationSet[_currentState].CurrentFrame.Item1;
                Vector2 currentFrameOffset = _animationSet[_currentState].CurrentFrame.Item2;

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

        #endregion

        #region Event Handles

        /// <summary>
        /// Method called when the eye-opening animation ends.
        /// </summary>
        /// <param name="sender">The animation that ended.</param>
        /// <param name="e">Event arguments.</param>
        void opening_OnAnimationEnd(object sender, EventArgs e)
        {
            //Change the state.
            _currentState = State.Open;
        }

        /// <summary>
        /// Method called when the eye-closing animation ends.
        /// </summary>
        /// <param name="sender">The animation that ended.</param>
        /// <param name="e">Event arguments.</param>
        void closing_OnAnimationEnd(object sender, EventArgs e)
        {
            //Change the state.
            _currentState = State.Closed;
        }

        #endregion
    }
}
