using o0.Geometry;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.UI;
using o0.Geometry2D.Float;

namespace o0.Project
{
    public static partial class Extension
    {
        public static (Vector, float) lineSegGradient(Matrix mat, Matrix dir, float gradient, Vector start, Vector end, bool reverseXY)
        {
            int startX, endX;
            if (start.x < end.x)
            {
                startX = Mathf.RoundToInt(start.x) + 1;
                endX = Mathf.RoundToInt(end.x) + 1;
            }
            else
            {
                startX = Mathf.RoundToInt(start.x) - 1;
                endX = Mathf.RoundToInt(end.x) - 1;
            }
            if(startX == endX)
                return (new Vector(start.x, start.y), 0);

            var xDiff = end.x - start.x;
            var xToYRate = (float)(end.y - start.y) / xDiff;
            Func<float, float> xToYFunc = (x) => (x - start.x) * xToYRate + start.y;

            var B1 = Mathf.RoundToInt(start.x);
            var B1V = mat[B1, Mathf.RoundToInt(start.y)];
            var Sum1 = B1V;

            foreach (var x in startX.Range(endX))
            {
                var y = xToYFunc(x);
                var y1 = Mathf.FloorToInt(y);
                var y2 = Mathf.CeilToInt(y);
                var y0 = y1 - 1;
                var y3 = y2 + 1;
                int C0I, C3I;
                int C1I, C2I;
                if (reverseXY)
                {
                    C0I = mat.Index(y0, x);
                    C1I = mat.Index(y1, x);
                    C2I = mat.Index(y2, x);
                    C3I = mat.Index(y3, x);
                }
                else
                {
                    C0I = mat.Index(x, y0);
                    C1I = mat.Index(x, y1);
                    C2I = mat.Index(x, y2);
                    C3I = mat.Index(x, y3);
                }
                var C0A = dir.Element[C0I];
                var C1A = dir.Element[C1I];
                var C2A = dir.Element[C2I];
                var C3A = dir.Element[C3I];
                //var CV = (y2 - y) * mat.Element[C1I] + (y - y1) * mat.Element[C2I];
                var CV = o0.Max(mat.Element[C0I], mat.Element[C1I], mat.Element[C2I], mat.Element[C3I]);
                //var avaAngle2 = 22.5;
                var avaAngle = 90;
                var halfAvaAngle = avaAngle/2;
                //var avaAngle2 = 33.75;

                if (!(MathF.Abs(C0A - gradient) <= halfAvaAngle || MathF.Abs(C0A - 360 - gradient) <= halfAvaAngle  || MathF.Abs(C0A + 360 - gradient) <= halfAvaAngle
                || MathF.Abs(C1A - gradient) <= halfAvaAngle    || MathF.Abs(C1A - 360 - gradient) <= halfAvaAngle  || MathF.Abs(C1A + 360 - gradient) <= halfAvaAngle
                || MathF.Abs(C2A - gradient) <= halfAvaAngle    || MathF.Abs(C2A - 360 - gradient) <= halfAvaAngle  || MathF.Abs(C2A + 360 - gradient) <= halfAvaAngle
                || MathF.Abs(C3A - gradient) <= halfAvaAngle    || MathF.Abs(C3A - 360 - gradient) <= halfAvaAngle  || MathF.Abs(C3A + 360 - gradient) <= halfAvaAngle))
                {
                    break;
                }
                B1 = x;
                Sum1 += CV;
            }
            return (new Vector(B1, xToYFunc(B1)), Sum1);
        }
        public static (Line, float) lineSegGradient(Matrix mat, Matrix dir, float gradient, Vector start)
        {
            var lineDir = (gradient + 90) % 180;//需要检测的线的角度
            var line = mat.MaskLine(start, lineDir);

            var range = (6, 2);
            if (lineDir < 45 || lineDir > 135)
            {
                //var (p1, sum1) = lineSegGradient(mat, dir, gradient, start, line.A, false); 
                //var (p2, sum2) = lineSegGradient(mat, dir, gradient, start, line.B, false);
                var (p1, sum1) = ZIMLineSegGradient(mat, dir, gradient, start, line.A, range, false);
                var (p2, sum2) = ZIMLineSegGradient(mat, dir, gradient, start, line.B, range, false);
                return (new Line(p1, p2), sum1 + sum2);
            }
            else
            {
                var startR = new Vector(start.y, start.x);
                //var (p1, sum1) = lineSegGradient(mat, dir, gradient, startR, new Vector(line.A.y, line.A.x), true);
                //var (p2, sum2) = lineSegGradient(mat, dir, gradient, startR, new Vector(line.B.y, line.B.x), true);
                var (p1, sum1) = ZIMLineSegGradient(mat, dir, gradient, startR, new Vector(line.A.y, line.A.x), range, true);
                var (p2, sum2) = ZIMLineSegGradient(mat, dir, gradient, startR, new Vector(line.B.y, line.B.x), range, true);
                return (new Line(new Vector(p1.y, p1.x), new Vector(p2.y, p2.x)), sum1 + sum2);
            }
        }/**/


