using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace RecrownedAthenaeum.Render
{
    /// <summary>
    /// A <see cref="SpriteBatch"/> that keeps it's settings through begin and end unless manually changed either by the <see cref="Begin(SpriteSortMode, BlendState, SamplerState, DepthStencilState, RasterizerState, Effect, Matrix?)"/> or through changing the fields. Note that changing the fields while the batch has begun will not take effect until the next time the batch is started.
    /// Casting this as a <see cref="SpriteBatch"/> will not persist the configuration and will call the normal <see cref="SpriteBatch.Begin(SpriteSortMode, BlendState, SamplerState, DepthStencilState, RasterizerState, Effect, Matrix?)"/>.
    /// </summary>
    public class ConsistentSpriteBatch : SpriteBatch
    {
        /// <summary>
        /// How to blend the colors. Uses <see cref="SpriteBatch"/>'s default if not set before or during <see cref="Begin(SpriteSortMode, BlendState, SamplerState, DepthStencilState, RasterizerState, Effect, Matrix?)"/> call.
        /// Changes will only take effect on <see cref="Begin(SpriteSortMode, BlendState, SamplerState, DepthStencilState, RasterizerState, Effect, Matrix?)"/> call.
        /// </summary>
        public BlendState blendState;

        /// <summary>
        /// The state of sampler to use for the spritebatch. Uses <see cref="SpriteBatch"/>'s default if not set before or during <see cref="Begin(SpriteSortMode, BlendState, SamplerState, DepthStencilState, RasterizerState, Effect, Matrix?)"/> call.
        /// Changes will only take effect on <see cref="Begin(SpriteSortMode, BlendState, SamplerState, DepthStencilState, RasterizerState, Effect, Matrix?)"/> call.
        /// </summary>
        public SamplerState samplerState;

        /// <summary>
        /// The state of the depth-stencil buffer. Uses <see cref="SpriteBatch"/>'s defaultdefault if not set before or during <see cref="Begin(SpriteSortMode, BlendState, SamplerState, DepthStencilState, RasterizerState, Effect, Matrix?)"/> call.
        /// Changes will only take effect on <see cref="Begin(SpriteSortMode, BlendState, SamplerState, DepthStencilState, RasterizerState, Effect, Matrix?)"/> call.
        /// </summary>
        public DepthStencilState depthStencilState;

        /// <summary>
        /// The state of rasterizer to use. Uses <see cref="SpriteBatch"/>'s default if not set before or during <see cref="Begin(SpriteSortMode, BlendState, SamplerState, DepthStencilState, RasterizerState, Effect, Matrix?)"/> call.
        /// Changes will only take effect on <see cref="Begin(SpriteSortMode, BlendState, SamplerState, DepthStencilState, RasterizerState, Effect, Matrix?)"/> call.
        /// </summary>
        public RasterizerState rasterizerState;

        /// <summary>
        /// An effect to apply to the batch. Uses <see cref="SpriteBatch"/>'s default if not set before or during <see cref="Begin(SpriteSortMode, BlendState, SamplerState, DepthStencilState, RasterizerState, Effect, Matrix?)"/> call.
        /// Changes will only take effect on <see cref="Begin(SpriteSortMode, BlendState, SamplerState, DepthStencilState, RasterizerState, Effect, Matrix?)"/> call.
        /// </summary>
        public Effect effect;

        /// <summary>
        /// A matrix to be applied to transform the sprites geometry. An identity matrix is used if not provided before or during <see cref="Begin(SpriteSortMode, BlendState, SamplerState, DepthStencilState, RasterizerState, Effect, Matrix?)"/> call.
        /// Changes will only take effect on <see cref="Begin(SpriteSortMode, BlendState, SamplerState, DepthStencilState, RasterizerState, Effect, Matrix?)"/> call.
        /// </summary>
        public Matrix? transformMatrix;

        /// <summary>
        /// Creates a consistent sprite batch with default values.
        /// </summary>
        /// <param name="graphicsDevice">The graphics device to use to create a <see cref="SpriteBatch"/>.</param>
        public ConsistentSpriteBatch(GraphicsDevice graphicsDevice) : base(graphicsDevice)
        {
        }

        /// <summary>
        /// Begins the consistent sprite batch. The configuration passed to this function is saved for later begin calls.
        /// </summary>
        /// <param name="sortMode">Defines the spritebatch's method of sorting the items in each batch. Uses <see cref="SpriteBatch"/>'s default.</param>
        /// <param name="blendState">How to blend the colors. Uses <see cref="SpriteBatch"/>'s default if not set and field is also not set.</param>
        /// <param name="samplerState">The state of sampler to use for the spritebatch. Uses <see cref="SpriteBatch"/>'s default if not set and field is also not set.</param>
        /// <param name="depthStencilState">What type of rasterization to use. Uses <see cref="SpriteBatch"/>'s default if not set and field is also not set.</param>
        /// <param name="rasterizerState">What type of rasterization to use. Uses <see cref="SpriteBatch"/>'s default if not set and field is also not set.</param>
        /// <param name="effect">An effect to apply to the batch. Uses <see cref="SpriteBatch"/>'s default if not set and field is also not set.</param>
        /// <param name="transformMatrix">A matrix to be applied to transform the sprites geometry. Uses <see cref="SpriteBatch"/>'s default if not set and field is also not set.</param>
        public new void Begin(SpriteSortMode sortMode = SpriteSortMode.Deferred, BlendState blendState = null, SamplerState samplerState = null, DepthStencilState depthStencilState = null, RasterizerState rasterizerState = null, Effect effect = null, Matrix? transformMatrix = null)
        {
            if (blendState != null) this.blendState = blendState;
            if (samplerState != null) this.samplerState = samplerState;
            if (depthStencilState != null) this.depthStencilState = depthStencilState;
            if (rasterizerState != null) this.rasterizerState = rasterizerState;
            if (effect != null) this.effect = effect;
            if (transformMatrix != null) this.transformMatrix = transformMatrix;

            base.Begin(
                sortMode: sortMode,
                blendState: this.blendState,
                samplerState: this.samplerState,
                depthStencilState: this.depthStencilState,
                rasterizerState: this.rasterizerState,
                effect: this.effect,
                transformMatrix: this.transformMatrix);
        }

        /// <summary>
        /// Begins the consistent sprite batch without saving the configuration. Useful for one time changes to one portion of the configuration.
        /// </summary>
        /// <param name="sortMode">Defines the spritebatch's method of sorting the items in each batch. Uses <see cref="SpriteBatch"/>'s default.</param>
        /// <param name="blendState">How to blend the colors. Uses <see cref="SpriteBatch"/>'s default if not set and field is also not set.</param>
        /// <param name="samplerState">The state of sampler to use for the spritebatch. Uses <see cref="SpriteBatch"/>'s default if not set and field is also not set.</param>
        /// <param name="depthStencilState">What type of rasterization to use. Uses <see cref="SpriteBatch"/>'s default if not set and field is also not set.</param>
        /// <param name="rasterizerState">What type of rasterization to use. Uses <see cref="SpriteBatch"/>'s default if not set and field is also not set.</param>
        /// <param name="effect">An effect to apply to the batch. Uses <see cref="SpriteBatch"/>'s default if not set and field is also not set.</param>
        /// <param name="transformMatrix">A matrix to be applied to transform the sprites geometry. Uses <see cref="SpriteBatch"/>'s default if not set and field is also not set.</param>
        public void BeginWithoutSaving(SpriteSortMode sortMode = SpriteSortMode.Deferred, BlendState blendState = null, SamplerState samplerState = null, DepthStencilState depthStencilState = null, RasterizerState rasterizerState = null, Effect effect = null, Matrix? transformMatrix = null)
        {
            base.Begin(
            sortMode: sortMode,
            blendState: blendState ?? this.blendState,
            samplerState: samplerState ?? this.samplerState,
            depthStencilState: depthStencilState ?? this.depthStencilState,
            rasterizerState: rasterizerState ?? this.rasterizerState,
            effect: effect ?? this.effect,
            transformMatrix: transformMatrix ?? this.transformMatrix);
        }
    }
}
