using MahApps.Metro.Controls;
using MahApps.Metro.Controls.Dialogs;
using Newtonsoft.Json;
using OfficeOpenXml;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using WSDDBBrowser.Core;
using WSDDBBrowser.Core.Model;

namespace WSDDBBrowser.Core
{
    public class DBViewerModel : INotifyPropertyChanged
    {

        public static DBViewerModel Instance { get; } = new DBViewerModel();

        public event PropertyChangedEventHandler PropertyChanged;

        #region 需要保存的参数
        /// <summary>
        /// 混合数

        /// </summary>
        public int Mix { get; set; } = 6;

        /// <summary>
        /// 直方图统计步进
        /// </summary>
        public double Step { get; set; } = 1;

        /// <summary>
        /// Y轴显示比例
        /// </summary>
        public double YRangePercent { get; set; } = 2;

        /// <summary>
        /// 分切宽度，单位mm
        /// </summary> 
        public int CuttingWidth { get; set; } = 100;

        /// <summary>
        /// 分切表
        /// </summary>
        public List<Bolt> DefaultCuttingMap { get; set; } = new List<Bolt>();

        /// <summary>
        /// 双数
        /// </summary>
        public bool IsCuttingCntEven { get; set; } = false;

        /// <summary>
        /// 服务器 用户名
        /// </summary>
        public string ServerUser { get; set; } = "scpw";

        /// <summary>
        /// 服务器 IP
        /// </summary>
        public string ServerIP { get; set; } = @"192.168.0.186\sql2010";

        /// <summary>
        /// 服务器 密码
        /// </summary>
        public string ServerPw { get; set; } = "123456";

        public int MachineNo { get; set; }
        public int StepNo { get; set; }
        #endregion

        /// <summary>
        /// 数据准备好了
        /// </summary>
        public bool IsDBReady { get; set; }

        /// <summary>
        /// 当前被选择的process
        /// </summary>
        public Process CurrProcess { get; set; }
        /// <summary>
        /// 温度数据包
        /// </summary>
        public SBProcessPack SBProcessPack = null;



        #region 测厚
        /// <summary>
        /// 厚度数据包
        /// </summary>
        public List<TGProcessPack> TGProcessPacks = new List<TGProcessPack>();

        #region 显示用
        public g_tgProcess tgProcess { get; private set; }
        #endregion
        public List<ScanDataItem> ScanDataItems { get; } = new List<ScanDataItem>();


        /// <summary>
        /// 膜宽 从 process 获取的平均膜宽
        /// </summary>
        public int FilmWidth { get; private set; }

        /// <summary>
        /// 分区数
        /// </summary>
        public int NBolts { get; private set; }


        /// <summary>
        /// 分条表
        /// </summary>
        public List<Bolt> CuttingMap { get; set; } = new List<Bolt>();
        #endregion

        #region 温度
        [PropertyChanged.DoNotCheckEquality]
        public List<RecordCollection> RecordCollections { get; private set; }

        #endregion
        public DBViewerModel()
        {
            if (!Load())
                Save();

            this.PropertyChanged += DBViewerModel_PropertyChanged;
        }

