using FLY.Thick.Base.Common;
using FLY.Thick.Base.IService;
using FLY.Thick.FilmCasting.IService;
using FObjBase;
using Misc;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;

namespace FLY.Thick.FilmCasting.Server
{
    /// <summary>
    /// 螺丝位 从 0 开始
    /// </summary>
    public class BoltMapFilmCasting : IBoltMapFilmCastingService, IBoltMapBase
    {
        #region 基本设定

        /// <summary>
        /// 螺丝宽度 mm
        /// </summary>
        public double BoltWidth { get; set; } = 25.4;

        /// <summary>
        /// 总螺丝数 
        /// </summary>
        public int BoltCnt { get; set; } = 97;

        /// <summary>
        /// 膜中心位置 mm
        /// </summary>
        public int MidMm { get; set; } = 1000;

        /// <summary>
        /// 与机架方向相反
        /// </summary>
        public bool IsReversed { get; set; }

        /// <summary>
        /// 平滑，以前后分区平均滤波 ，=0，只有自己，1，左右各一个
        /// </summary>
        public int Smooth { get; set; }

        public BORDER_TYPE BorderType => BORDER_TYPE.Mid;
        #endregion



        #region 状态
        /// <summary>
        /// 左单边不工作螺丝位
        /// </summary>
        public int LeftEmptyBolts { get; private set; }

        /// <summary>
        /// 右单边不工作螺丝位
        /// </summary>
        public int RightEmptyBolts { get; private set; }

        /// <summary>
        /// 收缩率  Uniform(均匀区)Shrink(收缩)Percent(比例)
        /// </summary>
        public double UniformShrinkPercent { get; private set; }

        /// <summary>
        /// 总分区数
        /// </summary>
        public int NBolts { get; protected set; } = 100;

        /// <summary>
        /// 第1个分区号
        /// </summary>
        public int FirstBoltNo { get; set; } = 1;

        public DateTime DebugUpdate { get; private set; }
        #endregion

        /// <summary>
        /// mm/脉冲
        /// </summary>
        public double Mmpp { get; set; } = 0.1;


        /// <summary>
        /// 机架长度 脉冲
        /// </summary>
        public int PosLength { get; set; } = 8000;



        //脉冲为单位分区表
        private List<RangeStruct> bolts = new List<RangeStruct>();
        private DynBoltAD dyn;
        private DynBoltValue dynValue;
        private BoltMapHelper helper;
        public event ActiveEventHandler ActiveEvent;



        private string file_path = "boltmap.json";
        public BoltMapFilmCasting()
        {
            dyn = new DynBoltAD(this);
            dynValue = new DynBoltValue(this);
            helper = new BoltMapHelper(this);
            Load();
        }

        public void Init(double mmpp, int leftEmptyBolts, int rightEmptyBolts,double uniformShrinkPercent)
        {
            Mmpp = mmpp;
            LeftEmptyBolts = leftEmptyBolts;
            RightEmptyBolts = rightEmptyBolts;
            UniformShrinkPercent = uniformShrinkPercent;
            CreateAvgBolts();
        }

        /// <summary>
        /// 创建 分区表, 平均分区
        /// </summary>
        /// <param name="mid">中间位置 脉冲</param>
        /// <param name="boltWidth">螺丝宽度 脉冲</param>
        /// <param name="boltCnt">螺丝总数</param>
        /// <param name="boltIndexOfMid">中心螺丝序号</param>
        /// <param name="isboltCntEven">螺丝总数为双数</param>
        /// <param name="isReversed">与机架反向</param>
        /// <param name="bolts">输出 分区表</param>
        void create_bolts(int mid, double boltWidth, int boltCnt, int boltIndexOfMid, bool isboltCntEven, bool isReversed, out List<RangeStruct> bolts)
        {
            bolts = new List<RangeStruct>();

            //中间螺丝位 序号， boltIndexOfMid
            //当为双数时，为中间偏大的那个，
            //当为单数时，就是中间的


            //中间螺丝位 开始位置
            double pos_mid = mid;
            if (!isboltCntEven)
                pos_mid -= boltWidth / 2;

            int bolti;
            double b, e;

            bolti = boltIndexOfMid;
            b = pos_mid;
            while (true)
            {
                if (bolti - 1 < 0)
                    break;

                bolti--;
                //脉冲可以有负数
                e = b - 1;
                b = e - boltWidth;

                bolts.Insert(0, new RangeStruct((int)b, (int)e));
            }

            bolti = boltIndexOfMid;
            b = pos_mid;
            while (true)
            {
                e = b + boltWidth;
                bolts.Add(new RangeStruct((int)b, (int)e));

                if (bolts.Count() == boltCnt)
                {
                    //这是最后一个
                    break;
                }

                b = e + 1;
                e = b + boltWidth;

                bolti++;
            }

            if (isReversed)
            {
                //反向
                bolts.Reverse();
            }
        }


