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

namespace MegaManRipoff.TitleScreenClasses
{

    /// <summary>
    /// Signature for the event called when the exit option is selected.
    /// </summary>
    /// <param name="sender">The object calling the event.</param>
    /// <param name="e">Event arguments.</param>
    public delegate void ExitSelected(object sender, EventArgs e);

    /// <summary>
    /// Class for handling the game's title screen.
    /// </summary>
    class TitleScreen : GameState
    {
        #region Member Variables

        /// <summary>
        /// The camera used for displaying the title screen nicely.
        /// </summary>
        Camera _camera;

        /// <summary>
        /// The texture used for the title screen.
        /// </summary>
        Texture2D _titleTexture;

        /// <summary>
        /// The text used for the press start display.
        /// </summary>
        readonly string _pressStartText = "Press Start/Enter";

        /// <summary>
        /// The font used for the press start text.
        /// </summary>
        SpriteFont _pressStartFont;

        /// <summary>
        /// The font used for the menu.
        /// </summary>
        SpriteFont _menuFont;

        /// <summary>
        /// The texture used for the cursor.
        /// </summary>
        Texture2D _cursorTexture;

        /// <summary>
        /// The offset of the cursor from the selected menu item.
        /// </summary>
        readonly Vector2 _cursorOffset = new Vector2(-28, 9);

        /// <summary>
        /// The main menu.
        /// </summary>
        Menu _menu;

        /// <summary>
        /// The menu's items, used to subscribe and unsubscribe to/from their selection events.
        /// </summary>
        MenuItem _easyGame, _normalGame, _hardGame, _controlsItem, _creditsItem, _quitItem;

        /// <summary>
        /// The controls window.
        /// </summary>
        ControlsWindow _controlsWindow;

        /// <summary>
        /// The credits window.
        /// </summary>
        CreditsWindow _creditsWindow;

        /// <summary>
        /// The title music.
        /// </summary>
        Song _music;

        /// <summary>
        /// Whether or not the music has played.
        /// </summary>
        bool _hasPlayed;

        #endregion

        #region Event Declarations

        /// <summary>
        /// Event called when the title screen quits.
        /// </summary>
        public override event GameStateQuit OnGameStateQuit;

        /// <summary>
        /// Event called when the exit option is selected.
        /// </summary>
        public event ExitSelected OnExitSelected;

        #endregion

        /// <summary>
        /// Creates a new title screen.
        /// </summary>
        /// <param name="game1">The current Game1 class.</param>
        public TitleScreen(Game1 game1)
            : base(game1)
        {
        }

        #region Methods

        /// <summary>
        /// Initialises the title screen.
        /// </summary>
        public override void Initialise()
        {
            //Create the camera.
            _camera = new Camera(_game1.GraphicsDevice, Vector2.Zero);

            //Create the controls and credits windows.
            _controlsWindow = new ControlsWindow();
            _creditsWindow = new CreditsWindow();
        }

        /// <summary>
        /// Loads all assets required by the title screen.
        /// </summary>
        /// <param name="content">The game's content manager.</param>
        public override void LoadContent(ContentManager content)
        {
            _titleTexture = content.Load<Texture2D>("Images\\Title");
            _cursorTexture = content.Load<Texture2D>("Images\\Cursor");

            _pressStartFont = content.Load<SpriteFont>("Fonts\\SegoeUI");
            _menuFont = content.Load<SpriteFont>("Fonts\\SegoeUISmall");

            _music = content.Load<Song>("Music\\Title");

            Menu.LoadContent(content);
            ControlsWindow.LoadContent(content);
            CreditsWindow.LoadContent(content);
        }

        /// <summary>
        /// Plays the title music.
        /// </summary>
        private void PlayMusic()
        {
            //This ensures the music only plays once.
            if (!_hasPlayed && Game1.MUSIC)
            {
                MediaPlayer.IsRepeating = false;
                MediaPlayer.Play(_music);
                _hasPlayed = true;
            }
        }

