using FLY.OBJComponents.Server.Model;
using FLY.Thick.FilmCasting.Server.Model;
using FLY.Thick.FilmCasting.UI.DbViewer.Db.ModelAccessory;
using Misc;
using SQLite;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;

namespace FLY.Thick.FilmCasting.UI.DbViewer.Db
{
    public class DbHelper : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        public const string DefaultDbDirPath = @"D:\film_casting_data";
        public string DbDirPath { get; private set; } = DefaultDbDirPath;

        /// <summary>
        /// 月份路径
        /// </summary>
        public string MonthPath { get; private set; }

        public string DbPathThk
        {
            get
            {
                if (string.IsNullOrEmpty(MonthPath))
                {
                    return System.IO.Path.Combine(DbDirPath, "thickness.sqlite3");
                }
                else
                {
                    return System.IO.Path.Combine(DbDirPath, MonthPath, "thickness.sqlite3");
                }
            }
        }


        public bool IsThkExist { get; private set; }
        public bool IsDbExist
        {
            get
            {
                return IsThkExist;
            }
        }
        public DateTime ThkDbBeginTime { get; private set; }

        public DateTime ThkDbEndTime { get; private set; }

        public DateTime DbBeginTime { get; private set; }

        public DateTime DbEndTime { get; private set; }

        /// <summary>
        /// 出错代码
        /// </summary>
        public string ErrorMsg { get; private set; }

        public bool IsDbErr { get; private set; }

        FLY.Thick.FilmCasting.Server.Model.DbModel dbModelThk = null;
        AccessoryDbModel dbModelAcc = null;
        string DbPathAcc => System.IO.Path.Combine(DbDirPath, "accessory.sqlite3");

        

        public DbHelper()
        {

        }


        /// <summary>
        /// 从表获取数据
        /// </summary>
        /// <param name="monthPath">月份文件夹， null为近2个月</param>
        /// <returns></returns>
        public bool Init(string dbDirPath, string monthPath)
        {
            DbDirPath = dbDirPath;
            MonthPath = monthPath;
            IsThkExist = System.IO.File.Exists(DbPathThk);

            if (!IsDbExist)
            {
                //没有任何数据
                ErrorMsg = $"不能找到任何数据";
                IsDbErr = true;
                return false;
            }

            if (IsThkExist)
            {
                if (!Init_thk())
                {
                    return false;
                }
            }


            dbModelAcc = new AccessoryDbModel(DbPathAcc);
            dbModelAcc.Init();

            DbBeginTime = ThkDbBeginTime;
            DbEndTime = ThkDbEndTime;

            return true;
        }

        bool Init_thk()
        {
            try
            {
                dbModelThk = new FLY.Thick.FilmCasting.Server.Model.DbModel(DbPathThk);
                if (!dbModelThk.InitNoBuild())
                {
                    ErrorMsg = $"{DbPathThk} 数据库格式异常";
                    IsDbErr = true;
                    return false;
                }
                //获取 第1幅扫描数据的时间
                string cmd = $"SELECT Time FROM {dbModelThk.TbScanData.TableName} ORDER BY ID LIMIT 1";
                var ret = dbModelThk.sqliteHelper.ExecuteScalar(cmd);
                ThkDbBeginTime = System.Convert.ToDateTime(ret);

                //获取 最后1幅扫描数据的时间
                cmd = $"SELECT Time FROM {dbModelThk.TbScanData.TableName} ORDER BY ID DESC LIMIT 1";
                ret = dbModelThk.sqliteHelper.ExecuteScalar(cmd);
                ThkDbEndTime = System.Convert.ToDateTime(ret);
            }
            catch (Exception e)
            {
                ErrorMsg = e.Message;
                IsDbErr = true;
                return false;
            }
            return true;
        }

        /// <summary>
        /// 获取月份列表
        /// </summary>
        /// <returns></returns>
        public List<string> GetMonthList()
        {
            List<string> monthList = new List<string>();
            //枚举文件夹 全部 yyyy-MM 格式的数据
            Regex regex = new Regex(@"\A(\d\d\d\d)-(\d\d)\z");
            var dirInfo = new DirectoryInfo(DbDirPath);
            var dirInfos = dirInfo.GetDirectories();
            if (dirInfos == null || dirInfos.Count() == 0)
                return monthList;

            foreach (var dinfo in dirInfos)
            {
                var match = regex.Match(dinfo.Name);
                if (match.Success)
                {
                    if (!int.TryParse(match.Groups[1].Value, out int y))
                        continue;
                    if (!int.TryParse(match.Groups[2].Value, out int m))
                        continue;
                    if (m > 12 || m < 1)
                        continue;//异常
                    if (y < 1900 || y > 2100)
                        continue;//异常

                    monthList.Add($"{match.Groups[1].Value}-{match.Groups[2].Value}");
                }
            }
            return monthList;
        }

