using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Storage;
using StrategicCombat.GameScreens;

namespace StrategicCombat
{
    /// <summary>
    /// The main menu screen is the first thing displayed when the game starts up.
    /// </summary>
    class MainMenuScreen : MenuScreen
    {
        
        #region Variables

        // To show a buy this game entry in a trial game.
        MenuEntry buyMenuEntry;
        bool isTrialGame = true;

        // Variables used to create a session.
        PlayerIndex currentPlayerIndex = PlayerIndex.One;
        NetworkSessionType currentSessionType = NetworkSessionType.Local;

        // Autosave message.
        public String autosaveMessage;

        #endregion

        #region Initialization

        /// <summary>
        /// Constructor fills in the menu contents.
        /// </summary>
        public MainMenuScreen()
            : base(Resources.MainMenu)
        {
            // Create our menu entries.
            MenuEntry singlePlayerMenuEntry = new MenuEntry(Resources.SinglePlayer);
            MenuEntry twoPlayerMenuEntry = new MenuEntry(Resources.LocalMultiplayer);
            MenuEntry systemLinkMenuEntry = new MenuEntry(Resources.SystemLink);
            MenuEntry liveMenuEntry = new MenuEntry(Resources.PlayerMatch);
            MenuEntry optionsMenuEntry = new MenuEntry(Resources.Options);
            buyMenuEntry = new MenuEntry("Unlock game");
            MenuEntry exitMenuEntry = new MenuEntry(Resources.Exit);

            // Hook up menu event handlers.
            singlePlayerMenuEntry.Selected += SinglePlayerMenuEntrySelected;
            twoPlayerMenuEntry.Selected += TwoPlayerMenuEntrySelected;
            systemLinkMenuEntry.Selected += SystemLinkMenuEntrySelected; 
            liveMenuEntry.Selected += LiveMenuEntrySelected;
            optionsMenuEntry.Selected += OptionsMenuEntrySelected;
            buyMenuEntry.Selected += ShowMarketPlace;
            exitMenuEntry.Selected += OnCancel;

            // Add entries to the menu.
            MenuEntries.Add(singlePlayerMenuEntry);
            MenuEntries.Add(twoPlayerMenuEntry);
            MenuEntries.Add(systemLinkMenuEntry);
            MenuEntries.Add(liveMenuEntry);
            MenuEntries.Add(optionsMenuEntry);
            MenuEntries.Add(buyMenuEntry);
            MenuEntries.Add(exitMenuEntry);

            this.autosaveMessage = "Gamer statistics are automatically loaded and " + Environment.NewLine +
                                   "saved in System Link and Xbox LIVE, please do " + Environment.NewLine +
                                   "not remove your storage device while playing " + Environment.NewLine +
                                   "System Link or Xbox LIVE. ";

            // Play the menu music if it not is playing already.
            if (Program.menuMusic != null && Program.menuMusic.IsPaused && Program.menuMusicOff == false)
            {
                Program.menuMusic.Resume();
            }
        
        }

        #endregion

        #region Update

        /// <summary>
        /// Update the main menu screen.
        /// </summary>
        /// <param name="gameTime"></param>
        /// <param name="otherScreenHasFocus"></param>
        /// <param name="coveredByOtherScreen"></param>
        public override void Update(GameTime gameTime, bool otherScreenHasFocus, bool coveredByOtherScreen)
        {
            base.Update(gameTime, otherScreenHasFocus, coveredByOtherScreen);

            // Show the buy menu entry if the game is a trial game.
            if (this.isTrialGame == true)
            {
                if (Guide.IsTrialMode == false)
                {
                    MenuEntries.Remove(buyMenuEntry);
                    this.isTrialGame = false;
                }
            }

        } // End of the Update method.
        
        #endregion

        #region Handle Input

        /// <summary>
        /// Event handler for when the Single Player menu entry is selected.
        /// </summary>
        private void SinglePlayerMenuEntrySelected(object sender, PlayerIndexEventArgs e)
        {
            // First, we need to make sure a suitable gamer profile is signed in.
            ProfileSignInScreen profileSignIn = new ProfileSignInScreen(NetworkSessionType.Local);

            // Hook up an event so once the ProfileSignInScreen is happy,
            // it will activate the StartTwoPlayerSessionOperation.
            profileSignIn.ProfileSignedIn += delegate
            {
                // Create the single player lobby.
                ScreenManager.AddScreen(new SinglePlayerLobby(null, e.PlayerIndex), e.PlayerIndex);

            };

            // Activate the ProfileSignInScreen.
            ScreenManager.AddScreen(profileSignIn, e.PlayerIndex);

        } // End of the SinglePlayerMenuEntrySelected method.

