using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using System.Diagnostics;

namespace MegaManRipoff.MainGameClasses
{
    /// <summary>
    /// The handler for the event called when this animation ends.
    /// </summary>
    /// <param name="sender">The sender object.</param>
    /// <param name="e">The event's arguments, if any.</param>
    public delegate void AnimationEnd(object sender, EventArgs e);

    /// <summary>
    /// Provides a set of frames from one texture to be used in an animation.
    /// </summary>
    class Animation
    {
        #region Member Variables

        /// <summary>
        /// The list of frames used in the animation, defining the source rectangle of the
        /// frame, the drawing offset from the entity's position and the animation length of
        /// the frame in ticks, respectively.
        /// </summary>
        List<Tuple<Rectangle, Vector2, int>> _frames;

        /// <summary>
        /// The index number of the current frame.
        /// </summary>
        int _currentIndex;

        /// <summary>
        /// A counter used for counting how many ticks are left in the current frame.
        /// </summary>
        int _frameTimer;

        /// <summary>
        /// The index of the frame that should be called when the animation finishes looping.
        /// </summary>
        int _loopFrameIndex;

        #endregion

        /// <summary>
        /// The event called when an animation ends.
        /// </summary>
        public event AnimationEnd OnAnimationEnd;

        #region Properties

        public Tuple<Rectangle, Vector2, int> CurrentFrame
        {
            get { return _frames[_currentIndex]; }
        }

        #endregion

        /// <summary>
        /// Creates a new animation.
        /// </summary>
        /// <param name="firstFrame">The first (and possibly only) frame of the animation.
        /// The tuple takes the source rectangle (I quickly realised that using rectangles
        /// of various sizes was a stupid idea), the drawing offset from the entity's
        /// position and the number of ticks the frame should last, respectively. Note that
        /// a frame duration of 0 will not move.</param>
        /// <param name="loopFrameIndex">Sets how the animation should loop.</param>
        public Animation(Tuple<Rectangle, Vector2, int> firstFrame, int loopFrameIndex)
        {
            _frames = new List<Tuple<Rectangle, Vector2, int>>();
            _frames.Add(firstFrame);
            _currentIndex = 0;
            _frameTimer = firstFrame.Item3;
            _loopFrameIndex = loopFrameIndex;
        }

        #region Methods

        /// <summary>
        /// Adds a frame to the animation.
        /// </summary>
        /// <param name="newFrame">The new frame. The tuple takes the source rectangle, 
        /// the drawing offset from the entity's position and the number of ticks the frame 
        /// should last, respectively. Note that a frame duration of 0 will not move.</param>
        public void AddFrame(Tuple<Rectangle, Vector2, int> newFrame)
        {
            _frames.Add(newFrame);
        }

        /// <summary>
        /// Updates which frame should currently be shown.
        /// </summary>
        public void Update()
        {
            //Decrement the timer if we haven't gone into negatives yet.
            if (_frameTimer > 0)
            {
                //Decrement the frame timer; if we've now reached the end of
                //the frame, move on to the next one.
                if (--_frameTimer == 0)
                {
                    //If the current frame is the last one, go to the loop point.
                    if (_currentIndex == _frames.Count - 1)
                    {
                        _currentIndex = _loopFrameIndex;

                        //Call the animation end event if there is at least one subscriber.
                        if (OnAnimationEnd != null)
                            OnAnimationEnd(this, new EventArgs());
                    }
                    //Otherwise, we're not at the last frame, so move to the next one.
                    else
                    {
                        _currentIndex++;
                    }

                    //Get the amount of ticks this new frame lasts for.
                    _frameTimer = _frames[_currentIndex].Item3;

                    //If this new frame has length zero, the animation won't continue
                    //so call the animation end event (if there's a subscriber).
                    if (_frameTimer == 0 && OnAnimationEnd != null)
                        OnAnimationEnd(this, new EventArgs());
                }
            }
        }

        /// <summary>
        /// Sets the current frame of the animation.
        /// </summary>
        /// <param name="frame">The index of the frame.</param>
        public void ToFrame(int frame)
        {
            _currentIndex = frame;
            _frameTimer = _frames[_currentIndex].Item3;
        }

        #endregion
    }
}
