using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;

namespace o0.Project
{
    public partial class InfraredIdentification
    {

        Geometry.Vector<float> LastMin = Geometry.Vector<float>.Zero;
        Geometry.Vector<float> LastMax = Geometry.Vector<float>.One;



        public Texture2D AutoLight2(Texture2D texture, bool Smooth = false) => AutoLight2(texture.GetPixels(), Smooth);
        public Texture2D AutoLight2(WebCamTexture texture, bool Smooth = false) => AutoLight2(texture.GetPixels(), Smooth);
        public Texture2D AutoLight2(UnityEngine.Color[] pixel, bool Smooth = false)
        {
            List<float> rList = new List<float>();
            List<float> gList = new List<float>();
            List<float> bList = new List<float>();
            var sampleCount = 1000;
            float step = pixel.Length / sampleCount;
            for (float i = 0; i < pixel.Length; i += step)
            {
                var iP = pixel[Mathf.FloorToInt(i)];
                rList.Add(iP.r);
                gList.Add(iP.g);
                bList.Add(iP.b);
            }
            rList.Sort((a, b) => a.CompareTo(b));
            gList.Sort((a, b) => a.CompareTo(b));
            bList.Sort((a, b) => a.CompareTo(b));
            var minC = new Geometry.Vector<float>(rList[Mathf.FloorToInt(rList.Count * 0.01f)], gList[Mathf.FloorToInt(rList.Count * 0.01f)], bList[Mathf.FloorToInt(rList.Count * 0.01f)]);
            var maxC = new Geometry.Vector<float>(rList[Mathf.FloorToInt(rList.Count * 0.99f)], gList[Mathf.FloorToInt(rList.Count * 0.99f)], bList[Mathf.FloorToInt(rList.Count * 0.99f)]);

            if (Smooth)
            {
                LastMin = LastMin * 0.9 + minC * 0.1;
                LastMax = LastMax * 0.9 + maxC * 0.1;
                minC = LastMin;
                maxC = LastMax;
            }/**/

            var mapToMin = 0.05f;
            var mapToMax = 0.95f;
            Func<float, float, float, float> map = (value, min, max) =>
            {
                if (value <= min)
                    return mapToMin;
                if (value >= max)
                    return mapToMax;
                return (value - min) / (max - min) * (mapToMax - mapToMin) + mapToMin;
            };

            foreach (var i in pixel.Index())
            {
                var iP = pixel[i];
                pixel[i] = new UnityEngine.Color(map(iP.r, minC[0], maxC[0]),
                    map(iP.g, minC[1], maxC[1]),
                    map(iP.b, minC[2], maxC[2]));
            }


            var newTex = new Texture2D(Size.x, Size.y);
            newTex.SetPixels(pixel);
            newTex.Apply();
            return newTex;
        }

    }
    public static partial class Extension
    {
        public static Texture2D AutoLight(this Texture2D texture, int ignoreExtremum = 10, int sampleCount = -1) => AutoLight(texture.GetPixels(), new Geometry2D.Vector<int>(texture.width, texture.height), ignoreExtremum, sampleCount);
        public static Texture2D AutoLight(this WebCamTexture texture, int ignoreExtremum = 10, int sampleCount = -1) => AutoLight(texture.GetPixels(), new Geometry2D.Vector<int>(texture.width, texture.height), ignoreExtremum, sampleCount);
        /////////////////////////////////////////////////////////忽略极值///////////////采样数量
        public static Texture2D AutoLight(this UnityEngine.Color[] pixel, Geometry2D.Vector<int> size, int ignoreExtremum = 10, int sampleCount = -1)
        {
            SortedList<float> rMax = new SortedList<float>();
            SortedList<float> rMin = new SortedList<float>();
            SortedList<float> gMax = new SortedList<float>();
            SortedList<float> gMin = new SortedList<float>();
            SortedList<float> bMax = new SortedList<float>();
            SortedList<float> bMin = new SortedList<float>();
            foreach (var i in ignoreExtremum.Range())
            {
                rMax.Add(0);
                rMin.Add(1);
                gMax.Add(0);
                gMin.Add(1);
                bMax.Add(0);
                bMin.Add(1);
            }
            float step = sampleCount < 1 ? 1 : pixel.Length / sampleCount;
            for (float i = 0; i < pixel.Length; i += step)
            {
                var iP = pixel[Mathf.FloorToInt(i)];
                if (iP.r > rMax.First())
                {
                    rMax.Add(iP.r);
                    rMax.RemoveAt(0);
                }
                if (iP.r < rMin.Last())
                {
                    rMin.Add(iP.r);
                    rMin.RemoveAt(rMin.Count - 1);
                }//
                if (iP.g > gMax.First())
                {
                    gMax.Add(iP.g);
                    gMax.RemoveAt(0);
                }
                if (iP.g < gMin.Last())
                {
                    gMin.Add(iP.g);
                    gMin.RemoveAt(gMin.Count - 1);
                }//
                if (iP.b > bMax.First())
                {
                    bMax.Add(iP.b);
                    bMax.RemoveAt(0);
                }
                if (iP.b < bMin.Last())
                {
                    bMin.Add(iP.b);
                    bMin.RemoveAt(bMin.Count - 1);
                }
            }
            var minC = new Geometry.Vector<float>(rMin.Last(), gMin.Last(), bMin.Last());
            var maxC = new Geometry.Vector<float>(rMax.First(), gMax.First(), bMax.First());

            /*
            var mapToMin = 0;
            var mapToMax = 1;
            Func<float, float, float, float> map = (value, min, max) =>
            {
                if (value <= min)
                    return mapToMin;
                if (value >= max)
                    return mapToMax;
                return (value - min) / (max - min) * (mapToMax - mapToMin) + mapToMin;
            };/**/
            Func<float, float, float, float> map = (value, min, max) =>
            {
                if (value <= min)
                    return 0;
                if (value >= max)
                    return 1;
                return (value - min) / (max - min);
            };

            foreach (var i in pixel.Index())
            {
                var iP = pixel[i];
                pixel[i] = new UnityEngine.Color(map(iP.r, minC[0], maxC[0]),
                    map(iP.g, minC[1], maxC[1]),
                    map(iP.b, minC[2], maxC[2]));
            }


            var newTex = new Texture2D(size.x, size.y);
            newTex.SetPixels(pixel);
            newTex.Apply();
            return newTex;
        }



        /////////////////////////////////////////////////////////忽略极值///////////////采样数量
        public static Matrix AutoLight(this Matrix mat, int ignoreExtremum = 10)
        {
            var pixel = mat.Element;
            var min = pixel.Min(ignoreExtremum);
            var max = pixel.Max(ignoreExtremum);

            var minC = min.Last();
            var maxC = max.First();

            Func<float, float, float, float> map = (value, min, max) =>
            {
                if (value <= min)
                    return 0;
                if (value >= max)
                    return 1;
                return (value - min) / (max - min);
            };

            var newMat = new Matrix(mat.Size, Tiling: true);
            var newMatElement = newMat.Element;
            Parallel.For(0, pixel.Length, i => {
                newMatElement[i] = map(pixel[i], minC, maxC);
            });
            return newMat;
        }
    }
}
