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>
        /// 
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;

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

        public Func<BulkDbTempFrameChangedEventArgs> GetTempFrameAction;

        [Push(typeof(ScanDataIdChangedEventArgs))]
        public event EventHandler ScanDataIdChanged;

        /// <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;
            //清除本地缓存
            localDb.CurrProfile = profile;
            localDb.ProfileStartTime = profile.StartTime;
            localDb.ProfileEndTime = profile.EndTime;
            localDb.IsProfileFinished = profile.IsFinished;

            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;

            ScanDataIdChanged?.Invoke(this, new ScanDataIdChangedEventArgs() { LastScanDataId = LastScanDataId });
        }

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

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


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

            localDb.CurrProfile = profile;
            localDb.ProfileStartTime = profile.StartTime;
            localDb.ProfileEndTime = profile.EndTime;
            localDb.IsProfileFinished = profile.IsFinished;
            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 {nameof(Db_Profile.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)
        {
            Pack_GetFrameReponse 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 {nameof(Db_ScanData.ID)} DESC" +
                        $" LIMIT {request.Mix} OFFSET {-request.Id}");
                }
                else
                    db_scandatas = dbModel.TbScanData.Find(
                        $"WHERE {nameof(Db_ScanData.ID)} <= {request.Id}" +
                        $" ORDER BY {nameof(Db_ScanData.ID)} DESC" +
                        $" LIMIT {request.Mix}");

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

                var lc_scanDatas = Lc_AutoMapperProfile.Mapper.Map<List<Db_ScanData>, List<Lc_ScanData>>(db_scandatas);
                var lc_scanData = lc_scanDatas.First();
                if (lc_scanDatas.Count() > 1)
                {
                    //把时间小于 request.StartTime 删除
                    //从后向前

                    if (request.StartTime != DateTime.MinValue)
                    {
                        List<Lc_ScanData> lc_scanDatas2 = lc_scanDatas.Where(s => s.Time >= request.StartTime).ToList();

                        if (lc_scanDatas2.Count() == 0)
                        {
                            lc_scanDatas2 = new List<Lc_ScanData>();
                            lc_scanDatas2.Add(lc_scanDatas.First());
                        }
                        lc_scanDatas = lc_scanDatas2;
                    }
                    //求均值
                    lc_scanData.Thicks = GetMixScanData(lc_scanDatas, 0, lc_scanDatas.Count());
                }

                reponse.scanData = lc_scanData;
                reponse.scanData.Time = lc_scanDatas.Last().Time;
            });

            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)
        {
            //TODO 需要异步
            Pack_GetTrendReponse reponse = new Pack_GetTrendReponse();
            reponse.Request = request;

            if (request.Count > 400)//数量限制，避免死机
                request.Count = 400;
            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.IsSearchByTime)
                {
                    if (request.Id <= 0)
                        db_scandatas = dbModel.TbScanData.Find(
                            $"WHERE {nameof(Db_ScanData.ID)} % {request.Interval} = 0" +
                            $" ORDER BY {nameof(Db_ScanData.ID)} DESC" +
                            $" LIMIT {request.Mix + request.Count - 1} OFFSET {-request.Id}");
                    else
                        db_scandatas = dbModel.TbScanData.Find(
                            $"WHERE ({nameof(Db_ScanData.ID)} <= {request.Id})" +
                            $" AND ({nameof(Db_ScanData.ID)} % {request.Interval} = 0)" +
                            $" ORDER BY {nameof(Db_ScanData.ID)} DESC" +
                            $" LIMIT {request.Mix + request.Count - 1}");
                }
                else
                {
                    db_scandatas = dbModel.TbScanData.Find(
                            $"WHERE ({nameof(Db_ScanData.Time)} <= {request.Time.ToStringOfSQLiteFieldType()})" +
                            $" AND ({nameof(Db_ScanData.ID)} % {request.Interval} = 0)" +
                            $" ORDER BY {nameof(Db_ScanData.ID)} DESC" +
                            $" LIMIT {request.Mix + request.Count - 1}");
                }


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

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

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

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


            });

            asyncDelegate(asyncContext, reponse);
        }

        double[] GetMixScanData(List<Lc_ScanData> lc_scanDatas, int offset, int mix)
        {

            var lc_scanData = lc_scanDatas[offset];
            //求均值
            double[] thks = new double[lc_scanData.Thicks.Count()];
            int[] cnt = new int[thks.Count()];
            for (int j = offset; j < offset + mix && 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;
        }
        List<TrendValue> ToTrend(List<Lc_ScanData> lc_scanDatas, int count, int mix)
        {

            if (mix == 1)
            {
                return lc_scanDatas.Select(sd =>
                {
                    double avg = sd.Thicks.AverageNoNull();
                    double sigma = sd.Thicks.Sigma();
                    return new TrendValue()
                    {
                        Sigma = sigma,
                        Value = avg,
                        Time = sd.Time
                    };
                }).ToList();
            }

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

            for (int i = 0; i < lc_scanDatas.Count() && i < count; i++)
            {
                //求均值
                double[] thks = GetMixScanData(lc_scanDatas, i, mix);
                double avg = thks.AverageNoNull();
                double sigma = thks.Sigma();

                trendValues.Add(new TrendValue()
                {
                    Sigma = sigma,
                    Value = avg,
                    Time = lc_scanDatas[i].Time
                });
            }
            return trendValues;
        }



        /// <summary>
        /// 完成
        /// </summary>
        public void Finish()
        {
            shareDb.FinishProfile();
        }

    }
}
