using System.Collections.Generic;
using System.Linq;
using Assets.AnnotationPass;
using Assets.Cities;
using Assets.Common;
using UnityEngine;

namespace Assets.RenderPass
{
    public class CityRenderer : IRenderer
    {
        public void Render(Map map, Component component)
        {
            var go = new GameObject();
            go.AddComponent<MeshFilter>();
            go.AddComponent<MeshRenderer>();

            var cities = map.GetProperty<IEnumerable<City>>(LocateCitiesPass.CitiesProperty);
            var meshes = cities.Select(city => new CombineInstance { mesh = CreateCityMesh(city), transform = component.transform.localToWorldMatrix });
            
            var mesh = new Mesh();
            mesh.CombineMeshes(meshes.ToArray());
            
            go.GetComponent<MeshFilter>().sharedMesh = mesh;
        }

        private Mesh CreateCityMesh(City city)
        {
            List<Vector3> vertices  = new List<Vector3>();
            List<Vector3> normals  = new List<Vector3>();
            List<int> triangles = new List<int>();

            int start = 0, n = 0;
            foreach (var field in city.Fields)
            {
                start = vertices.Count;
                n = field.Boundary.Count;
                
                vertices.AddRange(field.Boundary.Select(p => p.ToVector3() + Vector3.up * 5));
                normals.AddRange(field.Boundary.Select(v => Vector3.up));
                triangles.AddRange(Triangulate(field).Select(x => x + start));

                start = vertices.Count;
                
                vertices.AddRange(field.Boundary.Select(p => p.ToVector3() + Vector3.up * 5));
                normals.AddRange(PointUtils.CalculateNormals(field.Boundary).Select(p => p.ToVector3()));
                vertices.AddRange(field.Boundary.Select(p => p.ToVector3()));
                normals.AddRange(PointUtils.CalculateNormals(field.Boundary).Select(p => p.ToVector3()));

                for (int i = 0; i < n; i++)
                {
                    triangles.AddRange(new []{ start + i, start + n + i, start + n + (i + 1) % n });
                    triangles.AddRange(new []{ start + i, start + n + (i + 1) % n, start + (i + 1) % n });
                }
            }
            
            return new Mesh
            {
                vertices = vertices.ToArray(),
                normals = normals.ToArray(),
                triangles = triangles.ToArray(), 
            };
        }

        private static IEnumerable<int> Triangulate(CityField field)
        {
            return new Triangulator(field.Boundary.Select(p => new Vector2((float)p.x, (float)p.y)).ToArray()).Triangulate();
        }

        public void DrawGizmos(Map map, Component component)
        {
            
        }
    }
}