        /// <summary>
        /// Creates the main menu add subscribes the title screen to its events.
        /// </summary>
        private void CreateMenu()
        {
            _menu = new Menu(_menuFont, _cursorTexture, _cursorOffset, true);
            _menu.OnClose += new Close(_menu_OnClose);

            //Create items and subscribe to their events.
            _easyGame = new MenuItem(_menu,
                                     "Easy Mode",
                                     new Vector2(_camera.Area.Center.X - (_menuFont.MeasureString("Easy Mode").X / 2),
                                                 155 - (_menuFont.MeasureString("Easy Mode").Y / 2)),
                                     70);
            _easyGame.OnSelected += new Selected(_easyGame_OnSelected);
            _menu.AddItem(_easyGame);

            _normalGame = new MenuItem(_menu,
                                     "Normal Mode",
                                     new Vector2(_camera.Area.Center.X - (_menuFont.MeasureString("Easy Mode").X / 2),
                                                 175 - (_menuFont.MeasureString("Easy Mode").Y / 2)),
                                     70);
            _normalGame.OnSelected += new Selected(_normalGame_OnSelected);
            _menu.AddItem(_normalGame);

            _hardGame = new MenuItem(_menu,
                                     "Hard Mode",
                                     new Vector2(_camera.Area.Center.X - (_menuFont.MeasureString("Easy Mode").X / 2),
                                                 195 - (_menuFont.MeasureString("Easy Mode").Y / 2)),
                                     70);
            _hardGame.OnSelected += new Selected(_hardGame_OnSelected);
            _menu.AddItem(_hardGame);

            _controlsItem = new MenuItem(_menu,
                                         "Controls",
                                         new Vector2(_camera.Area.Center.X - (_menuFont.MeasureString("Easy Mode").X / 2),
                                                     215 - (_menuFont.MeasureString("Easy Mode").Y / 2)),
                                         1);
            _controlsItem.OnSelected += new Selected(_controlsItem_OnSelected);
            _menu.AddItem(_controlsItem);

            _creditsItem = new MenuItem(_menu,
                                        "Credits",
                                        new Vector2(_camera.Area.Center.X - (_menuFont.MeasureString("Easy Mode").X / 2),
                                                    235 - (_menuFont.MeasureString("Easy Mode").Y / 2)),
                                        1);
            _creditsItem.OnSelected += new Selected(_creditsItem_OnSelected);
            _menu.AddItem(_creditsItem);

            _quitItem = new MenuItem(_menu,
                                     "Quit",
                                     new Vector2(_camera.Area.Center.X - (_menuFont.MeasureString("Easy Mode").X / 2),
                                                 255 - (_menuFont.MeasureString("Easy Mode").Y / 2)),
                                     70);
            _quitItem.OnSelected += new Selected(_quitItem_OnSelected);
            _menu.AddItem(_quitItem);
        }

        /// <summary>
        /// Toggles the game's debug and music states.
        /// </summary>
        private void ToggleDebug()
        {
            //If Ctrl and Shift are held..
            if ((InputHelper.IsKeyHeld(Keys.LeftControl) || InputHelper.IsKeyHeld(Keys.RightControl))
                && (InputHelper.IsKeyHeld(Keys.LeftShift) || InputHelper.IsKeyHeld(Keys.RightShift)))
            {
                //Toggle debug with Ctrl+Shift+D.
                if (InputHelper.IsKeyPressed(Keys.D))
                    Game1.DEBUG = !Game1.DEBUG;

                //Toggle debug with Ctrl+Shift+M.
                if (InputHelper.IsKeyPressed(Keys.M))
                    Game1.MUSIC = !Game1.MUSIC;
            }
        }

        /// <summary>
        /// Updates the title screen's camera and accepts input.
        /// </summary>
        /// <param name="gameTime">The current snapshot of time.</param>
        public override void Update(GameTime gameTime)
        {
            PlayMusic();

            //Update the input handler.
            InputHelper.Update();

            //Update the camera (used for scaling).
            _camera.Update();

            //If the menu doesn't exist, check if start is pressed.
            if (_menu == null)
            {
                if (InputHelper.IsStartPressed())
                    CreateMenu();
            }
            //Otherwise, update the menu if the controls and credits window isn't open.
            else
            {
                if (_controlsWindow.CurrentState != ControlsWindow.State.Open
                    && _creditsWindow.CurrentState != CreditsWindow.State.Open)
                    _menu.Update(gameTime);
            }

            //Update the controls and credits windows.
            _controlsWindow.Update(gameTime);
            _creditsWindow.Update(gameTime);

            //Toggle debug modes.
            ToggleDebug();
        }

        /// <summary>
        /// Draws the press start text to the screen.
        /// </summary>
        private void DrawPressStartText(SpriteBatch spriteBatch)
        {
            //Position the string at the centre of the screen.
            Vector2 position = new Vector2(_camera.Area.Center.X - (_pressStartFont.MeasureString(_pressStartText).X / 2),
                                           _camera.Area.Center.Y - (_pressStartFont.MeasureString(_pressStartText).Y / 2) + 48);

            //Draw the shadow, then the actual text.
            spriteBatch.DrawString(_pressStartFont,
                                   _pressStartText,
                                   position + Vector2.One,
                                   Color.Black);
            spriteBatch.DrawString(_pressStartFont,
                                   _pressStartText,
                                   position,
                                   Color.White);
        }

