using System;
using System.Globalization;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.GamerServices;

namespace ECOT.Engine
{
    /// <summary>
    /// Enum describes the state transition state.
    /// </summary>
    public enum StateStatus
    {
        TransitionOn,
        Active,
        TransitionOff,
        Hidden,
    }


    /// <summary>
    /// A state is a single layer that has update and draw logic, and which
    /// can be combined with other layers to build up a complex menu system.
    /// For instance the main menu, the options menu, the "are you sure you
    /// want to quit" message box, and the main game itself are all implemented
    /// as screens.
    /// </summary>
    public abstract class GameState
    {
        #region Members

        private bool isPopup = false;
        private TimeSpan transitionOnTime = TimeSpan.Zero;
        private TimeSpan transitionOffTime = TimeSpan.Zero;
        private float transitionPosition = 1.0f;
        private StateStatus stateStatus = StateStatus.TransitionOn;
        private bool isExiting = false;
        private bool otherScreenHasFocus;
        private StateManager stateManager;

        #endregion

        #region Properties


        /// <summary>
        /// Normally when one state is brought up over the top of another,
        /// the first state will transition off to make room for the new
        /// one. This property indicates whether the state is only a small
        /// popup, in which case screens underneath it do not need to bother
        /// transitioning off.
        /// </summary>
        public bool IsPopup
        {
            get { return isPopup; }
            protected set { isPopup = value; }
        }


        public bool IsLoaded
        {
            get { return this.loaded; }
            set { this.loaded = value; }
        }

        /// <summary>
        /// Indicates how long the state takes to
        /// transition on when it is activated.
        /// </summary>
        public TimeSpan TransitionOnTime
        {
            get { return transitionOnTime; }
            protected set { transitionOnTime = value; }
        }


        /// <summary>
        /// Indicates how long the state takes to
        /// transition off when it is deactivated.
        /// </summary>
        public TimeSpan TransitionOffTime
        {
            get { return transitionOffTime; }
            protected set { transitionOffTime = value; }
        }


        /// <summary>
        /// Gets the current position of the state transition, ranging
        /// from zero (fully active, no transition) to one (transitioned
        /// fully off to nothing).
        /// </summary>
        public float TransitionPosition
        {
            get { return transitionPosition; }
            protected set { transitionPosition = value; }
        }


        /// <summary>
        /// Gets the current alpha of the state transition, ranging
        /// from 255 (fully active, no transition) to 0 (transitioned
        /// fully off to nothing).
        /// </summary>
        public byte TransitionAlpha
        {
            get { return (byte)(255 - TransitionPosition * 255); }
        }




        /// <summary>
        /// Gets the current state transition state.
        /// </summary>
        public StateStatus StateStatus
        {
            get { return stateStatus; }
            protected set { stateStatus = value; }
        }


        /// <summary>
        /// There are two possible reasons why a state might be transitioning
        /// off. It could be temporarily going away to make room for another
        /// state that is on top of it, or it could be going away for good.
        /// This property indicates whether the state is exiting for real:
        /// if set, the state will automatically remove itself as soon as the
        /// transition finishes.
        /// </summary>
        public bool IsExiting
        {
            get { return isExiting; }
            protected set { isExiting = value; }
        }


        /// <summary>
        /// Checks whether this state is active and can respond to user input.
        /// </summary>
        public bool IsActive
        {
            get
            {
                return !otherScreenHasFocus &&
                       (stateStatus == StateStatus.TransitionOn ||
                        stateStatus == StateStatus.Active);
            }
        }


        public GamerPresenceMode PresenceMode
        {
            get { return Gamer.SignedInGamers[0].Presence.PresenceMode; }
            set
            {
                foreach (SignedInGamer gamer in Gamer.SignedInGamers)
                    gamer.Presence.PresenceMode = value;
            }
        }
        /// <summary>
        /// Gets the manager that this state belongs to.
        /// </summary>
        public StateManager StateManager
        {
            get { return stateManager; }
            internal set { stateManager = value; }
        }

        #endregion

        #region Initialization


