using AutoMapper;
using FLY.Thick.Base.Common;
using FLY.Thick.Base.IService;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;

namespace FLY.Thick.Base.Server
{
    public class CurveCollection : ICurveService
    {
        #region 数据文件需要保存的数据项

        /// <summary>
        /// AD曲线校正方式
        /// </summary>
        public CurveCorrectWay CorrectWay { get; set; } = CurveCorrectWay.OnePointIsScale;

        public CurveType Flag { get; set; } = CurveType.E;

        /// <summary>
        /// 输入的曲线, 排列顺序,Value 从 小到大
        /// </summary>
        [PropertyChanged.DoNotCheckEquality]
        public CurveCell[] Curves { get; set; }

        #endregion


        public struct ExChange
        {
            public ExChange(int orgAd, int currAd)
            {
                OrgAD = orgAd;
                CurrAD = currAd;
            }
            public int OrgAD { get; set; }
            public int CurrAD { get; set; }
        }
        /// <summary>
        /// 真实样品校正点
        /// </summary>
        List<ExChange> RevisingCurves = new List<ExChange>();

        private string param_path = "curve.json";
        public CurveCollection()
        {
            SetDefault();
            ReviseCurve();
        }

        public CurveCollection(string param_path)
        {
            if (!string.IsNullOrEmpty(param_path))
                this.param_path = param_path;

            SetDefault();
            if (!Load())
            {
                Save();
            }
            Curves = Curves.OrderBy(c => c.Value).ToArray();
            RevisingCurves.Clear();
            ReviseCurve();

        }
        void SetDefault()
        {
            CorrectWay = CurveCorrectWay.OnePointIsScale;
            Flag = CurveType.E;
            //Curves = new CurveCell[] {
            //    new CurveCell() { AD = 57564, Value = 0 },
            //    new CurveCell() { AD = 30850, Value = 88 },
            //    new CurveCell() { AD = 19000, Value = 176 },
            //    new CurveCell() { AD = 12528, Value = 264 },
            //    new CurveCell() { AD = 8409, Value = 352 },
            //    new CurveCell() { AD = 5650, Value = 440 },
            //    new CurveCell() { AD = 3779, Value = 528 },
            //    new CurveCell() { AD = 2513, Value = 616  },
            //    new CurveCell() { AD = 1660, Value = 704 }
            //};

            Curves = new CurveCell[] {
                new CurveCell() { Value = 0, AD = 50300 },
                new CurveCell() { Value = 19, AD = 46505 },
                new CurveCell() { Value = 38, AD = 42999 },
                new CurveCell() { Value = 58, AD = 39698 },
                new CurveCell() { Value = 78, AD = 36646 },
                new CurveCell() { Value = 98, AD = 33894 },
                new CurveCell() { Value = 118, AD = 31229 },
                new CurveCell() { Value = 137, AD = 28934 },
                new CurveCell() { Value = 156, AD = 26818 },
                new CurveCell() { Value = 175, AD = 24908 },
                new CurveCell() { Value = 194, AD = 23220 },
                new CurveCell() { Value = 213, AD = 21558 },
                new CurveCell() { Value = 232, AD = 20057 },
                new CurveCell() { Value = 251, AD = 18606 },
                new CurveCell() { Value = 271, AD = 17283 },
                new CurveCell() { Value = 290, AD = 16035 }
                };
            
        }
        public void Apply(CurveCorrectWay correctWay, CurveType flag, CurveCell2[] curves)
        {
            CorrectWay = correctWay;
            Flag = flag;

            Curves = curves.Select(c2 => new CurveCell() { AD = c2.AD, Value = c2.Value }).ToArray();
            ClearExChange();
            ReviseCurve();

            Save();
        }


        #region ExChange

        public void ModifyExChange(List<ExChange> list)
        {
            RevisingCurves.Clear();
            for (int i = 0; i < list.Count(); i++)
            {
                //检测RevisingCurves 正确性,避免 /0 溢出
                if ((list[i].OrgAD <= 10) || (list[i].CurrAD <= 10))
                {
                    //异常
                    continue;
                }
                var v = from rc in RevisingCurves where rc.OrgAD == list[i].OrgAD select rc;
                if (v.Count() > 0)
                {
                    //异常,重复了
                    continue;
                }
                RevisingCurves.Add(list[i]);
            }
            RevisingCurves.OrderByDescending(c => c.OrgAD);
        }


        public void ClearExChange()
        {
            RevisingCurves.Clear();
        }
        #endregion


        public static bool CheckRevisingPointValid(int ad)
        {
            if (ad < 3)
            {
                return false;
            }
            else
            {
                return true;
            }
        }

