using o0;
using o0.Num;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using UnityEngine;
using Color = UnityEngine.Color;

namespace ZIM
{
    // 亮区的点用来定位(计算center)，泛光区域的点用来计算radius
    public class PixelSpotArea
    {
        //public static float gridRatio0 = 6f / 1280;
        //public static float gridRatio1 = 20f / 720;

        public static int gridLength0 = 2;      // 亮区
        public static int gridLength1 = 10;     // 边缘泛光

        public static (int x, int y) GetGrid0(Vector2 v)
        {
            var m = (int)(v.x / gridLength0);
            var n = (int)(v.y / gridLength0);
            return (m, n);
        }

        public static (int x, int y) GetGrid1(Vector2 v)
        {
            var m = (int)(v.x / gridLength1);
            var n = (int)(v.y / gridLength1);
            return (m, n);
        }

        // 亮点聚类到区域
        public static List<PixelSpotArea> Cluster(List<Vector2> spotPoint, List<Vector2> brightPoint, Func<Vector2, bool> PointFilter = null)
        {
            var spotArea = new List<PixelSpotArea>();
            for (int i = 0; i < spotPoint.Count; i++)
            {
                var p = spotPoint[i];
                if (PointFilter != null && !PointFilter(p))        // 筛选是否在屏幕内
                    continue;

                var grid = PixelSpotArea.GetGrid0(p);
                var join = new SortedList<int, PixelSpotArea>();
                for (int j = 0; j < spotArea.Count; j++)
                {
                    var area = spotArea[j];
                    if (area.Include0(grid))
                        join.Add(j, area);
                }
                if (join.Count == 0)
                    spotArea.Add(new PixelSpotArea(p, grid, PixelSpotArea.GetGrid1(p)));
                else if (join.Count == 1)
                    join.First().Value.Join(p, grid, PixelSpotArea.GetGrid1(p));
                else
                {
                    var combine = new PixelSpotArea(join.Values);
                    combine.Join(p, grid, PixelSpotArea.GetGrid1(p));
                    spotArea.Add(combine);
                    for (int j = join.Count - 1; j >= 0; j--)
                        spotArea.RemoveAt(join.ElementAt(j).Key);
                }
            }
            return spotArea;
        }

        Vector2 center = default;       // 该项目里center不可能等于0,0
        public Vector2 Center
        {
            get
            {
                if (center != default)
                    return center;

                foreach (var p in Pixels0)
                {
                    center += p;
                }
                return center /= Pixels0.Count;
            }
        }
        float radius = 0;
        public float Radius
        {
            set
            {
                radius = value;
            }
            get
            {
                if (radius != 0)
                    return radius;

                //var radiusDic = new Dictionary<int, float>() { { 0, 0 }, { 45, 0 }, { 90, 0 }, { 135, 0 }, { 180, 0 }, { 225, 0 }, { 270, 0 }, { 315, 0 } };
                var radiusDic = new float[8];
                foreach (var p in Pixels0)
                {
                    var dir = p - Center;
                    var radius = dir.magnitude;
                    var degreeI = radius < 0.00001f ? 0 : (int)(dir.DegreeToXAxis() / 45);
                    if (degreeI > 7) degreeI = 0;       // 防止正好360度

                    if (radius > radiusDic[degreeI])
                        radiusDic[degreeI] = radius;
                }

                foreach (var p in Pixels1)
                {
                    var dir = p - Center;
                    var radius = dir.magnitude;
                    var degreeI = radius < 0.00001f ? 0 : (int)(dir.DegreeToXAxis() / 45);
                    if (degreeI > 7) degreeI = 0;

                    if (radius > radiusDic[degreeI])
                        radiusDic[degreeI] = radius;
                }
                return radius = radiusDic.Mean();
            }
        }

        public List<Vector2> Pixels0;
        public List<Vector2> Pixels1;