        /// <summary>
        /// 创建 分区表， 平均分区, 螺丝从1开始排
        /// </summary>
        void CreateAvgBolts()
        {
            double mmpp = Mmpp;
            int mid = (int)(MidMm / mmpp);
            double boltWidth = BoltWidth / mmpp * (1 - UniformShrinkPercent);

            //剩下有限的螺丝数量为偶数
            bool isboltCntEven = ((BoltCnt - RightEmptyBolts - LeftEmptyBolts) % 2 == 0);

            //当 isboltCntEven = true,  boltIndexOfMid 为 中间螺丝序号较大的那个
            //当 isboltCntEven = false,  boltIndexOfMid 为 中间螺丝序号
            int boltIndexOfMid = ((BoltCnt - RightEmptyBolts) + LeftEmptyBolts) / 2;

            create_bolts(mid, boltWidth, BoltCnt, boltIndexOfMid, isboltCntEven, IsReversed, out List<RangeStruct> bolts);

            NBolts = BoltCnt;
            FirstBoltNo = 1;

            this.bolts.Clear();
            this.bolts.AddRange(bolts);

        }


        #region IBoltMapBase

        #region 根据边界调整分区
        RangeStruct[] boltRanges = new RangeStruct[2] { RangeStruct.InvalidValue, RangeStruct.InvalidValue };

        public void InitExChange()
        {
            helper.InitExChange();
            boltRanges[0] = RangeStruct.InvalidValue;
            boltRanges[1] = RangeStruct.InvalidValue;

        }
        public void CalExChange(Misc.DIRECTION direction, Range bolt_r)
        {
            int idx = (direction == Misc.DIRECTION.FORWARD) ? 0 : 1;
            this.boltRanges[idx] = bolt_r.ToStruct();

            if (!this.boltRanges[idx].IsValid)//方向无效都不计算
                return;

            //if (BorderType != BORDER_TYPE.Mid)
            //    return;

            //目标对齐位置
            int targetMidPos = (int)(MidMm / Mmpp);

            //原始位置
            int orgMidPos = this.boltRanges[idx].Mid;
            var exchange = helper.mExChange[idx];
            exchange.Init();
            exchange.Offset = orgMidPos - targetMidPos;
        }

        public int PositionExchange(Misc.DIRECTION direction, int position)
        {
            return helper.PositionExchange(direction, position);
        }
        public int BoltExchange(Misc.DIRECTION direction, int bolt_pos)
        {
            return helper.BoltExchange(direction, bolt_pos);
        }


        #endregion

        #region 用于动态生成分区数据

        public void InitBoltAD()
        {
            dyn.Init();
            dynValue.Init();
        }

        public bool UpdateBoltAD(int[] data, int start_pos, int posOfGrid, Misc.DIRECTION direction, out int start_boltIndex, out int[] boltAD)
        {
            return dyn.UpdateBoltAD(data, start_pos, posOfGrid, direction, out start_boltIndex, out boltAD);
        }
        public bool UpdateBoltValue(double[] data, int start_pos, int posOfGrid, Misc.DIRECTION direction, out int start_boltIndex, out double[] boltValue)
        {
            return dynValue.UpdateBoltValue(data, start_pos, posOfGrid, direction, out start_boltIndex, out boltValue);
        }
        #endregion
        BoltMapFilmCastingDebugData debugData;


        public int[] Map(Misc.DIRECTION direction, int posBegin, int posOfGrid, int[] dat)
        {
            return helper.Map(direction, posBegin, posOfGrid, dat);
        }

        public double[] Map(Misc.DIRECTION direction, int posBegin, int posOfGrid, double[] dat)
        {
            int idx = (direction == Misc.DIRECTION.FORWARD) ? 0 : 1;
            var debugData = new BoltMapFilmCastingDebugData()
            {
                posOfGrid = posOfGrid,
                posBegin = posBegin,
                buf = (double[])dat.Clone(),
                border = this.boltRanges[idx]
            };
            this.debugData = debugData;
            DebugUpdate = DateTime.Now;


            return helper.Map(direction, posBegin, posOfGrid, dat);
        }