        /// <summary>
        /// 根据样品值,修正曲线,只支持2个样品
        /// </summary>
        /// <returns></returns>
        public bool ReviseCurve()
        {
            if (RevisingCurves.Count < 1)//没数据
            {
                foreach (CurveCell c in Curves)
                {
                    c.RevisedAD = c.AD;
                }
                return false;
            }

            if (RevisingCurves.Count == 1)//只有一个校正点
            {
                switch (CorrectWay)
                {
                    case CurveCorrectWay.OnePointIsScale:
                        {
                            double ux = ((double)RevisingCurves[0].CurrAD) / RevisingCurves[0].OrgAD;

                            foreach (CurveCell c in Curves)
                            {
                                c.RevisedAD = (int)(c.AD * ux);
                            }
                        }
                        break;
                    default:
                        //case CurveCorrectWay.OnePointIsOffset:
                        {
                            int x = RevisingCurves[0].CurrAD - RevisingCurves[0].OrgAD;

                            foreach (CurveCell c in Curves)
                            {
                                c.RevisedAD = c.AD + x;
                            }
                        }
                        break;
                }

                return true;
            }
            else
            {
                int adh = RevisingCurves[0].OrgAD;
                int adl = RevisingCurves[1].OrgAD;
                double uh = ((double)RevisingCurves[0].CurrAD) / adh;
                double ul = ((double)RevisingCurves[1].CurrAD) / adl;

                for (int i = 0, j = 1; i < Curves.Count(); i++)
                {
                    double ux = uh - (adh - Curves[i].AD) * (uh - ul) / (adh - adl);
                    Curves[i].RevisedAD = (int)(Curves[i].AD * ux);
                }
                return true;
            }
        }

        #region Ad To Value

        double AD2Value_E(int ad, AD2ValueFlag flag)
        {
            int i;
            double value;

            if (Curves.Count() < 1) return -1;
            if (ad < 0) return -1;
            if (ad == 0) ad = 1;
            if (flag == AD2ValueFlag.NoRevised)
            {
                for (i = 0; i < Curves.Count(); i++)
                {
                    if (ad < Curves[i].AD)
                        continue;
                    else
                        break;
                }
            }
            else
            {
                for (i = 0; i < Curves.Count(); i++)
                {
                    if (ad < Curves[i].RevisedAD)
                        continue;
                    else
                        break;
                }
            }

            if (i >= Curves.Count())
                i = Curves.Count() - 1;
            if (i == 0)
                i = 1;

            var c0 = Curves[i - 1];
            var c1 = Curves[i];
            double ad0, ad1, ad2;
            if (flag == AD2ValueFlag.NoRevised)
            {
                ad0 = Math.Log(c0.AD, Math.E);
                ad1 = Math.Log(c1.AD, Math.E);
                ad2 = Math.Log(ad, Math.E);
            }
            else
            {
                ad0 = Math.Log(c0.RevisedAD, Math.E);
                ad1 = Math.Log(c1.RevisedAD, Math.E);
                ad2 = Math.Log(ad, Math.E);
            }
            double u = (c1.Value - c0.Value) / (ad1 - ad0);
            value = (ad2 - ad0) * u + c0.Value;

            return value;
        }
        
        
        double AD2Value_Line(int ad, AD2ValueFlag flag)
        {
            int i;
            double value;

            if (Curves.Count() < 2) return -1;
            if (ad < 0) return -1;
            if (ad == 0) ad = 1;
            bool isDescending = true;//降序排列
            if (Curves[0].AD < Curves[1].AD)
                isDescending = false;
            //找 ad0<ad<adi

            if (flag == AD2ValueFlag.NoRevised)
            {
                if (isDescending)//降序排列
                {
                    for (i = 0; i < Curves.Count(); i++)
                    {
                        if (ad < Curves[i].AD)
                            continue;
                        else
                            break;
                    }
                }
                else
                {
                    for (i = 0; i < Curves.Count(); i++)
                    {
                        if (ad > Curves[i].AD)
                            continue;
                        else
                            break;
                    }
                }
            }
            else
            {
                if (isDescending)//降序排列
                {
                    for (i = 0; i < Curves.Count(); i++)
                    {
                        if (ad < Curves[i].RevisedAD)
                            continue;
                        else
                            break;
                    }
                }
                else
                {
                    for (i = 0; i < Curves.Count(); i++)
                    {
                        if (ad > Curves[i].RevisedAD)
                            continue;
                        else
                            break;
                    }
                }
            }

            if (i >= Curves.Count()) i = Curves.Count() - 1;
            if (i == 0) i = 1;

            var c0 = Curves[i - 1];
            var c1 = Curves[i];
            double ad0, ad1, ad2 = ad;
            if (flag == AD2ValueFlag.NoRevised)
            {
                ad0 = c0.AD;
                ad1 = c1.AD;
            }
            else
            {
                ad0 = c0.RevisedAD;
                ad1 = c1.RevisedAD;
            }
            double u = (c1.Value - c0.Value) / (ad1 - ad0);
            value = (ad2 - ad0) * u + c0.Value;
            return value;
        }
        

        public double AD2Value(int ad, AD2ValueFlag flag)
        {
            double value;
            switch (Flag)
            {
                case CurveType.Line:
                    value = AD2Value_Line(ad, flag);
                    break;
                default:
                    value = AD2Value_E(ad, flag);
                    break;
            }
            return value;
        }
        #endregion