        public GameState()
        {
            this.TransitionOnTime = this.TransitionOffTime = TimeSpan.FromSeconds(0.5);

            Kernel.Instance.StateManager.RegisterState(this.ToString(), this);
            Kernel.Instance.OnLocaleChange += this.OnLocaleChangeHandler;
        }

        private void OnLocaleChangeHandler(CultureInfo locale)
        {
            if (this.loaded)
                this.UpdateLocale();
        }


        public virtual void Preload()
        {
            this.LoadGraphicsContent(true);
        }


        /// <summary>
        /// Load graphics content for the state.
        /// </summary>
        public virtual void LoadGraphicsContent(bool loadAllContent)
        {
            this.loaded = true;
        }


        /// <summary>
        /// Unload content for the state.
        /// </summary>
        public virtual void UnloadGraphicsContent(bool unloadAllContent) { }


        #endregion

        #region Update and Draw


        /// <summary>
        /// Allows the state to run logic, such as updating the transition position.
        /// Unlike HandleInput, this method is called regardless of whether the state
        /// is active, hidden, or in the middle of a transition.
        /// </summary>
        public virtual void Update(GameTime gameTime, bool otherScreenHasFocus,
                                                      bool coveredByOtherScreen)
        {
            this.otherScreenHasFocus = otherScreenHasFocus;

            if (isExiting)
            {
                // If the state is going away to die, it should transition off.
                stateStatus = StateStatus.TransitionOff;

                if (!UpdateTransition(gameTime, transitionOffTime, 1))
                {
                    // When the transition finishes, remove the state.
                    this.isExiting = false;
                    this.stateStatus = StateStatus.TransitionOn;
                }
            }
            else if (coveredByOtherScreen)
            {
                // If the state is covered by another, it should transition off.
                if (UpdateTransition(gameTime, transitionOffTime, 1))
                {
                    // Still busy transitioning.
                    stateStatus = StateStatus.TransitionOff;
                }
                else
                {
                    // Transition finished!
                    stateStatus = StateStatus.Hidden;
                }
            }
            else
            {
                // Otherwise the state should transition on and become active.
                if (UpdateTransition(gameTime, transitionOnTime, -1))
                {
                    // Still busy transitioning.
                    stateStatus = StateStatus.TransitionOn;
                }
                else
                {
                    // Transition finished!
                    stateStatus = StateStatus.Active;
                }
            }
        }


        /// <summary>
        /// Helper for updating the state transition position.
        /// </summary>
        bool UpdateTransition(GameTime gameTime, TimeSpan time, int direction)
        {
            // How much should we move by?
            float transitionDelta;

            if (time == TimeSpan.Zero)
                transitionDelta = 1;
            else
                transitionDelta = (float)(gameTime.ElapsedGameTime.TotalMilliseconds /
                                          time.TotalMilliseconds);

            // Update the transition position.
            transitionPosition += transitionDelta * direction;

            // Did we reach the end of the transition?
            if ((transitionPosition <= 0) || (transitionPosition >= 1))
            {
                transitionPosition = MathHelper.Clamp(transitionPosition, 0, 1);
                return false;
            }

            // Otherwise we are still busy transitioning.
            return true;
        }

        protected virtual void UpdateLocale() { }

        /// <summary>
        /// Allows the state to handle user input. Unlike Update, this method
        /// is only called when the state is active, and not when some other
        /// state has taken the focus.
        /// </summary>
        public virtual void HandleInput(InputState input) { }


        /// <summary>
        /// This is called when the state should draw itself.
        /// </summary>
        public abstract void Draw(GameTime gameTime);

        protected bool loaded = false;


        #endregion

        #region Public Methods


        /// <summary>
        /// Tells the state to go away. Unlike StateManager.RemoveState, which
        /// instantly kills the state, this method respects the transition timings
        /// and will give the state a chance to gradually transition off.
        /// </summary>
        public void Exit()
        {
             if (!this.IsExiting)
            {
                // Otherwise flag that it should transition off and then exit.
                isExiting = true;
            }
        }

        public virtual void SetParameters(params object[] args)
        {
        }

        public abstract override string ToString();


        #endregion
    }
}
