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.Graphics;

namespace MegaManRipoff.UI
{
    /// <summary>
    /// The handler for the event called when the menu is closed with no item selected.
    /// </summary>
    /// <param name="sender">The sender object.</param>
    /// <param name="e">Event arguments.</param>
    public delegate void Close(object sender, EventArgs e);

    /// <summary>
    /// Handles the menu and its menu items.
    /// </summary>
    class Menu
    {
        #region Member Variables

        /// <summary>
        /// The font used for the list items.
        /// </summary>
        readonly SpriteFont _font;

        /// <summary>
        /// The texture used for the menu cursor.
        /// </summary>
        readonly Texture2D _cursor;

        /// <summary>
        /// The offset of the cursor from the selected menu item.
        /// </summary>
        readonly Vector2 _cursorOffset;

        /// <summary>
        /// The list of menu items.
        /// </summary>
        List<MenuItem> _items;

        /// <summary>
        /// The index of the currently selected menu item.
        /// </summary>
        int _index;

        /// <summary>
        /// Keeps track of how long the menu should be delayed after selecting a
        /// menu item that causes a delay. The selection event will be called
        /// after the delay has expired.
        /// </summary>
        int _delayTimer;

        /// <summary>
        /// The sound effect used when the pause menu is opened or closed.
        /// </summary>
        static SoundEffect _pauseMenu;

        /// <summary>
        /// The sound effect used when the cursor moves.
        /// </summary>
        static SoundEffect _cursorMovement;

        /// <summary>
        /// The sound effect used when the an item is selected.
        /// </summary>
        static SoundEffect _itemSelected;

        #endregion

        /// <summary>
        /// The event called when the menu is closed with no item selected.
        /// </summary>
        public event Close OnClose;  

        /// <summary>
        /// Creates a new menu.
        /// </summary>
        /// <param name="font">The font used for the menu items.</param>
        /// <param name="cursor">The texture used for the menu cursor.</param>
        /// <param name="cursorOffset">The offset of the cursor from the selected menu item.</param>
        /// <param name="playSoundEffect">Whether or not to play the opening sound effect.</param>
        public Menu(SpriteFont font, Texture2D cursor, Vector2 cursorOffset, bool playSoundEffect)
        {
            _font = font;
            _cursor = cursor;
            _cursorOffset = cursorOffset;

            //Instantiate list.
            _items = new List<MenuItem>();
            _index = 0;

            //Play the sound effect if necessary.
            if (playSoundEffect)
                _pauseMenu.Play();
        }

        #region Methods

        /// <summary>
        /// Loads the assets required by the menu.
        /// </summary>
        /// <param name="content">The game's content manager.</param>
        public static void LoadContent(ContentManager content)
        {
            _pauseMenu = content.Load<SoundEffect>("Sounds\\Pause");
            _cursorMovement = content.Load<SoundEffect>("Sounds\\Select");
            _itemSelected = content.Load<SoundEffect>("Sounds\\Selected");
        }

        /// <summary>
        /// Adds a menu item to the menu.
        /// </summary>
        /// <param name="item"></param>
        public void AddItem(MenuItem item)
        {
            _items.Add(item);
        }

        /// <summary>
        /// Handles cursor movement when the user inputs up or down.
        /// </summary>
        private void MoveCursor()
        {
            //Move the cursor up.
            if (InputHelper.IsUpPressed())
            {
                //Play the movement sound.
                _cursorMovement.Play();

                //Loop the cursor round if we move up from the first object.
                if (--_index < 0)
                    _index = _items.Count - 1;
            }
            else
            {
                //Move the cursor down.
                if (InputHelper.IsDownPressed())
                {
                    //Play the movement sound.
                    _cursorMovement.Play();

                    //Loop the cursor round if we move down from the last object.
                    if (++_index >= _items.Count)
                        _index = 0;
                }
            }
        }

        /// <summary>
        /// Handles selecting items.
        /// </summary>
        private void SelectItems()
        {
            //If the selection button is pressed...
            if (InputHelper.IsJumpPressed() || InputHelper.IsStartPressed())
            {
                //Play the sound.
                _itemSelected.Play();

                //If the item has no delay setting, activate the current index's select event.
                //Otherwise, set our delay timer.
                if (_items[_index].Delay == 0)
                    _items[_index].Select();
                else
                    _delayTimer = _items[_index].Delay;
            }
        }

        /// <summary>
        /// Handles closing the screen when the user inputs the start button again or presses
        /// the back button (shoot).
        /// </summary>
        private void CloseMenu()
        {
            if (InputHelper.IsShootPressed())
            {
                //If there are any subscribers, call the close event.
                if (OnClose != null)
                {
                    OnClose(this, new EventArgs());

                    //Play the sound effect.
                    _pauseMenu.Play();
                }
            }
        }

        /// <summary>
        /// Delays the menu according to the delay timer. It activates the event of the selected
        /// item when the timer expires.
        /// </summary>
        private void Delay()
        {
            //Decrement the delay timer; if it is now zero, activate the current item's selection
            //event.
            if (--_delayTimer <= 0)
            {
                _items[_index].Select();
            }
        }

        /// <summary>
        /// Updates the menu.
        /// </summary>
        /// <param name="gameTime">The current snapshot of time.</param>
        public void Update(GameTime gameTime)
        {
            //Handle user input if we're not being delayed
            if (_delayTimer <= 0)
            {
                MoveCursor();
                SelectItems();
                CloseMenu();
            }
            //Otherwise, be delayed.
            else
            {
                Delay();
            }
        }

        /// <summary>
        /// Draws the menu and its items.
        /// </summary>
        /// <param name="spriteBatch">The sprite batch to draw to.</param>
        public void Draw(SpriteBatch spriteBatch)
        {
            //Draw the cursor.
            spriteBatch.Draw(_cursor,
                             _items[_index].Position + _cursorOffset,
                             Color.White);

            foreach (MenuItem item in _items)
            {
                //Draw shadow, then the item's text.
                spriteBatch.DrawString(_font, item.Text, item.Position + Vector2.One, Color.Black);
                spriteBatch.DrawString(_font, item.Text, item.Position, Color.White);
            }
        }

        #endregion
    }
}