        #region Value To Ad
        /// <summary>
        /// 基本没有用
        /// </summary>
        /// <param name="value"></param>
        /// <param name="flag"></param>
        /// <returns></returns>
        public int Value2Ad(double value, AD2ValueFlag flag) {
            int ad;
            switch (Flag)
            {
                case CurveType.Line:
                    ad = Value2Ad_Line(value, flag);
                    break;
                default:
                    ad = Value2Ad_E(value, flag);
                    break;
            }
            return ad;
        }

        int Value2Ad_E(double value, AD2ValueFlag flag)
        {
            int i;
            int ad;

            if (Curves.Count() < 1) return -1;

            for (i = 0; i < Curves.Count(); i++)
            {
                if (value > Curves[i].Value)
                    continue;
                else
                    break;
            }

            if (i >= Curves.Count())
                i = Curves.Count() - 1;
            if (i == 0)
                i = 1;

            var c0 = Curves[i - 1];
            var c1 = Curves[i];
            double ad0, ad1, ad2;
            if (flag == AD2ValueFlag.NoRevised)
            {
                ad0 = Math.Log(c0.AD, Math.E);
                ad1 = Math.Log(c1.AD, Math.E);
                //ad2 = Math.Log(ad, Math.E);
            }
            else
            {
                ad0 = Math.Log(c0.RevisedAD, Math.E);
                ad1 = Math.Log(c1.RevisedAD, Math.E);
                //ad2 = Math.Log(ad, Math.E);
            }
            double u = (c1.Value - c0.Value) / (ad1 - ad0);
            ad2 = (value - c0.Value) / u + ad0;
            ad = (int)Math.Round(Math.Pow(Math.E, ad2));
            return ad;
        }

        int Value2Ad_Line(double value, AD2ValueFlag flag)
        {
            int i;
            int ad;

            if (Curves.Count() < 2) return -1;

            for (i = 0; i < Curves.Count(); i++)
            {
                if (value > Curves[i].Value)
                    continue;
                else
                    break;
            }

            if (i >= Curves.Count()) i = Curves.Count() - 1;
            if (i == 0) i = 1;

            var c0 = Curves[i - 1];
            var c1 = Curves[i];
            double ad0, ad1, ad2;
            if (flag == AD2ValueFlag.NoRevised)
            {
                ad0 = c0.AD;
                ad1 = c1.AD;
            }
            else
            {
                ad0 = c0.RevisedAD;
                ad1 = c1.RevisedAD;
            }
            double u = (c1.Value - c0.Value) / (ad1 - ad0);
            ad2 = (value - c0.Value) / u + ad0;
            ad = (int)Math.Round(ad2);
            return ad;
        }
        
        #endregion


        public bool Load()
        {
            return CurveCollectionJsonDb.Load(this, param_path);
        }
        public bool Save()
        {
            return CurveCollectionJsonDb.Save(this, param_path);
        }

        #region INotifyPropertyChanged 成员

        public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
        #endregion
    }


    public class CurveCollectionJsonDb
    {
        static Mapper Mapper { get; } = new AutoMapper.Mapper(new MapperConfiguration(c =>
        {
            c.CreateMap<CurveCollection, CurveCollectionJsonDb>()
            .ForMember(s => s.Curves, opt =>
            {
                opt.MapFrom(s => s.Curves == null ? null : s.Curves.Select(cu => new CurveCell2() { AD = cu.AD, Value = cu.Value }).ToArray());

            }).ReverseMap().ForMember(s => s.Curves, opt =>
            {
                opt.MapFrom(s => s.Curves == null ? null : s.Curves.Select(cu => new CurveCell() { AD = cu.AD, Value = cu.Value }).ToArray());
            });
        }));
        public static bool Load(CurveCollection src, string filePath)
        {
            try
            {
                if (File.Exists(filePath))
                {
                    string json = File.ReadAllText(filePath);
                    var p = JsonConvert.DeserializeObject<CurveCollectionJsonDb>(json);
                    Mapper.Map(p, src);
                    return true;
                }
            }
            catch
            {
                //异常,没有json 解码失败
            }

            return false;
        }
        public static bool Save(CurveCollection src, string filePath)
        {
            var p = Mapper.Map<CurveCollectionJsonDb>(src);
            try
            {
                File.WriteAllText(filePath, JsonConvert.SerializeObject(p, Formatting.Indented));
                return true;
            }
            catch
            {
                //异常,没有json 编码失败

            }
            return false;

        }

        /// <summary>
        /// AD曲线校正方式
        /// </summary>
        public CurveCorrectWay CorrectWay = CurveCorrectWay.OnePointIsScale;

        public CurveType Flag = CurveType.E;

        public CurveCell2[] Curves = new CurveCell2[] {
            new CurveCell2() { AD = 57564, Value = 0 },
            new CurveCell2() { AD = 30850, Value = 88 },
            new CurveCell2() { AD = 19000, Value = 176 },
            new CurveCell2() { AD = 12528, Value = 264 },
            new CurveCell2() { AD = 8409, Value = 352 },
            new CurveCell2() { AD = 5650, Value = 440 },
            new CurveCell2() { AD = 3779, Value = 528 },
            new CurveCell2() { AD = 2513, Value = 616  },
            new CurveCell2() { AD = 1660, Value = 704 }
        };
    }
}