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

namespace RigidBody {

	/// <summary>
	/// 無限平面の一部を描画する。
	/// </summary>
	class PlaneRenderer {
		readonly GraphicsDevice graphicsDevice;
		readonly BasicEffect effect;

		readonly VertexPositionTexture[] vertices = new VertexPositionTexture[4] {
				new VertexPositionTexture(new Vector3(-1, -1, 0), new Vector2(00, 00)),
				new VertexPositionTexture(new Vector3(+1, -1, 0), new Vector2(10, 00)),
				new VertexPositionTexture(new Vector3(+1, +1, 0), new Vector2(10, 10)),
				new VertexPositionTexture(new Vector3(-1, +1, 0), new Vector2(00, 10)),
			};

		readonly int[] indices = new int[] { 0, 1, 2, 2, 3, 0, };
		readonly int[] wireframeIndices = new int[] { 0, 1, 1, 2, 2, 3, 3, 0, };
		readonly VertexDeclaration declararation;
		Vector3 forward = Vector3.Forward;
		readonly Texture2D texture;

		public PlaneRenderer(GraphicsDevice graphicsDevice, Texture2D texture) {
			this.graphicsDevice = graphicsDevice;
			this.texture = texture;
			effect = new BasicEffect(graphicsDevice, null);
			declararation = new VertexDeclaration(graphicsDevice, VertexPositionTexture.VertexElements);
		}


		public void Draw(Plane plane, float scale, Matrix view, Matrix projection) {
			Matrix world;
			float dot;
			Vector3.Dot(ref forward, ref plane.Normal, out dot);
			if (dot == 1) {
				world = Matrix.Identity;
			} else {
				dot = MathHelper.Clamp(dot, -1, 1);
				Vector3 axis;
				Vector3.Cross(ref forward, ref plane.Normal, out axis);
				axis.Normalize();
				world = Matrix.CreateFromAxisAngle(axis, (float)Math.Acos(dot));
			}
			world *= Matrix.CreateScale(scale);

			effect.TextureEnabled = true;
			effect.Texture = texture;
			effect.World = world;
			effect.View = view;
			effect.Projection = projection;
			effect.Begin();
			graphicsDevice.VertexDeclaration = declararation;
			foreach (var pass in effect.CurrentTechnique.Passes) {
				pass.Begin();
				graphicsDevice.RenderState.FillMode = FillMode.Solid;
				graphicsDevice.DrawUserIndexedPrimitives(
					PrimitiveType.TriangleList, vertices, 0, vertices.Length, indices, 0, 2);
			}

			effect.TextureEnabled = false;
			effect.CommitChanges();

			foreach (var pass in effect.CurrentTechnique.Passes) {
				graphicsDevice.DrawUserIndexedPrimitives(
					PrimitiveType.LineList, vertices, 0, vertices.Length, wireframeIndices, 0, 4);
				pass.End();
			}
			effect.End();

		}
	}
}
