using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using RecrownedAthenaeum.Render;
using System.Collections.Generic;

namespace RecrownedAthenaeum.ScreenSystem
{
    /// <summary>
    /// Represents one of the poosible states a screen can be in.
    /// </summary>
    public enum ScreenState {
        /// <summary>
        /// Screen is transitioning in.
        /// </summary>
        EnterTransition,
        /// <summary>
        /// Screen is transitioning out.
        /// </summary>
        ExitTransition,
        /// <summary>
        /// Screen is currently displayed normally without transition.
        /// </summary>
        Normal
    }

    /// <summary>
    /// A screen represents a virtual system of management that controls an system of items to be displayed.
    /// </summary>
    public class Screen
    {
        /// <summary>
        /// Transitions to apply.
        /// </summary>
        public readonly List<ITransition> Transitions;

        /// <summary>
        /// Whether or not to continue rendering this screen onto a buffer for the benefit of the next screen to use as a transition.
        /// </summary>
        public  bool UseRenderTargetForExitTransition { get; private set; }

        /// <summary>
        /// The background color to be used to clear the screen.
        /// </summary>
        public Color BackgroundColor;

        /// <summary>
        /// The next screen to be displayed after exit transition finishes. May be null, leading to transition to previous screen or loading screen.
        /// </summary>
        public Screen NextScreen { get; set; }

        /// <summary>
        /// The current window dimensions.
        /// </summary>
        public int width, height;

        /// <summary>
        /// Current state of the screen.
        /// </summary>
        public ScreenState State { get; private set; }
        
        /// <summary>
        /// Creates a new screen.
        /// </summary>
        /// <param name="useEnterTransition">True to start in entering transition state.</param>
        public Screen(bool useEnterTransition = false)
        {
            State = useEnterTransition ? ScreenState.EnterTransition : ScreenState.Normal;
            Transitions = new List<ITransition>();
        }

        /// <summary>
        /// Called when screen size is set.
        /// </summary>
        /// <param name="width">The width of the screen.</param>
        /// <param name="height">The height of the screen.</param>
        public virtual void ApplySize(int width, int height)
        {
            this.width = width;
            this.height = height;
        }

        /// <summary>
        /// Called to update the screen.
        /// </summary>
        /// <param name="gameTime">Game time information.</param>
        public virtual void Update(GameTime gameTime)
        {

        }

        /// <summary>
        /// Called to draw this screen.
        /// </summary>
        /// <param name="spriteBatch">SpriteBatch to use.</param>
        public virtual void Draw(ConsistentSpriteBatch spriteBatch)
        {
            foreach (ITransition transition in Transitions)
            {
                transition.DrawTransition(spriteBatch);
            }
        }

        /// <summary>
        /// Updates the transition based on the current state of the screen.
        /// </summary>
        /// <param name="delta">Time passed since last frame in seconds.</param>
        /// <param name="waiting">If the this transition should wait.</param>
        /// <returns>Only returns true if exit transition is complete. Returns false otherwise.</returns>
        public bool UpdateTransition(double delta, bool waiting)
        {
            switch (State)
            {
                case ScreenState.EnterTransition:
                    EnteringTransition(delta, waiting);
                    break;
                case ScreenState.ExitTransition:
                    return ExitingTransition(delta, waiting);
            }

            return false;
        }

        private void EnteringTransition(double delta, bool waiting)
        {
            bool complete = true;
            for (int transitionID = 0; transitionID < Transitions.Count; transitionID++)
            {
                ITransition transition = Transitions[transitionID];
                if (!transition.UpdateEnteringTransition(delta, waiting))
                {
                    complete = false;
                }
            }
            if (complete && State != ScreenState.ExitTransition)
            {
                State = ScreenState.Normal;
            }
        }

        private bool ExitingTransition(double delta, bool waiting)
        {
            bool complete = true;
            foreach (ITransition transition in Transitions)
            {
                if (!transition.UpdateExitingTransition(delta, waiting))
                {
                    complete = false;
                }

            }
            return complete;
        }

        /// <summary>
        /// Called when the screen is shown.
        /// </summary>
        public virtual void Show()
        {
            foreach (ITransition transition in Transitions)
            {
                transition.InitiateTransition(new Rectangle(0, 0, width, height));
            }
        }

        /// <summary>
        /// Called when this screen is no longer the displayed screen.
        /// </summary>
        public virtual void Hide()
        {

        }

        /// <summary>
        /// Called whenever the status of assets changes from being loaded to unloaded or unloaded to loaded.
        /// </summary>
        /// <param name="state">True for loaded, false for unloaded.</param>
        public virtual void AssetLoadStateChange(bool state)
        {

        }


        /// <summary>
        /// Call this to begin exit transition.
        /// </summary>
        /// <param name="UseRenderTargetForExitTransition">Whether or not to use a render target for the next screen to use.</param>
        public void StartExitTransition(bool UseRenderTargetForExitTransition = false)
        {
            this.UseRenderTargetForExitTransition = UseRenderTargetForExitTransition;
            State = ScreenState.ExitTransition;
        }
        
        /// <summary>
        /// Called everytime the previous screen frame buffer updates. Used for transition purposes.
        /// </summary>
        /// <param name="previousScreenFrame">The previous screen's render buffer.</param>
        public virtual void UpdatePreviousScreenFrame(RenderTarget2D previousScreenFrame)
        {
            foreach (ITransition transition in Transitions)
            {
                transition.UpdatePreviousScreenFrame(previousScreenFrame);
            }
        }
    }
}