        /// <summary>
        /// Event handler for when the 2 player menu entry is selected.
        /// </summary>
        private void TwoPlayerMenuEntrySelected(object sender, PlayerIndexEventArgs e)
        {
            
            // First, we need to make sure a suitable gamer profile is signed in.
            ProfileSignInScreen profileSignIn = new ProfileSignInScreen(NetworkSessionType.Local);

            // Hook up an event so once the ProfileSignInScreen is happy,
            // it will activate the StartTwoPlayerSessionOperation.
            profileSignIn.ProfileSignedIn += delegate
            {
                // Call the method to start the operation for a 2 player game.
                StartTwoPlayerSessionOperation(e.PlayerIndex);

            };

            // Activate the ProfileSignInScreen.
            ScreenManager.AddScreen(profileSignIn, e.PlayerIndex);
    
        } // End of the TwoPlayerMenuEntryMenuEntrySelected method

        /// <summary>
        /// Begin to start a 2 player game.
        /// </summary>
        /// <param name="playerIndex"></param>
        private void StartTwoPlayerSessionOperation(PlayerIndex playerIndex)
        {
            // Set the maximum number of local gamers.
            NetworkSessionComponent.MaxLocalGamers = 2;

            try
            {
                // Which local profiles should we include in this session?
                List<SignedInGamer> localGamers = new List<SignedInGamer>();
                localGamers.Add(Gamer.SignedInGamers[playerIndex]);

                // Begin an asynchronous create network session operation.
                IAsyncResult asyncResult = NetworkSession.BeginCreate(
                                                    NetworkSessionType.Local, localGamers,
                                                    NetworkSessionComponent.MaxGamers,
                                                    0, null, null, null);

                // Activate the network busy screen, which will display
                // an animation until this operation has completed.
                NetworkBusyScreen busyScreen = new NetworkBusyScreen(asyncResult);

                busyScreen.OperationCompleted += TwoPlayerSessionOperationCompleted;

                ScreenManager.AddScreen(busyScreen, playerIndex);
            }
            catch (Exception exception)
            {
                NetworkErrorScreen errorScreen = new NetworkErrorScreen(exception);

                ScreenManager.AddScreen(errorScreen, playerIndex);
            }

        } // End of the StartTwoPlayerSessionOperation method

        /// <summary>
        /// Event handler for when the asynchronous create network session
        /// operation has completed.
        /// </summary>
        private void TwoPlayerSessionOperationCompleted(object sender, OperationCompletedEventArgs e)
        {
            try
            {
                // End the asynchronous create network session operation.
                NetworkSession networkSession = NetworkSession.EndCreate(e.AsyncResult);

                // Create a component that will manage the session we just created.
                NetworkSessionComponent.Create(ScreenManager, networkSession);

                // Go to the lobby screen. We pass null as the controlling player,
                // because the lobby screen accepts input from all local players
                // who are in the session, not just a single controlling player.
                ScreenManager.AddScreen(new LocalNetworkLobby(networkSession), null);
            }
            catch (Exception exception)
            {
                NetworkErrorScreen errorScreen = new NetworkErrorScreen(exception);

                ScreenManager.AddScreen(errorScreen, ControllingPlayer);
            }

        } // End of the TwoPlayerSessionOperationCompleted method.


        /// <summary>
        /// Event handler for when the Live menu entry is selected.
        /// </summary>
        private void LiveMenuEntrySelected(object sender, PlayerIndexEventArgs e)
        {
            // Ask the user to select a storage device.
            if (Program.storageDevices[(int)e.PlayerIndex] == null || !Program.storageDevices[(int)e.PlayerIndex].IsConnected)
            {
                if (!Guide.IsVisible)
                {
                    // Set variables that can be used to create or find a session.
                    this.currentPlayerIndex = e.PlayerIndex;
                    this.currentSessionType = NetworkSessionType.PlayerMatch;

                    try
                    {
                        // Start to show the selector for a storage device.
                        AsyncCallback asyncCallback = new AsyncCallback(this.StartSessionAsync);
                        StorageDevice.BeginShowSelector(e.PlayerIndex, asyncCallback, null);
                    }
                    catch (Exception exception)
                    {
                        String exMessage = exception.Message;
                        ScreenManager.AddScreen(new MessageBoxScreen("A storage device could not be selected, please try again."), e.PlayerIndex);
                    }
    
                }
            }
            else
            {
                CreateOrFindSession(NetworkSessionType.PlayerMatch, e.PlayerIndex);
            }        
        }


        /// <summary>
        /// Event handler for when the System Link menu entry is selected.
        /// </summary>
        private void SystemLinkMenuEntrySelected(object sender, PlayerIndexEventArgs e)
        {

            // Ask the user to select a storage device.
            if (Program.storageDevices[(int)e.PlayerIndex] == null || !Program.storageDevices[(int)e.PlayerIndex].IsConnected)
            {
                if (!Guide.IsVisible)
                {

                    // Set variables that can be used to create or find a session.
                    this.currentPlayerIndex = e.PlayerIndex;
                    this.currentSessionType = NetworkSessionType.SystemLink;

                    try
                    {
                        // Start to show the selector for a storage device.
                        AsyncCallback asyncCallback = new AsyncCallback(this.StartSessionAsync);
                        StorageDevice.BeginShowSelector(e.PlayerIndex, asyncCallback, null);
                    }
                    catch (Exception exception)
                    {
                        String exMessage = exception.Message;
                        ScreenManager.AddScreen(new MessageBoxScreen("A storage device could not be selected, please try again."), e.PlayerIndex);
                    }
                }

            }
            else
            {
                CreateOrFindSession(NetworkSessionType.SystemLink, e.PlayerIndex);
            }
        }

