using ANX.Framework.Graphics;
using ANX.Framework.NonXNA;
using Dx11 = SharpDX.Direct3D11;

// This file is part of the ANX.Framework created by the
// "ANX.Framework developer group" and released under the Ms-PL license.
// For details see: http://anxframework.codeplex.com/license

namespace ANX.RenderSystem.Windows.Metro
{
	public class DepthStencilState_Metro : BaseStateObject, INativeDepthStencilState
	{
		#region Private
		private Dx11.DepthStencilStateDescription description;
		private Dx11.DepthStencilState nativeDepthStencilState;
		private int referenceStencil;
		#endregion

		#region Public
		public StencilOperation CounterClockwiseStencilDepthBufferFail
		{
			set
			{
				Dx11.StencilOperation operation = FormatConverter.Translate(value);
				SetValueIfDifferentAndMarkDirty(
					ref description.BackFace.DepthFailOperation, ref operation);
			}
		}

		public StencilOperation CounterClockwiseStencilFail
		{
			set
			{
				Dx11.StencilOperation operation = FormatConverter.Translate(value);
				SetValueIfDifferentAndMarkDirty(
					ref description.BackFace.FailOperation, ref operation);
			}
		}

		public CompareFunction CounterClockwiseStencilFunction
		{
			set
			{
				Dx11.Comparison comparison = FormatConverter.Translate(value);
				SetValueIfDifferentAndMarkDirty(
					ref description.BackFace.Comparison, ref comparison);
			}
		}

		public StencilOperation CounterClockwiseStencilPass
		{
			set
			{
				Dx11.StencilOperation operation = FormatConverter.Translate(value);
				SetValueIfDifferentAndMarkDirty(
					ref description.BackFace.PassOperation, ref operation);
			}
		}

		public bool DepthBufferEnable
		{
			set
			{
				if (description.IsDepthEnabled != value)
				{
					description.IsDepthEnabled = value;
					isDirty = true;
				}
			}
		}

		public CompareFunction DepthBufferFunction
		{
			set
			{
				Dx11.Comparison comparison = FormatConverter.Translate(value);
				SetValueIfDifferentAndMarkDirty(
					ref description.DepthComparison, ref comparison);
			}
		}

		public bool DepthBufferWriteEnable
		{
			set
			{
				Dx11.DepthWriteMask writeMask = value ?
					Dx11.DepthWriteMask.All :
					Dx11.DepthWriteMask.Zero;
				SetValueIfDifferentAndMarkDirty(
					ref description.DepthWriteMask, ref writeMask);
			}
		}

		public int ReferenceStencil
		{
			set
			{
				SetValueIfDifferentAndMarkDirty(ref referenceStencil, ref value);
			}
		}

		public StencilOperation StencilDepthBufferFail
		{
			set
			{
				Dx11.StencilOperation operation = FormatConverter.Translate(value);
				SetValueIfDifferentAndMarkDirty(
					ref description.FrontFace.DepthFailOperation, ref operation);
			}
		}

		public bool StencilEnable
		{
			set
			{
				if (description.IsStencilEnabled != value)
				{
					description.IsStencilEnabled = value;
					isDirty = true;
				}
			}
		}

		public StencilOperation StencilFail
		{
			set
			{
				Dx11.StencilOperation operation = FormatConverter.Translate(value);
				SetValueIfDifferentAndMarkDirty(
					ref description.FrontFace.FailOperation, ref operation);
			}
		}

		public CompareFunction StencilFunction
		{
			set
			{
				Dx11.Comparison comparison = FormatConverter.Translate(value);
				SetValueIfDifferentAndMarkDirty(
					ref description.FrontFace.Comparison, ref comparison);
			}
		}

		public int StencilMask
		{
			set
			{
				byte stencilMask = (byte)value;
				//TODO: check range
				SetValueIfDifferentAndMarkDirty(
					ref description.StencilReadMask, ref stencilMask);
			}
		}

		public StencilOperation StencilPass
		{
			set
			{
				Dx11.StencilOperation operation = FormatConverter.Translate(value);
				SetValueIfDifferentAndMarkDirty(
					ref description.FrontFace.PassOperation, ref operation);
			}
		}

		public int StencilWriteMask
		{
			set
			{
				byte stencilWriteMask = (byte)value;
				//TODO: check range
				SetValueIfDifferentAndMarkDirty(
					ref description.StencilWriteMask, ref stencilWriteMask);
			}
		}

		public bool TwoSidedStencilMode
		{
			set
			{
				//TODO: check if we really need this. in xna this enables only counter clockwise stencil operations
			}
		}
		#endregion

		#region Constructor
		public DepthStencilState_Metro()
			: base()
		{
			description = new Dx11.DepthStencilStateDescription();
		}
		#endregion

		#region Apply
		public void Apply(GraphicsDevice graphicsDevice)
		{
			UpdateNativeDepthStencilState();
			bound = true;

			NativeDxDevice.Current.OutputMerger.SetDepthStencilState(
				nativeDepthStencilState, referenceStencil);
		}
		#endregion

		#region Dispose
		public void Dispose()
		{
			if (nativeDepthStencilState != null)
			{
				nativeDepthStencilState.Dispose();
				nativeDepthStencilState = null;
			}
		}
		#endregion

		#region UpdateNativeDepthStencilState
		private void UpdateNativeDepthStencilState()
		{
			if (isDirty == true || nativeDepthStencilState == null)
			{
				if (nativeDepthStencilState != null)
				{
					nativeDepthStencilState.Dispose();
					nativeDepthStencilState = null;
				}

				nativeDepthStencilState = new Dx11.DepthStencilState( NativeDxDevice.Current.NativeDevice, description);

				isDirty = false;
			}
		}
		#endregion
	}
}