        public void GetBolts(out int boltno1st, out IEnumerable<Range> bolts)
        {
            boltno1st = FirstBoltNo;
            bolts = this.bolts.Select(b => new Range(b));
        }

        public void GetScanRange(Range boltno, out int posBegin, out int posEnd)
        {
            int boltindex_b = boltno.Begin - FirstBoltNo;
            if (boltindex_b < 0)
                boltindex_b = 0;
            else if (boltindex_b >= NBolts)
                boltindex_b = NBolts - 1;

            int boltindex_e = boltno.End - FirstBoltNo;
            if (boltindex_e < 0)
                boltindex_e = 0;
            else if (boltindex_e >= NBolts)
                boltindex_e = NBolts - 1;


            if (!IsReversed)
            {
                posBegin = bolts[boltindex_b].Begin;
                posEnd = bolts[boltindex_e].End;
            }
            else
            {
                posBegin = bolts[boltindex_e].Begin;
                posEnd = bolts[boltindex_b].End;
            }

            if (posBegin < 0)
                posBegin = 0;
            else if (posBegin >= PosLength)
                posBegin = PosLength - 1;

            if (posEnd < 0)
                posEnd = 0;
            else if (posEnd >= PosLength)
                posEnd = PosLength - 1;
        }

        public int GetBoltIndex_fromPos(int pos)
        {
            throw new Exception("不支持 GetBoltIndex_fromPos");
        }
        #endregion



        /// <summary>
        /// 应用 &amp; 保存
        /// </summary>
        public void Apply()
        {
            CreateAvgBolts();

            ActiveEvent?.Invoke(this);
            Save();
        }



        #region INotifyPropertyChanged 成员
        public event PropertyChangedEventHandler PropertyChanged;

        #endregion



        public bool Load()
        {
            return BoltMapFilmCastingJsonDb.Load(this, file_path);
        }

        bool Save()
        {
            return BoltMapFilmCastingJsonDb.Save(this, file_path);

        }

        public void GetDebugData(AsyncCBHandler asyncDelegate, object asyncContext)
        {
            asyncDelegate?.Invoke(asyncContext, debugData);
        }
    }

    /// <summary>
    /// 分区间，数据没有重叠
    /// </summary>
    public class DynBoltAD
    {
        #region 动态扫描时，提前算分区的AD值，正常时 通过 grid数据划分的
        private int[] boltADSum;
        private int[] boltADCnt;
        #endregion
        IBoltMapBase boltmap;
        public DynBoltAD(IBoltMapBase boltmap)
        {
            this.boltmap = boltmap;
        }

        public void Init()
        {
            if (boltADSum == null || boltADSum.Count() != boltmap.NBolts)
            {
                boltADSum = new int[boltmap.NBolts];
                boltADCnt = new int[boltmap.NBolts];
            }

            for (int i = 0; i < boltmap.NBolts; i++)
            {
                boltADSum[i] = 0;
                boltADCnt[i] = 0;
            }
        }

        public bool UpdateBoltAD(int[] data, int start_pos, int posOfGrid, Misc.DIRECTION direction, out int start_boltIndex, out int[] boltAD)
        {

            boltAD = null;
            start_boltIndex = -1;
            int start_grid = start_pos / posOfGrid;
            int bIndex = -1;
            int eIndex = -1;
            for (int i = 0; i < data.Length; i++)
            {
                int position = (start_grid + i) * posOfGrid;
                position = boltmap.PositionExchange(direction, position);
                int boltIndex = boltmap.GetBoltIndex_fromPos(position);
                if (boltIndex < 0 || boltIndex >= boltmap.NBolts)
                {
                    continue;
                }

                int ad = data[i];
                if (Misc.MyBase.ISVALIDATA(ad))
                {
                    boltADSum[boltIndex] += ad;
                    boltADCnt[boltIndex]++;
                }

                if (bIndex == -1)
                {
                    bIndex = boltIndex;
                    eIndex = boltIndex;
                }
                else if (boltIndex < bIndex)
                {
                    bIndex = boltIndex;
                }
                else if (boltIndex > eIndex)
                {
                    eIndex = boltIndex;
                }
            }
            if (bIndex == -1)
                return false;

            int count = eIndex - bIndex + 1;
            boltAD = new int[count];
            for (int i = 0; i < count; i++)
            {
                int idx = bIndex + i;
                if (boltADCnt[idx] > 0)
                    boltAD[i] = boltADSum[idx] / boltADCnt[idx];
                else
                    boltAD[i] = Misc.MyBase.NULL_VALUE;
            }
            start_boltIndex = bIndex;
            return true;
        }
    }