        /// <summary>
        /// Event handler for when the Options menu entry is selected.
        /// </summary>
        private void OptionsMenuEntrySelected(object sender, PlayerIndexEventArgs e)
        {
            // Add the options screen.
            ScreenManager.AddScreen(new OptionScreen(), e.PlayerIndex);
        }

        /// <summary>
        /// This method will call the method to create or find a session when a storage device has
        /// been selected.
        /// </summary>
        /// <param name="IAsyncResult"></param>
        private void StartSessionAsync(IAsyncResult result)
        {

            try
            {
                // Store the storage device.
                Program.storageDevices[(int)this.currentPlayerIndex] = StorageDevice.EndShowSelector(result);

                // Create or find a session
                CreateOrFindSession(this.currentSessionType, this.currentPlayerIndex);

            }
            catch (Exception exception)
            {
                String exMessage = exception.Message;
                ScreenManager.AddScreen(new MessageBoxScreen("A storage device could not be selected, please try again."), this.currentPlayerIndex);
            }        

        } // End of the StartSessionAsync method.

        /// <summary>
        /// Helper method shared by the Live and System Link menu event handlers.
        /// </summary>
        private void CreateOrFindSession(NetworkSessionType sessionType, PlayerIndex playerIndex)
        {
            // First, we need to make sure a suitable gamer profile is signed in.
            ProfileSignInScreen profileSignIn = new ProfileSignInScreen(sessionType);

            // Hook up an event so once the ProfileSignInScreen is happy,
            // it will activate the CreateOrFindSessionScreen.
            profileSignIn.ProfileSignedIn += delegate
            {
                // Create the create or find session screen.
                GameScreen createOrFind = new CreateOrFindSessionScreen(sessionType);

                ScreenManager.AddScreen(createOrFind, playerIndex);

            };

            // Activate the ProfileSignInScreen.
            ScreenManager.AddScreen(profileSignIn, playerIndex);
        }

        /// <summary>
        /// When the user cancels the main menu, ask if they want to exit the sample.
        /// </summary>
        protected override void OnCancel(PlayerIndex playerIndex)
        {
            MessageBoxScreen confirmExitMessageBox = new MessageBoxScreen(Resources.ConfirmExitSample);

            confirmExitMessageBox.Accepted += ConfirmExitMessageBoxAccepted;

            ScreenManager.AddScreen(confirmExitMessageBox, playerIndex);
        }

        /// <summary>
        /// Event handler for when the user selects ok on the "are you sure
        /// you want to exit" message box.
        /// </summary>
        private void ConfirmExitMessageBoxAccepted(object sender, PlayerIndexEventArgs e)
        {
            ScreenManager.Game.Exit();
        }

        /// <summary>
        /// Event handler to show the marketplace.
        /// </summary>
        private void ShowMarketPlace(object sender, PlayerIndexEventArgs e)
        {
            // Create a message box screens.
            MessageBoxScreen confirmSignIn = new MessageBoxScreen(Resources.ConfirmSignIn);

            // Show the market place if the gamer are signed in with an online gamer profile.
            if (ProfileSignInScreen.CanPlayerBuyGame(e.PlayerIndex))
            {
                Guide.ShowMarketplace(e.PlayerIndex);
            }
            else
            {
                confirmSignIn.Accepted += delegate
                {
                    Guide.ShowSignIn(1, true);
                };

                ScreenManager.AddScreen(confirmSignIn, e.PlayerIndex);  
            }

        } // End of the ShowMarketPlace method.

        /// <summary>
        /// Draw the main menu screen.
        /// </summary>
        /// <param name="gameTime"></param>
        public override void Draw(GameTime gameTime)
        {
            // Call the draw method in the base class.
            base.Draw(gameTime);

            SpriteBatch spriteBatch = ScreenManager.SpriteBatch;
            SpriteFont font = ScreenManager.Font;

            spriteBatch.Begin();

            // Draw a autosave message (681).
            spriteBatch.DrawString(font, this.autosaveMessage, new Vector2(xOffset + 5, 720 - yOffset - 145), Color.DarkGray * TransitionAlpha, 0,
                                   Vector2.Zero, 1, SpriteEffects.None, 0);

            spriteBatch.End();

        } // End of the Draw method.

        #endregion

    } // End of the class

} // End of namespace
