#region Using Statements
using System;
using ANX.Framework.NonXNA.Development;

#endregion

// 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.Framework.Graphics
{
    [PercentageComplete(100)]
    [Developer("Glatzemann")]
    [TestState(TestStateAttribute.TestState.InProgress)]
    public struct Viewport
	{
		#region Private
		private int x;
        private int y;
        private int width;
        private int height;
        private float near;
        private float far;
		#endregion

		#region Public
		public float AspectRatio
		{
			get
			{
				if (this.width != 0 && this.height != 0)
					return (float)width / (float)height;

				return 0f;
			}
		}

		public Rectangle Bounds
		{
			get
			{
				return new Rectangle(x, y, width, height);
			}
			set
			{
				this.x = value.X;
				this.y = value.Y;
				this.width = value.Width;
				this.height = value.Height;
			}
		}

		public int Height
		{
			get
			{
				return this.height;
			}
			set
			{
				this.height = value;
			}
		}

		public float MaxDepth
		{
			get
			{
				return this.far;
			}
			set
			{
				this.far = value;
			}
		}

		public float MinDepth
		{
			get
			{
				return this.near;
			}
			set
			{
				this.near = value;
			}
		}

		public Rectangle TitleSafeArea
		{
			get
			{
				// On Windows Xna simply returns the rectangle. Check if we need any other implementation on other platforms!
				return new Rectangle(x, y, width, height);
			}
		}

		public int Width
		{
			get
			{
				return this.width;
			}
			set
			{
				this.width = value;
			}
		}

		public int X
		{
			get
			{
				return this.x;
			}
			set
			{
				this.x = value;
			}
		}

		public int Y
		{
			get
			{
				return this.y;
			}
			set
			{
				this.y = value;
			}
		}
		#endregion

		#region Constructor
		public Viewport(int x, int y, int width, int height)
        {
            this.x = x;
            this.y = y;
            this.width = width;
            this.height = height;
            this.near = 0f;
            this.far = 1f;
        }

        public Viewport(Rectangle bounds)
        {
            this.x = bounds.X;
            this.y = bounds.Y;
            this.width = bounds.Width;
            this.height = bounds.Height;
			this.near = 0f;
            this.far = 1f;
        }
		#endregion

		#region Project
		public Vector3 Project(Vector3 source, Matrix projection, Matrix view, Matrix world)
		{
			Matrix wv;
			Matrix wvp;
			Matrix.Multiply(ref world, ref view, out wv);
			Matrix.Multiply(ref wv, ref projection, out wvp);
			Vector3 vector;
			Vector3.Transform(ref source, ref wvp, out vector);
			float num = source.X * wvp.M14 + source.Y * wvp.M24 + source.Z * wvp.M34 + wvp.M44;
			if (WithinEpsilon(num) == false)
				vector /= num;
			vector.X = (vector.X + 1f) * 0.5f * (float)width + (float)x;
			vector.Y = (-vector.Y + 1f) * 0.5f * (float)height + (float)y;
			vector.Z = vector.Z * (far - near) + near;
			return vector;
		}
		#endregion

		#region Unproject
		public Vector3 Unproject(Vector3 source, Matrix projection, Matrix view, Matrix world)
		{
			Matrix wv;
			Matrix wvp;
			Matrix.Multiply(ref world, ref view, out wv);
			Matrix.Multiply(ref wv, ref projection, out wvp);
			wvp = Matrix.Invert(wvp);
			source.X = (source.X - (float)x) / (float)width * 2f - 1f;
			source.Y = -((source.Y - (float)y) / (float)height * 2f - 1f);
			source.Z = (source.Z - near) / (far - near);
			Vector3 vector;
			Vector3.Transform(ref source, ref wvp, out vector);
			float num = source.X * wvp.M14 + source.Y * wvp.M24 + source.Z * wvp.M34 + wvp.M44;
			return (WithinEpsilon(num) == false) ? vector /= num : vector;
		}
		#endregion

		#region WithinEpsilon
		private static bool WithinEpsilon(float num)
		{
			num -= 1f;
			return -1.401298E-45f <= num && num <= 1.401298E-45f;
		}
		#endregion

		#region ToString
		public override string ToString()
        {
            return String.Format("Viewport X: {0} Y:{1} Width: {2} Height: {3} AspectRatio: {4} MinDepth: {5} MaxDepth: {6}",
				X, Y, Width, Height, AspectRatio, MinDepth, MaxDepth);
        }
		#endregion
    }
}