        private void DBViewerModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == "Mix")
            {
                Update4MixChanged();
            }
        }
        public bool GetProcessPacks()
        {
            bool tg_ret = false;
            if (CurrProcess.HasTGData)
            {
                tg_ret = GetTGProcessPacks();
            }
            bool sd_ret = false;
            if (CurrProcess.HasSBData)
            {
                sd_ret = GetSDProcessPacks();
            }
            if (tg_ret || sd_ret)
            {
                IsDBReady = true;
            }
            else
            {
                IsDBReady = false;
            }
            return IsDBReady;
        }


        #region 测厚
        public bool GetTGProcessPacks()
        {

            TGProcessPacks.Clear();
            ScanDataItems.Clear();


            foreach (int id in CurrProcess.TGID)
            {
                var profilePack = DBHelper.Instance.GetTGProcessPack(id);
                TGProcessPacks.Add(profilePack);
            }
            TGProcessPacks.RemoveAll(_p => _p.ScanDatas.Count() == 0);

            if (TGProcessPacks.Count() == 0)
            {
                CurrProcess.HasTGData = false;
                return false;
            }

            NBolts = TGProcessPacks.First().ScanDatas.First().nValues.Count();

            var processPack = TGProcessPacks.Last();
            tgProcess = processPack.Process;

            //创建ScanDataItems
            foreach (var tech in processPack.Technologies)
            {
                if (tech.bIsReal == false)
                    continue;
                var scanDataItem = new ScanDataItem();
                scanDataItem.DeviceID = tech.cDeviceId;
                scanDataItem.DeviceName = tech.cDeviceName;

                scanDataItem.Target = (double)tech.nTarget;
                scanDataItem.Tolerance = (double)tech.nTolerance;

                scanDataItem.Ctrlline = (double)tech.nCtrlLine;
                if (scanDataItem.Ctrlline < scanDataItem.Tolerance / 2)
                    scanDataItem.Ctrlline = scanDataItem.Tolerance * 2 / 3;

                scanDataItem.YRangePercent = YRangePercent;

                ScanDataItems.Add(scanDataItem);
            }

            this.PropertyChanged += (s, e) =>
            {
                if (e.PropertyName == "YRangePercent")
                {
                    foreach (var scanDataItem in ScanDataItems)
                        scanDataItem.YRangePercent = YRangePercent;
                }
            };

            FilmWidth = (int)(processPack.ScanDatas.Average(sd => sd.nFilmEnd - sd.nFilmBegin));

            CuttingMap.Clear();

            //使用默认分切表
            if (DefaultCuttingMap.Count() > 0)
            {
                CuttingMap.AddRange(DefaultCuttingMap);
            }
            else
            {
                //根据 CuttingWidth 创建
                var cuttingMap = CreateCuttingMap(CuttingWidth, IsCuttingCntEven);
                if (cuttingMap != null && cuttingMap.Count > 0)
                    CuttingMap.AddRange(cuttingMap);
            }


            //分解数据到每个scanDataItem中
            foreach (var p in TGProcessPacks)
            {
                foreach (var scanDataItem in ScanDataItems)
                {
                    var dB_ScanDatas = from sd in p.ScanDatas where sd.cDeviceId == scanDataItem.DeviceID select sd;
                    scanDataItem.Values.AddRange(dB_ScanDatas);
                }
            }


            Update4MixChanged();
            IsDBReady = true;
            return true;
        }

        public List<Bolt> CreateCuttingMap(int cuttingWidth, bool isEven)
        {

            if (cuttingWidth < 20)
            {
                return null;
            }
            if (FilmWidth < 50)
            {
                return null;
            }
            var cuttings = new List<Bolt>();


            int mid = FilmWidth / 2;
            if (!isEven)
                mid -= cuttingWidth / 2;

            int begin = mid;
            int end = mid + cuttingWidth;

            while (true)
            {
                cuttings.Add(new Bolt { Begin = begin, End = end });
                begin = end;
                end = begin + cuttingWidth;
                if (end > FilmWidth)
                {
                    break;
                }
            }

            end = mid;
            begin = end - cuttingWidth;
            while (true)
            {
                cuttings.Insert(0, new Bolt { Begin = begin, End = end });
                end = begin;
                begin = end - cuttingWidth;

                if (begin < 0)
                {
                    break;
                }
            }
            return cuttings;
        }

        void UpdateStatistics()
        {
            foreach (var scanDataItem in ScanDataItems)
            {
                scanDataItem.UpdateStatistics();
            }
        }

        public void SetCuttingMap(IEnumerable<Bolt> bolts)
        {
            bool isChanged = !CuttingMap.SequenceEqual(bolts);

            if (isChanged)
            {
                CuttingMap.Clear();
                CuttingMap.AddRange(bolts);
                DefaultCuttingMap.Clear();
                DefaultCuttingMap.AddRange(bolts);

                //更新MixCuttingValues
                Update4CuttingChanged();

                NotifyPropertyChanged("CuttingMap");
            }
        }
        public void Update4MixChanged()
        {
            int nbolts = NBolts;
            foreach (var scanDataItem in ScanDataItems)
            {
                scanDataItem.MixValues.Clear();

                int[] values_cnt = new int[nbolts];

                for (int j = 0; j < scanDataItem.Values.Count(); j++)
                {
                    double[] values = new double[nbolts];
                    Array.Clear(values_cnt, 0, nbolts);
                    for (int k = 0; k < Mix; k++)
                    {
                        int idx = j - k;
                        if (idx < 0 || idx >= scanDataItem.Values.Count())
                            break;//没有数据

                        var scanData = scanDataItem.Values[idx];

                        for (int n = 0; n < nbolts; n++)
                        {
                            if (!double.IsNaN(scanData.nValues[n]))
                            {
                                values[n] += scanData.nValues[n];
                                values_cnt[n]++;
                            }
                        }
                    }
                    for (int n = 0; n < nbolts; n++)
                    {
                        if (values_cnt[n] > 0)
                        {
                            values[n] /= values_cnt[n];
                        }
                        else
                        {
                            values[n] = double.NaN;
                        }
                    }
                    scanDataItem.MixValues.Add(values);
                }

                scanDataItem.NotifyPropertyChanged("MixValues");
            }

            UpdateMixValuesAverage();
            Update4CuttingChanged();

        }
        void UpdateMixValuesAverage()
        {
            foreach (var scanDataItem in ScanDataItems)
            {
                int nbolts = NBolts;
                double[] values_avg = new double[nbolts];
                for (int i = 0; i < nbolts; i++)
                {
                    double sum = 0;
                    int cnt = 0;
                    for (int j = 0; j < scanDataItem.MixValues.Count(); j++)
                    {
                        if (!double.IsNaN(scanDataItem.MixValues[j][i]))
                        {
                            sum += scanDataItem.MixValues[j][i];
                            cnt++;
                        }
                    }
                    if (cnt > 0)
                        values_avg[i] = sum / cnt;
                    else
                        values_avg[i] = double.NaN;
                }

                scanDataItem.MixValuesAverage = values_avg;
            }
        }
        void Update4CuttingChanged()
        {
            foreach (var scanDataItem in ScanDataItems)
            {
                scanDataItem.MixCuttingValues.Clear();
                for (int i = 0; i < scanDataItem.MixValues.Count(); i++)
                {
                    var scanData = scanDataItem.Values[i];

                    int filmbegin = (int)scanData.nFilmBegin;
                    BoltValue[] boltValues = new BoltValue[CuttingMap.Count()];
                    for (int k = 0; k < CuttingMap.Count(); k++)
                    {
                        Bolt bolt = CuttingMap[k];
                        List<double> a = new List<double>();
                        double b_cm = (bolt.Begin + filmbegin) / 10;
                        double e_cm = (bolt.End + filmbegin) / 10;
                        int b = (int)Math.Floor(b_cm);
                        int e = (int)Math.Ceiling(e_cm);
                        for (int n = b; n <= e; n++)
                        {
                            if (n >= 0 && n < scanDataItem.MixValues[i].Count() && !double.IsNaN(scanDataItem.MixValues[i][n]))
                            {
                                a.Add(scanDataItem.MixValues[i][n]);
                            }
                        }
                        boltValues[k] = (new BoltValue(b, e, a.Count() > 0 ? a.Average() : double.NaN));
                    }

                    scanDataItem.MixCuttingValues.Add(boltValues);
                }

            }

            UpdateMixCuttingValuesAverage();

            UpdateMixAverages();

            UpdateStatistics();


            foreach (var scanDataItem in ScanDataItems)
            {
                scanDataItem.NotifyPropertyChanged("MixCuttingValues");
            }
        }
        void UpdateMixAverages()
        {
            foreach (var item in ScanDataItems)
            {
                item.MixCuttingAverages.Clear();
                for (int i = 0; i < item.MixCuttingValues.Count(); i++)
                {
                    var list = from b in item.MixCuttingValues[i] where !double.IsNaN(b.Value) select b.Value;
                    if (list.Count() == 0)
                        item.MixCuttingAverages.Add(double.NaN);
                    else
                        item.MixCuttingAverages.Add(list.Average());
                }
            }
        }
        void UpdateMixCuttingValuesAverage()
        {
            foreach (var scanDataItem in ScanDataItems)
            {
                double[] values_avg = new double[CuttingMap.Count()];
                for (int i = 0; i < CuttingMap.Count(); i++)
                {
                    double sum = 0;
                    int cnt = 0;
                    for (int j = 0; j < scanDataItem.MixCuttingValues.Count(); j++)
                    {
                        double v = scanDataItem.MixCuttingValues[j][i].Value;
                        if (!double.IsNaN(v))
                        {
                            sum += v;
                            cnt++;
                        }
                    }
                    if (cnt > 0)
                        values_avg[i] = sum / cnt;
                    else
                        values_avg[i] = double.NaN;
                }
                scanDataItem.MixCuttingValuesAverage = values_avg;
            }
        }

        #endregion

        #region 温度
        bool GetSDProcessPacks()
        {
            SBProcessPack = DBHelper.Instance.GetSBProcessPack(CurrProcess.SBID);
            if (SBProcessPack.Records.Count() == 0)
            {
                CurrProcess.HasSBData = false;
                SBProcessPack = null;
                return false;
            }

            List<RecordCollection> record_collections = new List<RecordCollection>();
            var ColorBrushs = MahApps.Metro.ThemeManager.Themes.GroupBy(x => x.ColorScheme)
                                            .Select(x => x.First())
                                            .Select(a => a.ShowcaseBrush)
                                            .ToList();
            var techs = SBProcessPack.Technologies;
            int idx = 0;
            int step = (ColorBrushs.Count() > techs.Count()) ? (int)Math.Floor((double)(ColorBrushs.Count()) / techs.Count()) : 1;
            foreach (var n in techs)
            {
                RecordCollection recordCollection = new RecordCollection();
                recordCollection.DeviceID = n.cDeviceId;
                recordCollection.DeviceName = n.cDeviceName;
                recordCollection.Target = (double)n.nTarget;
                recordCollection.Tolerance = (double)n.nTolerance;
                recordCollection.ColorBrush = ColorBrushs[idx];
                idx += step;
                if (idx >= ColorBrushs.Count())
                    idx -= ColorBrushs.Count();

                recordCollection.Values.AddRange(
                    from record in SBProcessPack.Records
                    where record.cDeviceId == recordCollection.DeviceID
                    select
                    new Record()
                    {
                        MValue = (double)record.nMValue,
                        ProcessLen = (double)record.nProcessLen,
                        Time = (DateTime)record.tTime
                    });

                if (recordCollection.Values.Count() > 0)
                {
                    recordCollection.Max = recordCollection.Values.Max(r => r.MValue);
                    recordCollection.Min = recordCollection.Values.Min(r => r.MValue);
                    recordCollection.Average = recordCollection.Values.Average(r => r.MValue);
                    recordCollection.Sigma3 = recordCollection.Values.Sigma(r => r.MValue) * 3;

                    record_collections.Add(recordCollection);
                }
            }
            if (record_collections.Count() > 0)
                record_collections[0].IsVisibile = true;
            RecordCollections = record_collections;

            return true;
        }
        #endregion

        void NotifyPropertyChanged(string propertyName)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }


        string file_path = "dbsetting.json";
        public void Save()
        {
            try
            {
                var param = AutoMapper.Mapper.Map<DBViewerParamJsonDB>(this);

                File.WriteAllText(file_path, JsonConvert.SerializeObject(param, Formatting.Indented));
            }
            catch
            {
                //异常，没有json 编码失败

            }
        }
        bool Load()
        {
            try
            {
                if (File.Exists(file_path))
                {
                    string json = File.ReadAllText(file_path);
                    var param = JsonConvert.DeserializeObject<DBViewerParamJsonDB>(json);
                    AutoMapper.Mapper.Map<DBViewerParamJsonDB, DBViewerModel>(param, this);
                    return true;
                }
            }
            catch
            {
                //异常，没有json 解码失败

            }
            return false;
        }

    }















    public class DBViewerParamJsonDB
        {
            /// <summary>
            /// 混合数

            /// </summary>
            public int Mix = 6;

            /// <summary>
            /// 直方图统计步进
            /// </summary>
            public double Step = 1;

            /// <summary>
            /// Y轴显示比例
            /// </summary>
            public double YRangePercent = 2;

            /// <summary>
            /// 分切宽度，单位mm
            /// </summary> 
            public int CuttingWidth = 100;

            /// <summary>
            /// 分切表
            /// </summary>
            public List<Bolt> DefaultCuttingMap = new List<Bolt>();

            /// <summary>
            /// 双数
            /// </summary>
            public bool IsCuttingCntEven = false;

            /// <summary>
            /// 服务器 用户名
            /// </summary>
            public string ServerUser = "scpw";

            /// <summary>
            /// 服务器 IP
            /// </summary>
            public string ServerIP = "192.168.0.186";

            /// <summary>
            /// 服务器 密码
            /// </summary>
            public string ServerPw = "123456";

            public int MachineNo = 1;
            public int StepNo = 1;
    }

    public class ParamProfile : AutoMapper.Profile
        {
            public ParamProfile()
            {
                base.CreateMap<DBViewerModel, DBViewerParamJsonDB>().ReverseMap();
            }
        }
}