        ///////////////////线  线梯度和 线梯度方向0-180/////////返回的线以梯度和最高值到最低值排列
        static public List<(Line, float, float)> IdentifyLineLSD(this Matrix mat, float minLength = 100)
        {
            var (edge, edgeDir) = mat.IdentifyEdge();
            return edge.IdentifyLineLSD(edgeDir, minLength);
        }

        ///////////////////线  线梯度和 线梯度方向0-180/////////返回的线以梯度和最高值到最低值排列
        static public List<(Line, float, float)> IdentifyLineLSD(this Matrix gradientValueMat, Matrix gradientDirMat, float minLength = 100,
            int maxReturnCount = int.MaxValue)
        {
            var matBuffer = gradientValueMat;
            //var (mat, gradientMat) = tex.IdentifyEdge();

            //var lineMinLength = 50;
            var avaAngle = 60;
            var halfAvaAngle = avaAngle / 2;

            var returnLines = new List<(Line, float, float)> ();
            // while (mat.Element[maxIndex] != default)

            int count = 0;
            /*
            var minIndex = matBuffer.Element.MinIndex(matBuffer.Width * matBuffer.Height / 2);
            foreach (var i in minIndex)
                matBuffer.Element[i] = 0;/**/

            var maxIndex = matBuffer.Element.MaxIndex();
            var maxPos = matBuffer.Position(maxIndex);
            var maxVector = new Vector(maxPos.Item1, maxPos.Item2);
            while (matBuffer.Element[maxIndex] != 0 && count <= maxReturnCount)
            {
                count++;

                var gradient = gradientDirMat.Element[maxIndex];//梯度
                var lines = new List<(Line, float, float, float)>(avaAngle);
                Parallel.For((int)Math.Floor(gradient - halfAvaAngle), (int)Math.Ceiling(gradient + halfAvaAngle) + 1, gradientI =>//需要检测的梯度
                {
                    //var gradientF = (float)gradientI / scale;
                    if (gradientI < 0)
                        gradientI += 360;
                    else if (gradientI > 360)
                        gradientI -= 360;
                    var dir = (gradientI + 90) % 180;//需要检测的线的角度

                    //var (line, sum) = lineSegGradient(matBuffer, gradientDirMat, gradientF, maxVector);
                    var (line, sum) = lineSegGradient(gradientValueMat, gradientDirMat, gradientI, maxVector);
                    if (line.A != line.B)
                        lock (matBuffer)
                        {
                            lines.Add((line, line.Length, sum, gradientI));
                            //drawLineMap.DrawLine(line, sum);
                        }
                });
                var (line , lineLength, sum, gradientI) = lines.Max((a, b) => a.Item3.CompareTo(b.Item3));
                if(lineLength != 0)
                {
                    matBuffer.DrawLine(line, (x, y) => 0, Vector.One * minLength);
                }
                if (lineLength > minLength)
                    returnLines.Add((line, sum, gradientI));
                else
                    matBuffer.Element[maxIndex] = 0;
                maxIndex = matBuffer.Element.MaxIndex();
                maxPos = matBuffer.Position(maxIndex);
                maxVector = new Vector(maxPos.Item1, maxPos.Item2);
            }/**/
            returnLines.Sort((a, b) => b.Item2.CompareTo(a.Item2));
            return returnLines;
            /*
            var drawLineMap = new MatrixF2D(matBuffer.Width, matBuffer.Height);
            var returnMaxLines = returnLines.Sub(0,10);
            foreach (var (line, sum, gradient) in returnMaxLines)
            {
                drawLineMap.DrawLine(line, (x, y) => 1, new Vector(0, 10));
                //drawLineMap.DrawLine(line, (x, y) => sum / line.Length, new Vector(0, 10));
                Debug.Log(line);
            }
            Debug.Log(returnLines.Count);


            return drawLineMap;/**/
        }


    }
}
