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

namespace XELF.Sphynx {

	enum Mapping {
		Wrap,
		Fit,

		Max,
	}

	class Mesh {

		public Matrix SourceTextureTransform = Matrix.Identity;
		public Mapping Mapping;

		public List<VertexPositionColorTexture> Vertices;
		public List<int> Indices;

		public Mesh() {
			Vertices = new List<VertexPositionColorTexture>();
			Indices = new List<int>();
		}
		public Mesh(List<VertexPositionColorTexture> Vertices, List<int> Indices) {
			this.Vertices = Vertices;
			this.Indices = Indices;
		}

		public Mesh Clone() {
			var value = new Mesh(Vertices.Clone(), Indices.Clone())
			{
				SourceTextureTransform = this.SourceTextureTransform,
				Mapping = this.Mapping,
			};
			return value;
		}

		public void AddTriangle(int i0, int i1, int i2) {
			Indices.Add(i0);
			Indices.Add(i1);
			Indices.Add(i2);
		}
		public void BuildVertices(FarseerGames.FarseerPhysics.Collisions.Vertices vertices) {
			Vertices.Clear();
			for (int i = 0; i < vertices.Count; i++) {
				Vertices.Add(new VertexPositionColorTexture(
					new Vector3(vertices[i], 0), Color.White, Vector2.Zero));
			}
		}

		public void BuildTextureCoordinates(FarseerGames.FarseerPhysics.Collisions.Vertices vertices) {
			var bounds = new FarseerGames.FarseerPhysics.Collisions.AABB(vertices);

			Matrix transform;
			switch (Mapping) {
			default:
			case Mapping.Fit:
				transform = Matrix.CreateTranslation(new Vector3(-bounds.Min, 0))
					* Matrix.CreateScale(new Vector3(1 / (bounds.Max.X - bounds.Min.X), 1 / (bounds.Max.Y - bounds.Min.Y), 1))
					* SourceTextureTransform;
				break;
			case Mapping.Wrap:
				transform = SourceTextureTransform;
				break;
			}

			for (int i = 0; i < Vertices.Count; i++) {
				var vertex = Vertices[i];
				Vector2 point = new Vector2(Vertices[i].Position.X, Vertices[i].Position.Y);
				Vector2 result;
				Vector2.Transform(ref point, ref transform, out result);
				vertex.TextureCoordinate = result;
				Vertices[i] = vertex;
			}
		}

		public void Clear() {
			Vertices.Clear();
			Indices.Clear();
		}
	}

	static class Tessellator {
		public static void BuildFrom(this Mesh mesh, FarseerGames.FarseerPhysics.Collisions.Vertices vertices) {
			mesh.Clear();
			mesh.BuildVertices(vertices);

			List<int> corners = new List<int>(vertices.Count);
			for (int c = 0; c < vertices.Count; c++) {
				corners.Add(c);
			}

			List<Vector2> points = new List<Vector2>();
			while (corners.Count >= 3) {
				var remainCorners = corners.Count;
				for (int c = 0; c < corners.Count; c++) {
					var i0 = corners[(c + corners.Count - 1) % corners.Count];
					var i1 = corners[c];
					var i2 = corners[(c + 1) % corners.Count];
					Vector2 p0 = vertices[i0];
					Vector2 p1 = vertices[i1];
					Vector2 p2 = vertices[i2];

					var area = ComputeAreaOfParallelogram(ref p0, ref p1, ref p2);

					if (area >= 0) {

						points.Clear();
						FarseerGames.FarseerPhysics.Collisions.CollisionHelper.LineSegmentVerticesIntersect(
							ref p0, ref p2, vertices, ref points);
						if (points.Count <= 3) {
							mesh.AddTriangle(i0, i1, i2);
							corners.RemoveAt(c);
						}
					}
				}

				if (corners.Count == remainCorners)
					break; // 進展しないときは断念
			}

			mesh.BuildTextureCoordinates(vertices);
		}

		/// <summary>
		/// 平行四辺形の符号つき面積（三角形の2倍の符号つき面積）
		/// </summary>
		/// <param name="p0">頂点</param>
		/// <param name="p1">頂点</param>
		/// <param name="p2">頂点</param>
		/// <returns></returns>
		public static float ComputeAreaOfParallelogram(ref Vector2 p0, ref Vector2 p1, ref Vector2 p2) {
			var e0 = p0 - p1;
			var e2 = p2 - p1;
			return e0.X * e2.Y - e0.Y * e2.X;
		}

	}

}