        /// <summary>
        /// 删除profile
        /// </summary>
        /// <param name="profile"></param>
        public void DeleteProfile(FLY.Thick.FilmCasting.Server.Model.Db_Profile profile)
        {
            if (IsProfileDeleted(profile.ID, profile.StartTime))
            {
                //已经删除
                return;
            }
            else
            {
                dbModelAcc.TbProfileDelete.Add(new Db_ProfileDelete()
                {
                    ID = dbModelAcc.TbProfileDelete.FreeID,
                    Time = DateTime.Now,
                    ProfileID = profile.ID,
                    ProfileTime = profile.StartTime
                });
            }
        }
        /// <summary>
        /// 恢复删除的profile
        /// </summary>
        /// <param name="profileDeleteID"></param>
        public void RecoverProfile(Int64 profileDeleteID)
        {
            dbModelAcc.sqliteHelper.ExecuteNonQuery(
                $"DELETE FROM {dbModelAcc.TbProfileDelete.TableName}" +
                $" WHERE ID = {profileDeleteID}");
        }

        bool IsProfileDeleted(Int64 profileId, DateTime startTime)
        {
            if (dbModelAcc.TbProfileDelete.Find(
                $"WHERE {nameof(Db_ProfileDelete.ProfileID)}={profileId} " +
                $"AND {nameof(Db_ProfileDelete.ProfileTime)}={startTime.ToStringOfSQLiteFieldType()}").Count() > 0)
            {
                //已经删除
                return true;
            }
            else
            {
                return false;
            }
        }

        /// <summary>
        /// 获取删除profile记录列表
        /// </summary>
        /// <param name="starttime"></param>
        /// <param name="endtime"></param>
        /// <returns></returns>
        public List<Db_ProfileDelete> GetProfileDeleteList(DateTime starttime, DateTime endtime)
        {
            string startTime_str = starttime.Date.ToStringOfSQLiteFieldType();
            string endTime_str = endtime.Date.AddDays(1).ToStringOfSQLiteFieldType();
            string sql =
                $"WHERE {nameof(Db_ProfileDelete.Time)} >= {startTime_str} AND {nameof(Db_ProfileDelete.Time)} <= {endTime_str}";


            return dbModelAcc.TbProfileDelete.Find(sql);
        }


        /// <summary>
        /// 获取profiles,已经被删除的，不显示
        /// </summary>
        /// <param name="starttime"></param>
        /// <param name="endtime"></param>
        /// <param name="pname"></param>
        /// <returns></returns>
        public List<Db_Profile> GetProfileList(DateTime starttime, DateTime endtime, string pname, string batch)
        {
            string startTime_str = starttime.Date.ToStringOfSQLiteFieldType();
            string endTime_str = endtime.Date.AddDays(1).ToStringOfSQLiteFieldType();
            string pname_str = $"%{pname}%".ToStringOfSQLiteFieldType();
            string batch_str = $"%{batch}%".ToStringOfSQLiteFieldType();
            string sql =
                "WHERE " +
                "(" +
                $"({nameof(Db_Profile.StartTime)} > {startTime_str} AND {nameof(Db_Profile.StartTime)} < {endTime_str})" +
                $" OR ({nameof(Db_Profile.EndTime)} > {startTime_str} AND {nameof(Db_Profile.EndTime)} < {endTime_str})" +
                ") " +
                $" AND ({nameof(Db_Profile.PName)} LIKE {pname_str})" +
                $" AND ({nameof(Db_Profile.Batch)} LIKE {batch_str})";

            var db_profiles = dbModelThk.TbProfile.Find(sql);

            //剔除 在 accessory 中记录的删除行
            db_profiles.RemoveAll(p => IsProfileDeleted(p.ID, p.StartTime));

            return db_profiles;
        }


        public DbProfilePack GetProfilePack(Db_Profile db_Profile)
        {
            DbProfilePack profilePack = new DbProfilePack();
            profilePack.Profile = db_Profile;
            GetProfilePack_thk(db_Profile, profilePack);
            return profilePack;
        }


