using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Assets.AnnotationPass;
using Assets.Common;
using Assets.GridGenerator;
using Assets.PointDistribution;
using Assets.Utils;
using UnityEngine;
using UnityEngine.Serialization;
using Random = System.Random;

namespace Assets
{
    [Serializable]
    public class GraphGeneratorDebug
    {
        public bool enabled = true;
        
        public bool displayPoints = true;
        public bool displayBoundary = true;
        public bool displayVertices = true;
        public bool displayNeighbours = false;
        public bool displayLabels = false;
        public bool displayEdges = true;
        public bool displayLocations = true;
        public bool displayLocationCells = true;
        public bool displayLocationEdges = true;
        public bool displayLocationPoints = true;

        [NonSerialized] public float generationTime = 0.0f;
    }

    [Serializable]
    public class LocationType
    {
        public string name;
        public Color color;
        public float height = 0.0f;
    }

    [ExecuteInEditMode]
    public class LandmassGenerator : MonoBehaviour
    {
        [NonSerialized] public Map Map;
        [NonSerialized] public Thread GenerationThread;
        
        public GraphGeneratorDebug debug;
        
        public Vector2 size = new Vector2();
        public List<LocationType> types = new List<LocationType>();
        
        [Range(2.0f, 64.0f)]
        public float radius = 8;
        public int seed = Environment.TickCount;

        public Graph<MapSite> SitesGraph => Map?.Sites;
        public Graph<Point> BoundariesGraph => Map?.Boundaries;
        public Graph<Location> LocationGraph => Map?.Metadata.GetProperty<Graph<Location>>(LocationPass.MapLocationsProperty);

        public void Generate()
        {
            GenerationThread?.Abort();
            GenerationThread = new Thread(() =>
            { 
                var generator = CreateMapGenerator();
                var result = generator.Generate(size.x, size.y);
                
                lock (GetType())
                {
                    Map = result;
                }
            });
            
            GenerationThread.Start();
        }

        public void OnDisable()
        {
            GenerationThread?.Abort();
        }

        private MapGenerator CreateMapGenerator()
        {
            var generator = new MapGenerator(new VoronoiGridGenerator(seed, new PoissonDiskSampler(radius) { Generator = new Random(seed) }));

            generator.AddAnnotationPass(new LocationPass(types.Prepend(new LocationType { name = "Ocean", height = -1 }).Select(t => new Location { Type = t }), new Random(seed)));
            
            return generator;
        }

        public void Reset()
        {
            GenerationThread?.Abort();
        }

        void OnDrawGizmos()
        {
            if (!debug.enabled || Map == null) return;
            
            if (debug.displayBoundary)
            {
                Gizmos.color = Color.gray;

                Gizmos.DrawLine(new Vector3(0, 0, 0), new Vector3(size.x, 0, 0));
                Gizmos.DrawLine(new Vector3(0, 0, 0), new Vector3(0, 0, size.y));
                Gizmos.DrawLine(new Vector3(size.x, 0, size.y), new Vector3(size.x, 0, 0));
                Gizmos.DrawLine(new Vector3(size.x, 0, size.y), new Vector3(0, 0, size.y));
            }
            
            if (debug.displayPoints)
            {
                Gizmos.color = Color.white;
                DebugUtils.DisplayGraphVertices(Map.Sites, s => s.Center, displayLabels: debug.displayLabels);
            }

            if (debug.displayNeighbours)
            {
                Gizmos.color = Color.yellow;
                DebugUtils.DisplayGraphEdges(Map.Sites, s => s.Center);
            }

            if (debug.displayVertices)
            {
                Gizmos.color = Color.blue;
                DebugUtils.DisplayGraphVertices(Map.Boundaries, displayLabels: debug.displayLabels);
            }

            if (debug.displayEdges)
            {
                Gizmos.color = Color.white;
                DebugUtils.DisplayGraphEdges(Map.Boundaries);
            }
            
            Gizmos.color = Color.blue;

            if (!Map.Metadata.HasProperty(LocationPass.MapLocationsProperty)) return;

            var locations = Map.Metadata.GetProperty<Graph<Location>>(LocationPass.MapLocationsProperty);
            
            if (debug.displayLocationCells)
            {
                var cells = Map.Sites.Vertices
                    .Where(s => s.Metadata.HasProperty(LocationPass.SiteLocationProperty))
                    .Where(s => s.Metadata.GetProperty<Location>(LocationPass.SiteLocationProperty).Type.name != "Ocean");
                
                foreach (var site in cells)
                {
                    var location = site.Metadata.GetProperty<Location>(LocationPass.SiteLocationProperty);
                    
                    Gizmos.color = location.Type.color;
                    Gizmos.DrawSphere(site.Center.ToVector3(), 2);
                }
            }
//
//            if (debug.displayLocations && _locationGraph != null)
//            {
//                DisplayGraphEdges(_locationGraph.Morph(a => a.Item1));
//            }
//
//            if (debug.displayLocationPoints)
//            {
//                foreach (var location in _locations.Skip(1))
//                {
//                    Gizmos.color = location.Type.color;
//                    foreach (var point in location.BoundaryPoints)
//                    {
//                        var v = VoronoiGenerator.Voronoi.Vertices[point].ToVector3();
//                        Gizmos.DrawSphere(v, 1);
//                        if (debug.displayLabels) Handles.Label(v + Vector3.right, $"{point} at {v.x:F2}, {v.y:f2}");
//                    }

//                }
//            }
//
            if (debug.displayLocationEdges)
            {
                foreach (var location in Map.Metadata.GetProperty<Graph<Location>>(LocationPass.MapLocationsProperty).Vertices)
                {
                    Gizmos.color = location.Type.color;
                    foreach (var (a, b) in location.BoundaryEdges)
                    {
                        Gizmos.DrawLine(BoundariesGraph[a].ToVector3(), BoundariesGraph[b].ToVector3());
                    }
                }
            }
        }
    }
}