        HashSet<(int, int)> grids0;
        HashSet<(int, int)> grids1;

        public PixelSpotArea(Vector2 location, (int, int) grid0, (int, int) grid1)
        {
            Pixels0 = new List<Vector2>(1000);       // 预估的初始容量
            Pixels1 = new List<Vector2>(5000);
            this.grids0 = new HashSet<(int, int)>();        // hashset如果设置了太大的容量会降低效率
            this.grids1 = new HashSet<(int, int)>();
            Join(location, grid0, grid1);
        }

        public PixelSpotArea(IList<PixelSpotArea> areas)
        {
            Pixels0 = new List<Vector2>(1000);
            Pixels1 = new List<Vector2>(5000);
            grids0 = new HashSet<(int, int)>();
            grids1 = new HashSet<(int, int)>();
            foreach (var a in areas)
            {
                Pixels0.AddRange(a.Pixels0);
                grids0.AddRange(a.grids0);
                grids1.AddRange(a.grids1);
                //Center += a.Center * a.SpotPointCount;
                //SpotPointCount += a.SpotPointCount;
            }
            //Center /= SpotPointCount;
        }

        public bool Include0((int, int) grid) => grids0.Contains(grid);
        public bool Include1((int, int) grid) => grids1.Contains(grid);

        public void Join(Vector2 point) => Join(point, GetGrid0(point), GetGrid1(point));
        public void Join(Vector2 point, (int x, int y) grid0, (int x, int y) grid1)
        {
            Pixels0.Add(point);
            //SpotPointCount++;
            //Center += (point - Center) / SpotPointCount;
            grids0.AddRange(new (int, int)[]
            {
                    (grid0.x + -1, grid0.y +  0) , (grid0.x + 1 , grid0.y + 0),
                    (grid0.x + 1,  grid0.y + -1) , (grid0.x + -1, grid0.y + 1),
                    (grid0.x + 0,  grid0.y + -1) , (grid0.x + 0 , grid0.y + 1),
                    (grid0.x + -1, grid0.y + -1) , (grid0.x + 1 , grid0.y + 1),
                    grid0
            });
            grids1.AddRange(new (int, int)[]
            {
                    (grid1.x + -1, grid1.y +  0) , (grid1.x + 1 , grid1.y + 0),
                    (grid1.x + 1,  grid1.y + -1) , (grid1.x + -1, grid1.y + 1),
                    (grid1.x + 0,  grid1.y + -1) , (grid1.x + 0 , grid1.y + 1),
                    (grid1.x + -1, grid1.y + -1) , (grid1.x + 1 , grid1.y + 1),
                    grid1
            });
        }

        // 不再join之后，才可以用add添加周围泛光
        public void Add(Vector2 point)
        {
            //var radius = (point - Center).LengthManhattan();
            //if (radius > MaxRadius)
            //    MaxRadius = radius;
            Pixels1.Add(point);
            //var radius = (point - Center).LengthManhattan();
            //if (radius > MaxRadius)
            //    MaxRadius = radius;
        }

        public float TotalBrightness(Color[] pixels, Func<int, int, int> Vector2ToIndex, int outer_size = 3)
        {
            (int x, int y) rectMin = ((int)(Center.x - Radius - outer_size), (int)(Center.y - Radius - outer_size));
            (int x, int y) rectMax = ((int)(Center.x + Radius + outer_size), (int)(Center.y + Radius + outer_size));

            var total = 0f;
            Parallel.For(rectMin.x, rectMax.x, (i) =>
            {
                var t = 0;
                for (int j = rectMin.y; j < rectMax.y; j++)
                {
                    var index = Vector2ToIndex(i, j);
                    var b = pixels[index].Brightness(64);
                    t += b;
                }
                lock (InfraredLocate.locker)
                {
                    total += t;
                }
            });
            //total /= (rectMax.y - rectMin.y) * (rectMax.x - rectMin.x);

            return total;
        }
    }

}
