using FLY.Thick.FilmCasting.IService;
using FObjBase;
using FObjBase.Reflect;
using Misc;
using SQLite;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace FLY.Thick.FilmCasting.Server.Model
{
    /// <summary>
    /// 提供接口，让别人获取 整理好的 当前生产，或之前生产的数据。
    /// 从LocalDb 获取数据， 或从 直接从数据获取数据
    /// 
    /// 与HistoryDb，成对。 
    /// HistoryDb 是写
    /// BulkDb 是读
    /// </summary>
    public class BulkDb : IBulkDbService
    {
        /// <summary>
        /// 本地数据库
        /// </summary>
        LocalDb localDb;
        DbModel dbModel;
        IShareDbService shareDb;

        /// <summary>
        /// 最后一条扫描数据Id
        /// </summary>
        public long LastScanDataId { get; private set; } = -1;
        /// <summary>
        /// 最后一条产品Id
        /// </summary>
        public long LastProfileId { get; private set; } = -1;
        /// <summary>
        /// 数据库最后一次被修改时间
        /// </summary>
        public DateTime LastTimestamp { get; private set; }


        /// <summary>
        /// 开始产生数据时间
        /// </summary>
        public DateTime StartTime { get; private set; }

        /// <summary>
        /// 最后产生数据时间
        /// </summary>
        public DateTime EndTime { get; private set; }

        /// <summary>
        /// 当前生产，已经结束，已经下辊了
        /// </summary>
        public bool IsFinished { get; private set; }


        /// <summary>
        /// 混合数据，时间范围
        /// 大于 TimeSpan.Zero 起作用
        /// </summary>
        public TimeSpan MixDataTimeRange { get; set; } = TimeSpan.FromMinutes(5);

        /// <summary>
        /// 混合数据，均值波动范围
        /// 大于 0 起作用
        /// </summary>
        public double MixDataAvgRange { get; set; } = 5;
        /// <summary>
        /// 
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;

        /// <summary>
        /// 临时数据改变
        /// </summary>
        [Push(typeof(BulkDbTempFrameChangedEventArgs))]
        public event EventHandler TempFrameChanged;

        public Func<BulkDbTempFrameChangedEventArgs> GetTempFrameAction;

        /// <summary>
        /// 
        /// </summary>
        public BulkDb()
        {

        }

        /// <summary>
        /// 写入动态数据
        /// </summary>
        /// <param name="startIndex"></param>
        /// <param name="D"></param>
        public void SetTempFrame(DateTime time, DateTime endTime, int startIndex, double[] D)
        {
            TempFrameChanged?.Invoke(this, new BulkDbTempFrameChangedEventArgs() { Time = time, EndTime = endTime, StartIndex = startIndex, D = D });
        }

        /// <summary>
        /// 获取临时数据
        /// </summary>
        /// <param name="request"></param>
        /// <param name="asyncDelegate"></param>
        /// <param name="asyncContext"></param>
        [Call(typeof(BulkDbTempFrameChangedEventArgs))]
        public void GetTempFrame(
            AsyncCBHandler asyncDelegate, object asyncContext)
        {
            BulkDbTempFrameChangedEventArgs e;
            if (GetTempFrameAction != null)
                e = GetTempFrameAction();
            else
                e = new BulkDbTempFrameChangedEventArgs();
            asyncDelegate?.Invoke(asyncContext, e);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="shareDb"></param>
        /// <param name="localDb"></param>
        /// <param name="dbModel"></param>
        public void Init(IShareDbService shareDb,
             LocalDb localDb, DbModel dbModel)
        {
            this.localDb = localDb;

            this.dbModel = dbModel;
            this.shareDb = shareDb;

            LocalDBLoad();


            Misc.BindingOperations.SetBinding(localDb, nameof(localDb.ProfileStartTime), this, nameof(StartTime));
            Misc.BindingOperations.SetBinding(localDb, nameof(localDb.ProfileEndTime), this, nameof(EndTime));
            Misc.BindingOperations.SetBinding(localDb, nameof(localDb.IsProfileFinished), this, nameof(IsFinished));

            this.shareDb.ProfileChanged += ShareDB_ProfileChanged;
            this.shareDb.ScanDataAdded += ShareDB_ScanDataAdded;

        }

        private void ShareDB_ProfileChanged(object sender, EventArgs _e)
        {
            ProfileChangedEventArgs e = (ProfileChangedEventArgs)_e;

            var profile = e.profile;

            LastProfileId = profile.ID;
            LastTimestamp = DateTime.Now;
        }

        private void ShareDB_ScanDataAdded(object sender, EventArgs _e)
        {
            ScanDataAddedEventArgs e = (ScanDataAddedEventArgs)_e;

            LastScanDataId = e.scandata.ID;
            LastTimestamp = DateTime.Now;
        }

        /// <summary>
        /// 加载数据库中最后1次订单数据到 本地
        /// </summary>
        void LocalDBLoad()
        {
            var reponse = dbModel.sqliteHelper.ExecuteScalar($"SELECT MAX(ID) FROM {dbModel.TbProfile.TableName}");
            if (reponse is DBNull)
                return;

            long maxid = System.Convert.ToInt64(reponse);
            var profile = dbModel.TbProfile.Find($"WHERE ID = {maxid}").First();


            reponse = dbModel.sqliteHelper.ExecuteScalar($"SELECT MAX(ID) FROM {dbModel.TbScanData.TableName}");
            if (!(reponse is DBNull))
            {
                maxid = System.Convert.ToInt64(reponse);
                LastScanDataId = maxid;
            }

            LastProfileId = profile.ID;
        }

        /// <summary>
        /// 获取profile
        /// </summary>
        /// <param name="request"></param>
        /// <param name="asyncDelegate"></param>
        /// <param name="asyncContext"></param>
        [Call(typeof(Pack_GetProfileReponse))]
        public async void GetProfile(
            Pack_GetProfileRequest request,
            AsyncCBHandler asyncDelegate, object asyncContext)
        {
            Pack_GetProfileReponse reponse = new Pack_GetProfileReponse();
            reponse.Request = request;

            await Task.Factory.StartNew(() =>
            {
                List<Db_Profile> db_profiles = dbModel.TbProfile.Find($"WHERE ID = {request.Id}");

                if (db_profiles.Count() == 0)
                    return;

                reponse.profile = db_profiles.First();
            });

            asyncDelegate(asyncContext, reponse);
        }


        /// <summary>
        /// 获取N幅数据,需要异步
        /// </summary>
        /// <param name="request"></param>
        /// <param name="asyncDelegate"></param>
        /// <param name="asyncContext"></param>
        public async void GetFrame(Pack_GetFrameRequest request, AsyncCBHandler asyncDelegate, object asyncContext)
        {
            var reponse = new Pack_GetFrameReponse();
            reponse.Request = request;
            if (request.Mix > 100)//数量限制，避免死机
                request.Mix = 100;
            else if (request.Mix < 1)
                request.Mix = 1;

            await Task.Factory.StartNew(() =>
            {
                List<Db_ScanData> db_scandatas;
                if (request.Id <= 0)
                {
                    db_scandatas = dbModel.TbScanData.Find(
                        $"ORDER BY ID DESC" +
                        $" LIMIT {request.Mix} OFFSET {-request.Id}");
                }
                else
                    db_scandatas = dbModel.TbScanData.Find(
                        $"WHERE ID <= {request.Id}" +
                        $" ORDER BY ID DESC" +
                        $" LIMIT {request.Mix}");

                //数据是 从 后 到 前 排列的
                if (db_scandatas.Count() == 0)
                    return;
                

                var lc_scanDatas = Lc_AutoMapperProfile.Mapper.Map<List<Db_ScanData>, List<Lc_ScanData>>(db_scandatas);
                var newest_scanData = lc_scanDatas.First();//这个是最新的一幅图
                
                newest_scanData.Thicks = GetMixScanData(lc_scanDatas, 0, newest_scanData.DataBegin, newest_scanData.DataEnd, request.Mix, out int mix);
                reponse.mix = mix;

                reponse.scanData = newest_scanData;
                // Last() 就是最早的数据
                reponse.scanData.Time = lc_scanDatas.Last().Time;
            });

            asyncDelegate(asyncContext, reponse);
        }


        /// <summary>
        /// 获取N幅数据 与 profile,需要异步 
        /// </summary>
        /// <param name="request"></param>
        /// <param name="asyncDelegate"></param>
        /// <param name="asyncContext"></param>
        public async void GetFrame2(Pack_GetFrameRequest request, AsyncCBHandler asyncDelegate, object asyncContext)
        {
            var reponse = new Pack_GetFrame2Reponse();

            reponse.Request = request;
            if (request.Mix > 100)//数量限制，避免死机
                request.Mix = 100;
            else if (request.Mix < 1)
                request.Mix = 1;

            await Task.Factory.StartNew(() =>
            {
                List<Db_ScanData> db_scandatas;
                if (request.Id <= 0)
                {
                    db_scandatas = dbModel.TbScanData.Find(
                        $"ORDER BY ID DESC" +
                        $" LIMIT {request.Mix} OFFSET {-request.Id}");
                }
                else
                    db_scandatas = dbModel.TbScanData.Find(
                        $"WHERE ID <= {request.Id}" +
                        $" ORDER BY ID DESC" +
                        $" LIMIT {request.Mix}");

                //数据是 从 后 到 前 排列的
                if (db_scandatas.Count() == 0)
                    return;


                var lc_scanDatas = Lc_AutoMapperProfile.Mapper.Map<List<Db_ScanData>, List<Lc_ScanData>>(db_scandatas);
                var newest_scanData = lc_scanDatas.First();//这个是最新的一幅图

                newest_scanData.Thicks = GetMixScanData(lc_scanDatas, 0, newest_scanData.DataBegin, newest_scanData.DataEnd, request.Mix, out int realMix);
                reponse.mix = realMix;
                reponse.scanData = newest_scanData;
                // Last() 就是最早的数据
                reponse.scanData.Time = lc_scanDatas.Last().Time;



                //查找 profile
                var db_profiles = dbModel.TbProfile.Find($"WHERE ID = {newest_scanData.ProfileID}");
                if (db_profiles.Count() == 0)
                    return;//没有数据
                reponse.profile = db_profiles.First();
            });

            asyncDelegate(asyncContext, reponse);
        }


        /// <summary>
        /// 获取近期的大量数据
        /// </summary>
        /// <param name="request"></param>
        /// <param name="asyncDelegate"></param>
        /// <param name="asyncContext"></param>
        [Call(typeof(Pack_GetRecentFramesReponse))]
        public async void GetRecentFrames(
            Pack_GetRecentFramesRequest request,
            AsyncCBHandler asyncDelegate, object asyncContext)
        {
            var reponse = new Pack_GetRecentFramesReponse();
            reponse.Request = request;
            if (request.Count > 100)//数量限制，避免死机
                request.Count = 100;
            else if (request.Count < 1)
                request.Count = 1;

            await Task.Factory.StartNew(() =>
            {
                List<Db_ScanData> db_scandatas;

                if (request.Id <= 0)
                    db_scandatas = dbModel.TbScanData.Find(
                        $"ORDER BY ID DESC" +
                        $" LIMIT {request.Count} OFFSET {-request.Id}");
                
                else
                    db_scandatas = dbModel.TbScanData.Find(
                        $"WHERE ID <= {request.Id}" +
                        $" ORDER BY ID DESC" +
                        $" LIMIT {request.Count}");

                //数据是 从 后 到 前 排列的
                if (db_scandatas.Count() == 0)
                    return;

                //把时间不对的，删除
                if (request.FromTime > DateTime.MinValue) {
                    int index = db_scandatas.FindLastIndex(db_scandata => db_scandata.Time <= request.FromTime);
                    if (index >= 0) {//把它后面的全部删除
                        db_scandatas.RemoveRange(index, db_scandatas.Count() - index);
                    }
                }

                if (db_scandatas.Count() == 0)
                    return;

                //反向排序
                db_scandatas.Reverse();

                var lc_scanDatas = Lc_AutoMapperProfile.Mapper.Map<List<Db_ScanData>, List<Lc_ScanData>>(db_scandatas);

                //导出的数据， ID从小排到大
                reponse.scanDatas = lc_scanDatas;
            });

            asyncDelegate(asyncContext, reponse);
        }


        /// <summary>
        /// 获取纵向趋势图
        /// </summary>
        /// <param name="request"></param>
        /// <param name="AsyncDelegate"></param>
        /// <param name="AsyncContext"></param>
        public async void GetTrend(Pack_GetTrendRequest request, AsyncCBHandler asyncDelegate, object asyncContext)
        {
            Pack_GetTrendReponse reponse = new Pack_GetTrendReponse();
            reponse.Request = request;

            if (request.Count > 200)//数量限制，避免死机
                request.Count = 200;
            else if (request.Count < 1)
                request.Count = 1;

            if (request.Mix > 10)//数量限制，避免死机
                request.Mix = 10;
            else if (request.Mix < 1)
                request.Mix = 1;

            if (request.Interval < 1)//避免 /0
                request.Interval = 1;


            await Task.Factory.StartNew(() =>
            {
                List<Db_ScanData> db_scandatas = null;

                if (request.Interval <= request.Mix)
                {
                    //间隔<=混合，全部数据连续获取
                    int cnt = request.Count * request.Interval + request.Mix - 1;
                    if (!request.IsSearchByTime)
                    {
                        if (request.Id <= 0)
                            db_scandatas = dbModel.TbScanData.Find(
                                $" ORDER BY ID DESC" +
                                $" LIMIT {cnt} OFFSET {-request.Id}");
                        else
                            db_scandatas = dbModel.TbScanData.Find(
                                $"WHERE (ID <= {request.Id})" +
                                $" ORDER BY ID DESC" +
                                $" LIMIT {cnt}");
                    }
                    else
                    {
                        db_scandatas = dbModel.TbScanData.Find(
                                $"WHERE ({nameof(Db_ScanData.Time)} <= {request.Time.ToStringOfSQLiteFieldType()})" +
                                $" ORDER BY ID DESC" +
                                $" LIMIT {cnt}");
                    }
                }
                else
                {
                    //间隔>混合 隔段获取
                    int cnt = request.Count * request.Mix;
                    if (!request.IsSearchByTime)
                    {
                        if (request.Id <= 0)
                            db_scandatas = dbModel.TbScanData.Find(
                                $"WHERE ID % {request.Interval} < {request.Mix}" +
                                $" ORDER BY ID DESC" +
                                $" LIMIT {cnt} OFFSET {-request.Id}");
                        else
                            db_scandatas = dbModel.TbScanData.Find(
                                $"WHERE (ID <= {request.Id})" +
                                $" AND ( ID % {request.Interval} < {request.Mix})" +
                                $" ORDER BY ID DESC" +
                                $" LIMIT {cnt}");
                    }
                    else
                    {
                        db_scandatas = dbModel.TbScanData.Find(
                                $"WHERE ({nameof(Db_ScanData.Time)} <= {request.Time.ToStringOfSQLiteFieldType()})" +
                                $" AND ( ID % {request.Interval} < {request.Mix})" +
                                $" ORDER BY ID DESC" +
                                $" LIMIT {cnt}");
                    }
                }

                if (db_scandatas.Count() == 0)
                    return;

                //从尾向前排的！！！！
                var lc_scanDatas = Lc_AutoMapperProfile.Mapper.Map<List<Db_ScanData>, List<Lc_ScanData>>(db_scandatas);

                reponse.Values = ToTrend2(lc_scanDatas, request.Count, request.Mix, request.Interval);

                var lc_scanData = lc_scanDatas.First();
                reponse.ScanDataId = lc_scanData.ID;


            });

            asyncDelegate(asyncContext, reponse);
        }

        double[] GetMixScanData(List<Lc_ScanData> lc_scanDatas)
        {

            var lc_scanData = lc_scanDatas[0];
            //求均值
            double[] thks = new double[lc_scanData.Thicks.Count()];
            int[] cnt = new int[thks.Count()];
            for (int j = 0; j < lc_scanDatas.Count(); j++)
            {
                int index = j;
                if (lc_scanDatas[index].Thicks.Count() != thks.Count())
                {
                    //结束，异常
                    break;
                }

                for (int i = 0; i < thks.Count(); i++)
                {
                    var v = lc_scanDatas[index].Thicks[i];
                    if (!double.IsNaN(v))
                    {
                        thks[i] += v;
                        cnt[i]++;
                    }
                }
            }
            for (int i = 0; i < thks.Count(); i++)
            {
                if (cnt[i] > 0)
                    thks[i] /= cnt[i];
                else
                    thks[i] = double.NaN;
            }

            return thks;
        }

        double[] GetMixScanData(List<Lc_ScanData> lc_scanDatas, int offset, int dataBegin, int dataEnd, int mix, out int realMix)
        {
            //lc_scanDatas[0] 是最新的数据！！

            List<Lc_ScanData> lc_scanDatas2 = new List<Lc_ScanData>();
            for (int i = 0; i < mix; i++)
            {
                int index = offset + i;
                if (index >= lc_scanDatas.Count())
                    break;
                lc_scanDatas2.Add(lc_scanDatas[offset + i]);
            }

            //连续两幅图时间间隔 大于 MixDataTimeRange 删除
            if (MixDataTimeRange > TimeSpan.Zero)
            {
                for (int i = 1; i < lc_scanDatas2.Count(); i++)
                {
                    var scanData0 = lc_scanDatas2[i - 1];
                    var scanData1 = lc_scanDatas2[i];
                    if (scanData0.Time - scanData1.Time > MixDataTimeRange)
                    {
                        lc_scanDatas2.RemoveRange(i, lc_scanDatas2.Count() - i);
                        break;
                    }
                }
            }



            //把 均值与 最新的一幅图 的均值 偏差 大于 MixDataAvgRange 删除
            if (MixDataAvgRange > 0)
            {
                var newest_scanData = lc_scanDatas2[0];

                //注意数据是没有剔除的, 计算平均值时，要明确范围
                var newest_avg = Misc.MyMath.Avg(newest_scanData.Thicks, dataBegin, dataEnd);

                if (!double.IsNaN(newest_avg))
                {
                    for (int i = 1; i < lc_scanDatas2.Count(); i++)
                    {
                        var scanData = lc_scanDatas2[i];

                        var avg = Misc.MyMath.Avg(scanData.Thicks, dataBegin, dataEnd);
                        if (double.IsNaN(avg))//一幅数据异常，全部删除
                        {
                            lc_scanDatas2.RemoveRange(i, lc_scanDatas2.Count() - i);
                            break;
                        }

                        //变化太大
                        if (Math.Abs(newest_avg - avg) > MixDataAvgRange)
                        {
                            lc_scanDatas2.RemoveRange(i, lc_scanDatas2.Count() - i);
                            break;
                        }
                    }
                }
                else
                {
                    lc_scanDatas2.RemoveRange(1, lc_scanDatas2.Count() - 1);
                }
            }

            realMix = lc_scanDatas2.Count();

            if (lc_scanDatas2.Count() > 1)
            {
                //求均值
                return GetMixScanData(lc_scanDatas2);
            }
            else
            {
                return lc_scanDatas2.First().Thicks.ToArray();
            }


        }


        List<TrendValue> ToTrend2(List<Lc_ScanData> lc_scanDatas, int count, int mix, int interval)
        {
            // lc_scanDatas.First() 是ID最大的数据
            
            //if (mix == 1)
            //{
            //    return lc_scanDatas.Select(sd =>
            //    {
            //        double avg = sd.Thicks.AverageNoNull();
            //        double sigma = sd.Thicks.Sigma();
            //        return new TrendValue()
            //        {
            //            ScanDataID = sd.ID,
            //            ProfileID = sd.ProfileID,
            //            Sigma = sigma,
            //            Value = avg,
            //            Time = sd.Time
            //        };
            //    }).ToList();
            //}

            List<TrendValue> trendValues = new List<TrendValue>();

            for (int i = 0; i < lc_scanDatas.Count(); i++)
            {
                var sd = lc_scanDatas[i];
                if (sd.ID % interval == 0) 
                {
                    //它是数据点
                    double[] thks = GetMixScanData(lc_scanDatas, i, sd.DataBegin, sd.DataEnd, mix, out int _m);

                    //求均值
                    double avg = Misc.MyMath.Avg(thks, sd.DataBegin, sd.DataEnd);
                    double sigma = Misc.MyMath.Sigma(thks, sd.DataBegin, sd.DataEnd);

                    trendValues.Add(new TrendValue()
                    {
                        ScanDataID = sd.ID,
                        ProfileID = sd.ProfileID,
                        Sigma = sigma,
                        Value = avg,
                        Time = lc_scanDatas[i].Time
                    });
                    if (trendValues.Count() >= count)
                        break;//完成
                }

            }
            return trendValues;
        }

        /// <summary>
        /// 完成
        /// </summary>
        public void Finish()
        {
            //shareDb.FinishProfile();
            FinishEvent?.Invoke();
        }
        public event Action FinishEvent;
    }
}