        /// <summary>
        /// Draws indicators representing the debug mode if it is set.
        /// </summary>
        /// <param name="spriteBatch">The sprite batch to draw to.</param>
        private void DrawDebugIndicators(SpriteBatch spriteBatch)
        {
            //Draw the debug indicator.
            if (Game1.DEBUG)
                spriteBatch.DrawString(_menuFont, "D", new Vector2(492, 264), Color.LawnGreen);

            //Draw the no music indicator.
            if (!Game1.MUSIC)
                spriteBatch.DrawString(_menuFont, "M", new Vector2(492, 244), Color.HotPink);
        }

        /// <summary>
        /// Draws the title screen.
        /// </summary>
        /// <param name="spriteBatch">The sprite batch to draw to.</param>
        public override void Draw(SpriteBatch spriteBatch)
        {
            //Start the sprite batch.
            spriteBatch.Begin(SpriteSortMode.Deferred,
                              BlendState.AlphaBlend,
                              SamplerState.PointWrap,
                              null, null, null,
                              _camera.ScaleTransformation);

            //Draw the title texture.
            spriteBatch.Draw(_titleTexture, Vector2.Zero, Color.White);

            //Draw the press start text if the menu hasn't been created.
            if (_menu == null)
                DrawPressStartText(spriteBatch);
            else
                _menu.Draw(spriteBatch);

            //Draw the controls and credits windows.
            _controlsWindow.Draw(spriteBatch);
            _creditsWindow.Draw(spriteBatch);

            //Draw the debug indicators.
            DrawDebugIndicators(spriteBatch);

            spriteBatch.End();
        }

        #endregion

        #region Event Handles

        /// <summary>
        /// Method called when the easy game option is chosen.
        /// </summary>
        /// <param name="sender">The menu item that was selected.</param>
        /// <param name="e">Event arguments.</param>
        void _easyGame_OnSelected(object sender, EventArgs e)
        {
            //Call the start game event if there are subscribers.
            if (OnGameStateQuit != null)
                OnGameStateQuit(this, new GameStateQuitEventArgs(Game1.State.MainGame, GameDifficulty.Easy, 0));
        }

        /// <summary>
        /// Method called when the normal game option is chosen.
        /// </summary>
        /// <param name="sender">The menu item that was selected.</param>
        /// <param name="e">Event arguments.</param>
        void _normalGame_OnSelected(object sender, EventArgs e)
        {
            //Call the start game event if there are subscribers.
            if (OnGameStateQuit != null)
                OnGameStateQuit(this, new GameStateQuitEventArgs(Game1.State.MainGame, GameDifficulty.Normal, 0));
        }

        /// <summary>
        /// Method called when the hard game option is chosen.
        /// </summary>
        /// <param name="sender">The menu item that was selected.</param>
        /// <param name="e">Event arguments.</param>
        void _hardGame_OnSelected(object sender, EventArgs e)
        {
            //Call the start game event if there are subscribers.
            if (OnGameStateQuit != null)
                OnGameStateQuit(this, new GameStateQuitEventArgs(Game1.State.MainGame, GameDifficulty.Hard, 0));
        }

        /// <summary>
        /// Method called when the controls option is chosen.
        /// </summary>
        /// <param name="sender">The menu item that was selected.</param>
        /// <param name="e">Event arguments.</param>
        void _controlsItem_OnSelected(object sender, EventArgs e)
        {
            //Set the state of the controls window.
            _controlsWindow.CurrentState = ControlsWindow.State.Open;
        }

        /// <summary>
        /// Method called when the credits option is chosen.
        /// </summary>
        /// <param name="sender">The menu item that was selected.</param>
        /// <param name="e">Event arguments.</param>
        void _creditsItem_OnSelected(object sender, EventArgs e)
        {
            //Set the state of the credits window.
            _creditsWindow.CurrentState = CreditsWindow.State.Open;
        }

        /// <summary>
        /// Method called when the quit option is chosen.
        /// </summary>
        /// <param name="sender">The menu item that was selected.</param>
        /// <param name="e">Event arguments.</param>
        void _quitItem_OnSelected(object sender, EventArgs e)
        {
            //Call the exit event method if there are subscribers.
            if (OnExitSelected != null)
                OnExitSelected(this, new EventArgs());
        }

        /// <summary>
        /// Method called when the menu is closed by pressing back.
        /// </summary>
        /// <param name="sender">The menu that was quit.</param>
        /// <param name="e">Event arguments.</param>
        void _menu_OnClose(object sender, EventArgs e)
        {
            //Set the menu reference to null and unsubscribe from its events.
            _easyGame.OnSelected -= _easyGame_OnSelected;
            _normalGame.OnSelected -= _normalGame_OnSelected;
            _hardGame.OnSelected -= _hardGame_OnSelected;
            _quitItem.OnSelected -= _quitItem_OnSelected;

            _menu = null;
        }

        #endregion
    }
}