        void GetProfilePack_thk(Db_Profile db_Profile, DbProfilePack profilePack)
        {
            string startTime_str = db_Profile.StartTime.ToStringOfSQLiteFieldType();
            string endTime_str = db_Profile.EndTime.ToStringOfSQLiteFieldType();

            string condition;

            condition =
                $"WHERE {nameof(Db_ScanData.Time)}>={startTime_str}" +
                $" AND {nameof(Db_ScanData.EndTime)}<={endTime_str}";

            var db_scandatas = dbModelThk.TbScanData.Find(condition);
            var lc_scandatas = Lc_AutoMapperProfile.Mapper.Map<List<Db_ScanData>, List<Lc_ScanData>>(db_scandatas);
            profilePack.ScanDatas = lc_scandatas;

            condition =
                $"WHERE {nameof(Db_Error.Time)}>={startTime_str}" +
                $" AND {nameof(Db_Error.Time)}<={endTime_str}";
            var db_errors = dbModelThk.TbError.Find(condition);
            profilePack.Errors = db_errors;



        }

    }


    #region 链接表的关系
    /// <summary>
    /// 包装 DB_Profile， 组织 DB_Profile， DB_ScanData....
    /// </summary>
    public class DbProfilePack:INotifyPropertyChanged
    {
        public FLY.Thick.FilmCasting.Server.Model.Db_Profile Profile;
        public List<FLY.Thick.FilmCasting.Server.Model.Lc_ScanData> ScanDatas;
        public List<Db_Error> Errors;

        #region 能重新调整的参数
        public double USL
        {
            get
            {
                return Target + Target * TolerancePercent;
            }
        }
        public double LSL
        {
            get
            {
                return Target - Target * TolerancePercent;
            }
        }
        public double Target { get; set; }
        public double YMax
        {
            get
            {
                return Target + Target * TolerancePercent * YRangePercent;
            }
        }
        public double YMin
        {
            get
            {
                return Target - Target * TolerancePercent * YRangePercent;
            }
        }
        public double TolerancePercent { get; set; }
        public double YRangePercent { get; set; } = 2;

        #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 void Init(bool isJustRunning) {
            if (ScanDatas.Count() > 0) {
                NBolts = ScanDatas.Last().Thicks.Count();
            }
            if (isJustRunning)
            {
                //把静止下的删除！！！！！
                ScanDatas.RemoveAll(sd => sd.FilmVelocity == 0);
            }


            Target = Profile.Target;
            TolerancePercent = Profile.TolerancePercent;
            UpdateStatistics();
        }


        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;


        /// <summary>
        /// 统计 的 Y是 数量
        /// </summary>
        /// <param name="step"></param>
        /// <returns></returns>
        List<XY> GetHistogram(double step)
        {
            List<XY> list = new List<XY>();
            foreach (var scanData in ScanDatas)
            {
                foreach (var thk in scanData.Thicks) {
                    if (double.IsNaN(thk))
                        continue;

                    double x = ((int)(thk / step)) * step;
                    XY xy = list.Find(_xy => _xy.X == x);
                    if (xy != null)
                    {
                        xy.Y++;
                    }
                    else
                    {
                        xy = new XY(x, 1);
                        list.Add(xy);
                    }
                    
                }
            }

            list.Sort();
            
            return list;
        }


        /// <summary>
        /// 以 YMin 到 YMax 为范围，统计直方图
        /// </summary>
        /// <returns></returns>
        public List<XY> GetHistogram2(double step)
        {
            double xmin = YMin;
            double xmax = YMax;

            int xmin_core = ((int)(xmin / step));
            int xmax_core = ((int)(xmax / step));


            List<XY> all_values = GetHistogram(step);

            List<XY> values = new List<XY>();
            for (int x_core = xmin_core; x_core <= xmax_core; x_core++)
            {
                var list = from v in all_values where (int)(v.X / step) == x_core select v;
                if (list.Count() == 0)
                    values.Add(new XY(x_core * step, 0));
                else
                    values.Add(list.First());
            }
            return values;
        }

        public List<TrendValue> GetTrend() 
        {
            List<TrendValue> trendValues = new List<TrendValue>();
            for (int i = 0; i < ScanDatas.Count(); i++) 
            {
                var scanData = ScanDatas[i];
               
                double avg = scanData.Thicks.AverageNoNull();
                double sigma = scanData.Thicks.Sigma();
                trendValues.Add(new TrendValue() { Sigma = sigma, Time = scanData.Time, Value = avg });
            }
            return trendValues;
        }
    }
    #endregion


    public class TrendValue
    {
        public double Sigma { get; set; }
        public double Value { get; set; }
        public DateTime Time { get; set; }
    }

}