    /// <summary>
    /// 分区间，数据没有重叠
    /// </summary>
    public class DynBoltValue
    {
        #region 动态扫描时，提前算分区的AD值，正常时 通过 grid数据划分的
        private double[] boltValueSum;
        private int[] boltValueCnt;
        #endregion
        IBoltMapBase boltmap;
        public DynBoltValue(IBoltMapBase boltmap)
        {
            this.boltmap = boltmap;
        }

        public void Init()
        {
            if (boltValueSum == null || boltValueSum.Count() != boltmap.NBolts)
            {
                boltValueSum = new double[boltmap.NBolts];
                boltValueCnt = new int[boltmap.NBolts];
            }

            for (int i = 0; i < boltmap.NBolts; i++)
            {
                boltValueSum[i] = 0;
                boltValueCnt[i] = 0;
            }
        }

        public bool UpdateBoltValue(double[] data, int start_pos, int posOfGrid, Misc.DIRECTION direction, out int start_boltIndex, out double[] boltValues)
        {

            boltValues = null;
            start_boltIndex = -1;
            int start_grid = start_pos / posOfGrid;
            int bIndex = -1;
            int eIndex = -1;
            for (int i = 0; i < data.Length; i++)
            {
                int position = (start_grid + i) * posOfGrid;
                position = boltmap.PositionExchange(direction, position);
                int boltIndex = boltmap.GetBoltIndex_fromPos(position);
                if (boltIndex < 0 || boltIndex >= boltmap.NBolts)
                {
                    continue;
                }

                double value = data[i];
                if (!double.IsNaN(value))
                {
                    boltValueSum[boltIndex] += value;
                    boltValueCnt[boltIndex]++;
                }

                if (bIndex == -1)
                {
                    bIndex = boltIndex;
                    eIndex = boltIndex;
                }
                else if (boltIndex < bIndex)
                {
                    bIndex = boltIndex;
                }
                else if (boltIndex > eIndex)
                {
                    eIndex = boltIndex;
                }
            }
            if (bIndex == -1)
                return false;

            int count = eIndex - bIndex + 1;
            boltValues = new double[count];
            for (int i = 0; i < count; i++)
            {
                int idx = bIndex + i;
                if (boltValueCnt[idx] > 0)
                    boltValues[i] = boltValueSum[idx] / boltValueCnt[idx];
                else
                    boltValues[i] = double.NaN;
            }
            start_boltIndex = bIndex;
            return true;
        }
    }

    public class BoltMapHelper
    {
        IBoltMapBase boltmap;
        DynBoltAD dyn;
        DynBoltValue dynValue;
        public BoltMapHelper(IBoltMapBase boltmap)
        {
            this.boltmap = boltmap;
            dyn = new DynBoltAD(boltmap);
            dynValue = new DynBoltValue(boltmap);
        }

        public int[] Map(Misc.DIRECTION direction, int posBegin, int posOfGrid, int[] dat)
        {
            dyn.Init();
            int[] frame = new int[boltmap.NBolts];
            for (int i = 0; i < frame.Count(); i++)
            {
                frame[i] = Misc.MyBase.NULL_VALUE;
            }
            if (dyn.UpdateBoltAD(dat, posBegin, posOfGrid, direction, out int start_boltIndex, out int[] boltAD))
            {
                for (int i = 0; i < boltAD.Count(); i++)
                {
                    frame[start_boltIndex + i] = boltAD[i];
                }
                //数据平滑
                int smooth = boltmap.Smooth;
                if (smooth < 0)
                    smooth = 0;
                else if (smooth > 10)
                    smooth = 10;

                if (smooth > 0)
                {
                    int[] frame2 = new int[frame.Count()];
                    for (int i = 0; i < frame.Count(); i++)
                    {
                        int sum = 0;
                        int cnt = 0;
                        int index_b = i - smooth;
                        int index_e = i + smooth;
                        for (int j = index_b; j <= index_e; j++)
                        {
                            if ((j >= 0) && (j < frame.Count()))
                            {
                                if (Misc.MyBase.ISVALIDATA(frame[j]))
                                {
                                    sum += frame[j];
                                    cnt++;
                                }
                            }
                        }
                        if (cnt > 0)
                            frame2[i] = sum / cnt;
                        else
                            frame2[i] = Misc.MyBase.NULL_VALUE;
                    }

                    frame = frame2;
                }
            }
            return frame;
        }

