using FLY.OBJComponents.Server.Model;
using Misc;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Lc_ScanData = FLY.Thick.FilmCasting.Server.Model.Lc_ScanData;
using Db_Profile = FLY.Thick.FilmCasting.Server.Model.Db_Profile;
namespace FLY.Thick.FilmCasting.UI.DbViewer.Db
{
    #region 链接表的关系
    /// <summary>
    /// 包装 DB_Profile， 组织 DB_Profile， DB_ScanData....
    /// </summary>
    public class DbProfilePack : INotifyPropertyChanged
    {
        #region DbHelper 读出的数据库原始数据
        //产品 
        public Db_Profile Profile;

        /// <summary>
        /// 原始数据
        /// </summary>
        public List<Lc_ScanData> ScanDatas;


        public List<Db_Error> Errors;

        #endregion




        /// <summary>
        /// 混合后的数据
        /// </summary>
        public List<Lc_ScanData2> ScanDatasMix;
        public List<DistCell> Histogram;
        public int Histogram_TotalCount;
        public double Histogram_Sigma;
        public double Histogram_Average;

        #region 能重新调整的参数
        public double USL=> Target + Tolerance;

        public double LSL => Target - Tolerance;

        public double YMax=> Target + Tolerance * YRangePercent;

        public double YMin=>Target - Tolerance * YRangePercent;

        //从Profile 复制出来的数据
        public double Target { get; set; }
        //从Profile 复制出来的数据
        public double TolerancePercent { get; set; }


        public double YRangePercent { get; set; } = 3;
        public double Tolerance => Target * TolerancePercent;
        /// <summary>
        /// 数据需要先混合
        /// </summary>
        public int Mix { get; set; } = 4;
        /// <summary>
        /// 步进 单位um
        /// </summary>
        public int Step { get; set; } = 1;
        #endregion
        #region 统计结果，用于输出
        public int TotalCount { get; private set; }
        public double Average { get; private set; }
        public double Sigma2 { get; private set; }
        public double CPK { get; private set; }
        public double InTolerancePercentRange { get; private set; }
        #endregion
        
        /// <summary>
        /// 分区数
        /// </summary>
        public int NBolts { get; private set; }
        public int DataBegin { get; private set; }
        public int DataEnd { get; private set; }


        public bool Init()
        {
            //把异常的数据全部删除
            for (int i = 0; i < ScanDatas.Count(); i++) 
            {
                if (ScanDatas[i].Thicks == null || ScanDatas[i].Thicks.Count() == 0)
                {
                    //异常
                    //删除
                    ScanDatas.RemoveAt(i);
                    i--;
                    continue;
                }
            }

            if (ScanDatas.Count() == 0)
            {
                return false;
            }


            var scanData = ScanDatas.Last();
            NBolts = scanData.Thicks.Count();
            DataBegin = scanData.DataBegin;
            DataEnd = scanData.DataEnd;

            Target = Profile.Target;
            TolerancePercent = Profile.TolerancePercent;
            if (Mix < 1)
                Mix = 1;
            
            UpdateScanDatasMix();
            UpdateHistogram();
            UpdateStatistics();
            return true;
        }

        /// <summary>
        /// 混合数据
        /// </summary>
        void UpdateScanDatasMix() 
        {
            ScanDatasMix = new List<Lc_ScanData2>();
            for (int i = 0; i < ScanDatas.Count(); i++) 
            {
                Lc_ScanData scanData = ScanDatas[i];
                Lc_ScanData2 scanData2 = new Lc_ScanData2();

                Misc.PropertiesManager.CopyTo(scanData, scanData2);
                
                scanData2.DataBegin = DataBegin;
                scanData2.DataEnd = DataEnd;
                scanData2.Thicks = CalMixData(ScanDatas, NBolts, i, Mix);

                scanData2.Avg = CalScanDataAverage(scanData2, out double sigma2);

                scanData2.Sigma2 = sigma2;

                ScanDatasMix.Add(scanData2);
            }
        }
        double CalScanDataAverage(Lc_ScanData scanData, out double sigma2) 
        {
            sigma2 = double.NaN;

            var datas = GetThicksInDataRange(scanData);
            if (datas == null)
                return double.NaN;

            sigma2 = datas.Sigma() * 2;

            return datas.AverageNoNull();
        }
        IEnumerable<double> GetThicksInDataRange(Lc_ScanData scanData) 
        {

            int dataBegin = scanData.DataBegin;
            int dataEnd = scanData.DataEnd;

            if (dataBegin < 0)
                dataBegin = 0;
            else if (dataBegin >= scanData.Thicks.Count())
                return null;

            if (dataEnd < dataBegin)
                return null;
            else if (dataEnd >= scanData.Thicks.Count())
                dataEnd = scanData.Thicks.Count() - 1;
            int len = dataEnd - dataBegin + 1;

            return scanData.Thicks.Skip(dataBegin).Take(len);
        }

