using Cryville.EEW.Core.Map;
using Poly2Tri;
using System.Buffers;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using UnityEngine;
using Color = UnityEngine.Color;

namespace Cryville.EEW.Unity.Map {
	[RequireComponent(typeof(MeshFilter))]
	[RequireComponent(typeof(MeshRenderer))]
	class PolygonRenderer : MonoBehaviour {
		Material _sharedMaterial;
		public Material Material {
			get => _meshRenderer.sharedMaterial;
			set {
				if (value == _sharedMaterial) return;
				if (_sharedMaterial != null) Destroy(_meshRenderer.sharedMaterial);
				_sharedMaterial = value;
				if (_sharedMaterial == null) return;
				_meshRenderer.sharedMaterial = Instantiate(_sharedMaterial);
				_meshRenderer.sharedMaterial.color = m_color;
			}
		}

		[SerializeField]
		Color m_color = Color.white;
		public Color Color {
			get => m_color;
			set {
				if (m_color == value) return;
				m_color = value;
				_meshRenderer.material.color = value;
			}
		}

		Mesh _mesh;
		MeshFilter _meshFilter;
		MeshRenderer _meshRenderer;
		void Awake() {
			_meshFilter = GetComponent<MeshFilter>();
			_meshRenderer = GetComponent<MeshRenderer>();
			if (!_meshFilter.mesh) {
				_meshFilter.mesh = new();
			}
			_mesh = _meshFilter.mesh;
			if (_sharedMaterial == null && _meshRenderer.sharedMaterial != null) {
				_sharedMaterial = _meshRenderer.sharedMaterial;
				_meshRenderer.sharedMaterial = Instantiate(_sharedMaterial);
				_meshRenderer.sharedMaterial.color = m_color;
			}
		}
		void OnDestroy() {
			Destroy(_mesh);
			Destroy(Material);
		}
		public void SetPolygon(IEnumerable<IEnumerable<PointF>> polygon) {
			_mesh.Clear();

			Polygon convertedPolygon = null;
			foreach (var loop in polygon) {
				var convertedLoop = new Polygon(loop.Select(p => {
					var v = MapTileUtils.WorldToTilePos(p).ToVector2();
					return new PolygonPoint(v.x, v.y);
				}));
				if (convertedPolygon is null) {
					convertedPolygon = convertedLoop;
				}
				else {
					convertedPolygon.AddHole(convertedLoop);
				}
			}

			var tcx = new DTSweepContext();
			tcx.PrepareTriangulation(convertedPolygon);
			DTSweep.Triangulate(tcx);

			var codeToIndex = new Dictionary<uint, int>();
			var vertices = ArrayPool<Vector3>.Shared.Rent(convertedPolygon.Points.Count);
			var triangles = ArrayPool<int>.Shared.Rent(convertedPolygon.Triangles.Count * 3);
			int vi = 0, ii = 0;
			foreach (var tri in convertedPolygon.Triangles) {
				for (int i = 2; i >= 0; --i) {
					var p = tri.Points[i];
					if (!codeToIndex.TryGetValue(p.VertexCode, out int index)) {
						codeToIndex.Add(p.VertexCode, index = vi++);
						vertices[index] = new(p.Xf, p.Yf);
					}
					triangles[ii++] = index;
				}
			}

			_mesh.SetVertices(vertices, 0, vi);
			_mesh.SetTriangles(triangles, 0, ii, 0);
			_mesh.RecalculateNormals();
			_mesh.RecalculateBounds();

			ArrayPool<int>.Shared.Return(triangles);
			ArrayPool<Vector3>.Shared.Return(vertices);
		}
	}
}