        public double[] Map(Misc.DIRECTION direction, int posBegin, int posOfGrid, double[] dat)
        {
            dynValue.Init();
            double[] frame = new double[boltmap.NBolts];
            for (int i = 0; i < frame.Count(); i++)
            {
                frame[i] = double.NaN;
            }
            if (dynValue.UpdateBoltValue(dat, posBegin, posOfGrid, direction, out int start_boltIndex, out double[] boltValues))
            {
                for (int i = 0; i < boltValues.Count(); i++)
                {
                    frame[start_boltIndex + i] = boltValues[i];
                }

                //数据平滑
                int smooth = boltmap.Smooth;
                if (smooth < 0)
                    smooth = 0;
                else if (smooth > 10)
                    smooth = 10;

                if (smooth > 0)
                {
                    double[] frame2 = new double[frame.Count()];
                    for (int i = 0; i < frame.Count(); i++)
                    {
                        double sum = 0;
                        int cnt = 0;
                        int index_b = i - smooth;
                        int index_e = i + smooth;
                        for (int j = index_b; j <= index_e; j++)
                        {
                            if ((j >= 0) && (j < frame.Count()))
                            {
                                if (!double.IsNaN(frame[j]))
                                {
                                    sum += frame[j];
                                    cnt++;
                                }
                            }
                        }
                        if (cnt > 0)
                            frame2[i] = sum / cnt;
                        else
                            frame2[i] = double.NaN;
                    }

                    frame = frame2;
                }
            }
            return frame;
        }


        #region 根据边界调整分区
        public class ExChange
        {
            /// <summary>
            /// 平移
            /// </summary>
            public int Offset { get; set; }
            /// <summary>
            /// 比例
            /// </summary>
            public double Amp { get; set; } = 1;

            /// <summary>
            /// 初始化
            /// </summary>
            public void Init()
            {
                Offset = 0;
                Amp = 1;
            }
        }
        public ExChange[] mExChange = new ExChange[2] { new ExChange(), new ExChange() };
        public void InitExChange()
        {
            for (int i = 0; i < 2; i++)
            {
                ExChange exchange = mExChange[i];
                exchange.Init();
            }
        }
        public int PositionExchange(Misc.DIRECTION direction, int position)
        {
            int idx = (direction == Misc.DIRECTION.FORWARD) ? 0 : 1;
            ExChange exchange = mExChange[idx];
            return (int)((position - exchange.Offset) / exchange.Amp);
        }
        public int BoltExchange(Misc.DIRECTION direction, int bolt_pos)
        {
            int idx = (direction == Misc.DIRECTION.FORWARD) ? 0 : 1;
            ExChange exchange = mExChange[idx];
            return (int)(bolt_pos * exchange.Amp) + exchange.Offset;
        }
        #endregion
    }



    public class BoltMapFilmCastingJsonDb
    {
        static AutoMapper.Mapper Mapper = new AutoMapper.Mapper(new AutoMapper.MapperConfiguration(c =>
        {
            c.CreateMap<BoltMapFilmCasting, BoltMapFilmCastingJsonDb>().ReverseMap();
        }));


        public static bool Load(BoltMapFilmCasting src, string filePath)
        {
            try
            {
                if (File.Exists(filePath))
                {
                    string json = File.ReadAllText(filePath);
                    var p = JsonConvert.DeserializeObject<BoltMapFilmCastingJsonDb>(json);

                    Mapper.Map(p, src);
                    return true;
                }
            }
            catch
            {
                //异常，没有json 解码失败
            }
            return false;

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

            }
            return false;
        }


        #region 基本设定

        /// <summary>
        /// 螺丝宽度 mm
        /// </summary>
        public double BoltWidth { get; set; } = 25.4;

        /// <summary>
        /// 总螺丝数 
        /// </summary>
        public int BoltCnt { get; set; } = 97;

        /// <summary>
        /// 膜中心位置 mm
        /// </summary>
        public int MidMm { get; set; } = 1000;

        /// <summary>
        /// 与机架方向相反
        /// </summary>
        public bool IsReversed { get; set; }

        /// <summary>
        /// 平滑，以前后分区平均滤波 ，=0，只有自己，1，左右各一个
        /// </summary>
        public int Smooth { get; set; }

        public BORDER_TYPE BorderType { get; set; } = BORDER_TYPE.Mid;
        #endregion


    }
}