        private double[] CalMixData(List<Lc_ScanData> scanDatas, int nbolts, int index, int mix) 
        {
            double[] sum = new double[nbolts];
            int[] cnt = new int[nbolts];

            for (int i = 0; i < mix; i++) 
            {
                int idx = index - i;
                if (idx < 0)
                    break;
                if (idx >= scanDatas.Count())
                    continue;

                for (int j = 0; j < nbolts; j++) 
                {
                    if (j < scanDatas[idx].Thicks.Count()) 
                    {
                        var thk = scanDatas[idx].Thicks[j];
                        if (!double.IsNaN(thk))
                        { 
                            sum[j] += thk;
                            cnt[j]++;
                        }
                    }
                    else
                    {
                        break;
                    }
                }
            }
            for (int j = 0; j < nbolts; j++)
            {
                if (cnt[j] > 0)
                {
                    sum[j] = sum[j] / cnt[j];
                }
                else {
                    sum[j] = double.NaN;
                }
            }
            return sum;
        }
        void UpdateStatistics()
        {
            List<double> values = new List<double>();
            for (int i = 0; i < ScanDatas.Count(); i++)
            {
                var scanData = ScanDatas[i];
                //不统计NaN
                values.AddRange(scanData.Thicks.Where(t => !double.IsNaN(t)));

            }
            if (values.Count() == 0)
            {
                //没有数据
                Average = double.NaN;
                Sigma2 = double.NaN;
                CPK = double.NaN;
                TotalCount = 0;
                InTolerancePercentRange = double.NaN;
                return;
            }

            TotalCount = values.Count();

            Average = values.Average();

            double sigma = values.Sigma(d => d);
            Sigma2 = sigma * 2;
            int tolerance_cnt = values.Count(d => d <= USL && d >= LSL);

            InTolerancePercentRange = 1.0 * tolerance_cnt / TotalCount;

            CPK = values.CPK(USL, LSL);

            //NormValues.Clear();
            //var normvalues =
            //    from xy in Values select new XY(xy.X, MathExt.NormDist(xy.X, Average, sigma));

            //double sum = normvalues.Sum(xy => xy.Y);
            //NormValues.AddRange(normvalues.Select(xy => new XY(xy.X, total * xy.Y / sum)));
        }
        public event PropertyChangedEventHandler PropertyChanged;


        void UpdateHistogram() 
        {
            Histogram = GetHistogram(ScanDatasMix,Step);
        }

        /// <summary>
        /// 统计 的 Y是 数量, X是厚度
        /// </summary>
        /// <param name="step"></param>
        /// <returns></returns>
        List<DistCell> GetHistogram(List<Lc_ScanData2> scanDatas, double step)
        {
            List<DistCell> list = new List<DistCell>();
            List<double> all_datas = new List<double>();
            foreach (var scanData in scanDatas)
            {
                var datas = GetThicksInDataRange(scanData);
                if (datas == null)
                    continue;
                foreach (var thk in datas)
                {
                    if (!double.IsNaN(thk))
                        all_datas.Add(thk);
                }
            }
            if (all_datas.Count() == 0)
            {
                Histogram_TotalCount = 0;
                Histogram_Sigma = double.NaN;
                Histogram_Average = double.NaN;
                return list;
            }


            foreach (var thk in all_datas)
            {
                double x = ((int)(thk / step)) * step;
                DistCell xy = list.Find(_xy => _xy.Thick == x);
                if (xy != null)
                {
                    xy.Count++;
                }
                else
                {
                    xy = new DistCell { Thick = x, Count = 1 };
                    list.Add(xy);
                }
            }
            
            //X从小到大排列 
            list.Sort();



            Histogram_TotalCount = all_datas.Count();
            Histogram_Sigma = all_datas.Sigma();
            Histogram_Average = all_datas.Average();

            foreach (var xy in list) {
                xy.Freq100 = 1.0 * xy.Count / Histogram_TotalCount * 100;
                var k = 1 / (Math.Sqrt(2 * Math.PI) * Histogram_Sigma);
                var d = -(Math.Pow(xy.Thick - Histogram_Average, 2) / (2 * Math.Pow(Histogram_Sigma, 2)));
                xy.Norm100 = k * Math.Exp(d) * 100;
            }

            return list;
        }

    }
    #endregion

    public class Lc_ScanData2 : Lc_ScanData
    {
        public double Sigma2 { get; set; }
    }

    /// <summary>
    /// 分布
    /// </summary>
    public class DistCell : IComparable
    {
        public double Thick { get; set; }
        public int Count { get; set; }
        /// <summary>
        /// 频数(%)
        /// </summary>
        public double Freq100 { get; set; }

        /// <summary>
        /// 对应正态分布频数
        /// </summary>
        public double Norm100 { get; set; }

        public int CompareTo(object obj)
        {
            DistCell xy = obj as DistCell;
            return Thick.CompareTo(xy.Thick);
        }
    }
}
