using System;
using System.Collections.Generic;
using System.Linq;
using System.Diagnostics;
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.MainGameClasses
{
    /// <summary>
    /// Class responsible for handling the main game. This includes causing an
    /// individual level to be loaded via the LevelLoader class.
    /// </summary>
    class MainGame : GameState
    {
        #region Member Variables

        /// <summary>
        /// A list of the names of the folders and XML files for each of the levels.
        /// The contents of this list are loaded from Content\Levels\levels.xml using
        /// the main game loader class.
        /// </summary>
        static List<Tuple<string, string>> _levelLocations;

        /// <summary>
        /// The zero-based index of the current level. This is used to load the correct level using
        /// the details stored in _levelFolders.
        /// </summary>
        static int _levelIndex;

        /// <summary>
        /// The level that is currently being played, after it has been instantiated.
        /// </summary>
        Level _currentLevel;

        /// <summary>
        /// The game's current difficulty setting.
        /// </summary>
        GameDifficulty _gameDifficulty;

        /// <summary>
        /// The one and only Potato Man texture. Used for debugging.
        /// </summary>
        public static Texture2D POTATO_MAN;

        #endregion

        /// <summary>
        /// The event called when the main game quits.
        /// </summary>
        public override event GameStateQuit OnGameStateQuit;

        #region Properties

        /// <summary>
        /// Gets the game's content manager.
        /// </summary>
        public ContentManager Content
        {
            get { return _game1.Content; }
        }

        /// <summary>
        /// Gets the game's graphics device.
        /// </summary>
        public GraphicsDevice GraphicsDevice
        {
            get { return _game1.GraphicsDevice; }
        }

        /// <summary>
        /// Gets the game's difficulty setting.
        /// </summary>
        public GameDifficulty GameDifficulty
        {
            get { return _gameDifficulty; }
        }

        #endregion

        /// <summary>
        /// Sets the starting level index to the first level. Edit this if you want
        /// to cheat.
        /// </summary>
        static MainGame()
        {
            _levelIndex = 0;
        }

        /// <summary>
        /// Starts the main game on the most recently played level.
        /// </summary>
        /// <param name="game1">A reference to XNA's game class.</param>
        /// <param name="gameDifficulty">The game's difficulty setting.</param>
        public MainGame(Game1 game1, GameDifficulty gameDifficulty)
            : base(game1)
        {
            _gameDifficulty = gameDifficulty;
        }

        /// <summary>
        /// Starts the main game on a specified level.
        /// </summary>
        /// <param name="game1">A reference to XNA's game class.</param>
        /// <param name="gameDifficulty">The game's difficulty setting.</param>
        /// <param name="levelIndex">The index of the level to start at.</param>
        public MainGame(Game1 game1, GameDifficulty gameDifficulty, int levelIndex)
            : this(game1, gameDifficulty)
        {
            _levelIndex = levelIndex;
        }

        #region Methods

        /// <summary>
        /// Initialises the main game.
        /// </summary>
        public override void Initialise()
        {
            //Initialise all of the types that will appear in the game.
            Player.Initialise(_gameDifficulty);

            BigEvilKillingThing.Initialise(_gameDifficulty);
            ItemCreator.Initialise(_gameDifficulty);
            LargeHealth.Initialise(_gameDifficulty);
            Metool.Initialise(_gameDifficulty);
            PipiBaby.Initialise(_gameDifficulty);
            PipiBird.Initialise(_gameDifficulty);
            PipiEgg.Initialise(_gameDifficulty);
            PipiGenerator.Initialise(_gameDifficulty);
            PitLurker.Initialise(_gameDifficulty);
            QuickManLaserGenerator.Initialise(_gameDifficulty);
            SmallHealth.Initialise(_gameDifficulty);
            SniperJoe.Initialise(_gameDifficulty);
            Tripropellan.Initialise(_gameDifficulty);
            YellowDevil.Initialise(_gameDifficulty);

            //Get a list of levels that exist.
            MainGameLoader mainGameLoader = new MainGameLoader("Content\\Levels\\");
            _levelLocations = mainGameLoader.LoadLevelLocations();

            //Start the level at the current level index.
            StartLevel(_levelIndex);
        }

        /// <summary>
        /// Loads the assets for the main game.
        /// </summary>
        public override void LoadContent(ContentManager content)
        {
            POTATO_MAN = content.Load<Texture2D>("Images\\potatoMan");

            //Loads all the entities and things that might appear in the level.
            BasicBullet.LoadContent(content);
            BigBullet.LoadContent(content);
            BigEvilKillingThing.LoadContent(content);
            BossIntroduction.LoadContent(content);
            Cake.LoadContent(content);
            Eggshell.LoadContent(content);
            Enemy.LoadContent(content);
            Explosion.LoadContent(content);
            ExplosionEffect.LoadContent(content);
            ExtraLife.LoadContent(content);
            HeadsUpDisplay.LoadContent(content);
            LargeHealth.LoadContent(content);
            Menu.LoadContent(content);
            Metool.LoadContent(content);
            PauseScreen.LoadContent(content);
            PipiBaby.LoadContent(content);
            PipiBird.LoadContent(content);
            PipiEgg.LoadContent(content);
            PitLurker.LoadContent(content);
            Player.LoadContent(content);
            PlayerDeathEffect.LoadContent(content);
            QuickManLaserGenerator.LoadContent(content);
            SmallHealth.LoadContent(content);
            SniperJoe.LoadContent(content);
            Tripropellan.LoadContent(content);
            YellowDevil.LoadContent(content);
        }

        /// <summary>
        /// Sets the current level index to the given index, and instantiates that level.
        /// </summary>
        /// <param name="levelIndex">The index of the level that is to be started.</param>
        private void StartLevel(int levelIndex)
        {
            //Set the level index.
            _levelIndex = levelIndex;

            //Pull out the current level's names from the list of level location details.
            string levelFolderName = _levelLocations[levelIndex].Item1 + "\\";
            string levelXmlName = _levelLocations[levelIndex].Item2 + ".xml";

            //Now instantiate the current level.
            LevelLoader levelLoader = new LevelLoader(this, levelFolderName, levelXmlName);
            _currentLevel = null;
            _currentLevel = levelLoader.CreateLevel();
            _currentLevel.Initialise();

            //Load content for the current level.
            _currentLevel.LoadContent(Content);

            //Subscribe to the current level's quit event.
            _currentLevel.OnQuit += new Quit(_currentLevel_OnQuit);
        }

        /// <summary>
        /// Causes the current level to end.
        /// </summary>
        private void EndLevel()
        {
            //Unsubscribe from the level's events and nullify the reference.
            _currentLevel.OnQuit -= _currentLevel_OnQuit;
            _currentLevel = null;
        }

        /// <summary>
        /// Updates the main game.
        /// </summary>
        /// <param name="gameTime">The current snapshot of time.</param>
        public override void Update(GameTime gameTime)
        {
            //Update the room.
            _currentLevel.Update(gameTime);

            //Allow the game to exit to the title screen.
            if (InputHelper.IsKeyPressed(Keys.Escape))
                OnGameStateQuit(this, new GameStateQuitEventArgs(Game1.State.TitleScreen, _gameDifficulty));

            //Update the keyboard/gamepad helper.
            InputHelper.Update();
        }

        /// <summary>
        /// Draws the main game to the screen.
        /// </summary>
        /// <param name="spriteBatch">The sprite batch to draw to.</param>
        public override void Draw(SpriteBatch spriteBatch)
        {
            //Draw the current level.
            _currentLevel.Draw(spriteBatch);
        }

        #endregion

        #region Event Handles

        /// <summary>
        /// Method called when the current level has ended.
        /// </summary>
        /// <param name="sender">The level that ended.</param>
        /// <param name="e">Quit event arguments.</param>
        void _currentLevel_OnQuit(object sender, LevelQuitEventArgs e)
        {
            //Decide what to do based on why the level quit.
            switch (e.CurrentQuitReason)
            {
                case LevelQuitEventArgs.QuitReason.MenuQuit:
                    EndLevel();
                    //Call the main game quit event if there are subscribers.
                    if (OnGameStateQuit != null)
                        OnGameStateQuit(this, new GameStateQuitEventArgs(Game1.State.TitleScreen, _gameDifficulty));
                    break;
                case LevelQuitEventArgs.QuitReason.Victory:
                    EndLevel();
                    _levelIndex++;
                    StartLevel(_levelIndex);
                    break;
                case LevelQuitEventArgs.QuitReason.OutOfLives:
                    EndLevel();
                    //Call the main game quit event if there are subscribers.
                    if (OnGameStateQuit != null)
                        OnGameStateQuit(this, new GameStateQuitEventArgs(Game1.State.GameOverScreen, _gameDifficulty));
                    break;
                case LevelQuitEventArgs.QuitReason.CakeCollected:
                    //Call the main game quit event if there are subscribers.
                    if (OnGameStateQuit != null)
                        OnGameStateQuit(this, new GameStateQuitEventArgs(Game1.State.YouWinScreen, _gameDifficulty));
                    break;
                default:
                    throw new NotImplementedException();
            }
        }

        #endregion
    }
}
