using AutoMapper;
using CommunityToolkit.Mvvm.Input;
using Flyad7_WPF.Converters;
using Flyad7_WPF.Secute;
using FlyADBase;
using FObjBase;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using System.Windows.Threading;
using Unity;

namespace Flyad7_WPF
{
    /// <summary>
    /// WdTest.xaml 的交互逻辑
    /// </summary>
    public partial class WdTest : Window
    {
        WdTestVm viewModel;
        public WdTest()
        {
            InitializeComponent();
        }

        [InjectionMethod]
        public void Init(FlyAD7 flyad, FlyADClientUI flyADClientUI)
        {

            viewModel = new WdTestVm();
            viewModel.Init(flyad, flyADClientUI);
            this.DataContext = viewModel;
        }
    }
    public class WdTestVm : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        public const int AD_R = 13500;
        public const int AD_R_range = 500;
        /// <summary>
        /// [1000~62999]检测 精度。  10个数值内，出现一个就算合格
        /// </summary>
        public const int AD_NUMBER_MIN_ACCURACY = 50;
        /// <summary>
        /// [1000~62999] 最大值
        /// </summary>
        public const int AD_NUMBER_MAX_VALUE = 62999;

        /// <summary>
        /// [1000~62999] 最小值
        /// </summary>
        public const int AD_NUMBER_MIN_VALUE = 1000;
        public bool IsConnected_OK { get; set; }
        public bool IsInitArea_OK { get; set; }
        public bool IsOutput_OK { get; set; }
        public bool IsInput_OK { get; set; }
        public bool IsEncoder2_OK { get; set; }
        public bool IsEncoder1_OK { get; set; }

        public bool IsAdK_OK { get; set; }

        public bool IsAd_OK { get; set; }

        public bool IsAll_OK { get; set; }
        /// <summary>
        /// 使用码
        /// </summary>
        [PropertyChanged.DoNotCheckEquality]
        public byte[] Access { get; set; } = new byte[8];

        /// <summary>
        /// 序列码
        /// </summary>
        [PropertyChanged.DoNotCheckEquality]
        public byte[] Code { get; set; } = new byte[7];

        public int Hours { get; set; } = 8000;

        public string InitAreaMessage { get; set; }

        public bool IsConnected { get; private set; }
        public bool IsSync { get; private set; }
        public int SuccessCnt { get; private set; }
        public bool IsEnableReConnect => !IsConnected;

        /// <summary>
        /// 已经初始化完的AD盒
        /// </summary>
        public ObservableCollection<string> Codes { get; } = new ObservableCollection<string>();

        /// <summary>
        /// 输入口 每个位检查情况
        /// </summary>
        public UInt16 IStatusOk { get; set; }

        /// <summary>
        /// 每1000个数值分组，看AD数值出现率
        /// </summary>
        public ObservableCollection<AdNumberArray> AdNumberArrays { get; } = new ObservableCollection<AdNumberArray>();

        public int AdNumberCount { get; private set; }
        public int AdNumberSuccessCount { get; private set; } 


        public UInt16 IStatus { get; set; }
        public UInt16 OStatus { get; set; }
        public int Position { get; set; }
        public int Position2 { get; set; }

        public int AD { get; set; }

        public int HardVersion { get; set; } = 2;
        public RelayCommand ReConnectCmd { get; private set; }
        public RelayCommand InitAreaCmd { get; private set; }
        public RelayCommand SetInitAreaOkCmd { get; private set; }
        public RelayCommand SetOutputOKCmd { get; private set; }

        public RelayCommand ClearFlyAdStatesCmd { get; private set; }

        FlyAD7 flyad;
        FlyADClientUI flyADClientUI;
        CreateAccessHelper createA;
        DispatcherTimer timer;

        int adk_cnt = 0;
        int? lastPosition;
        int? lastPosition2;
        UInt16? lastInput;

        /// <summary>
        /// 走马灯输出点序号
        /// </summary>
        int outIdx;


        bool hasRegistTimeGrid;
        string dbpath = "testad.json";
        public WdTestVm()
        {

            ReConnectCmd = new RelayCommand(ReConnect);
            InitAreaCmd = new RelayCommand(InitArea);
            SetInitAreaOkCmd = new RelayCommand(SetInitAreaOk);
            SetOutputOKCmd = new RelayCommand(SetOutputOK);
            ClearFlyAdStatesCmd = new RelayCommand(ClearFlyAdStates);

            createA = new CreateAccessHelper();

            timer = new DispatcherTimer();
            timer.Interval = TimeSpan.FromSeconds(1);
            timer.Tick += Timer_Tick;
            

            initCheckAdNumber();
        }



        public void Init(FlyAD7 flyad, FlyADClientUI flyADClientUI)
        {
            this.flyad = flyad;
            this.flyADClientUI = flyADClientUI;
            WdTestJsonDb.Load(dbpath, this);
            SuccessCnt = Codes.Count();

            Misc.BindingOperations.SetBinding(flyad, nameof(flyad.IsConnected), this, nameof(IsConnected));
            Misc.BindingOperations.SetBinding(flyad, nameof(flyad.Code), this, () => {
                Code = flyad.Code.ToArray();

                var code = bytes2stirng(Code);
                if (Codes.Any(c => c == code)) { 
                    
                    setAllOk();
                }
            });
            Misc.BindingOperations.SetBinding(flyad, nameof(flyad.Access), this, () => {
                Access = flyad.Access.ToArray();
            });
            
            Misc.BindingOperations.SetBinding(flyad, nameof(flyad.Surplus), this, nameof(Hours));

            Misc.BindingOperations.SetBinding(flyad, nameof(flyad.Surplus), this, () => {
                if (flyad.Surplus > 7000)
                {
                    IsInitArea_OK = true;
                }
                else {
                    IsInitArea_OK = false;
                }
            });

            Misc.BindingOperations.SetBinding(flyad, nameof(flyad.IsConnected), this, ()=>{
                updateIsConnected();
            });
            
            Misc.BindingOperations.SetBinding(flyad, nameof(flyad.IStatus), this, nameof(IStatus));
            Misc.BindingOperations.SetBinding(flyad, nameof(flyad.OStatus), this, nameof(OStatus));
            Misc.BindingOperations.SetBinding(flyad, nameof(flyad.Position), this, nameof(Position));
            Misc.BindingOperations.SetBinding(flyad, nameof(flyad.Position2), this, nameof(Position2));
            Misc.BindingOperations.SetBinding(flyad, nameof(flyad.AD), this, nameof(AD));


            

            timer.Start();
        }


        private void Timer_Tick(object sender, EventArgs e)
        {
            if (!flyad.IsReady)
                return;

            if (IsAll_OK)
                return;

            checkEncoder1();
            checkEncoder2();
            checkInput();
            checkOutput();
            checkAdK();
            checkAdNumber();

            bool[] ok_array = new bool[] {
                    IsConnected_OK,
                    IsInitArea_OK,
                    IsOutput_OK,
                    IsInput_OK,
                    IsEncoder2_OK,
                    IsEncoder1_OK,
                    IsAdK_OK,
                    IsAd_OK
                };
            IsAll_OK = ok_array.All(ok => ok);

            if (IsAll_OK)
            {

                string code = bytes2stirng(Code);
                Codes.Add(code);

                WdTestJsonDb.Save(dbpath, this);

                SuccessCnt = Codes.Count();

            }

        }

        void checkAdK()
        {
            if (IsAdK_OK)
                return;

            //AD值 在调整的范围内
            if (Math.Abs(flyad.AD - AD_R) <= AD_R_range)
            {
                adk_cnt++;
                if (adk_cnt > 2) 
                {
                    //持续了2秒，完成
                    IsAdK_OK = true;
                }
            }
            else {
                //不在范围, 复位计时
                adk_cnt = 0;
            }
        }
        void resetCheckAdK() {

            IsAdK_OK = false;
            adk_cnt = 0;

        }
        void checkEncoder1() 
        {
            if (IsEncoder1_OK)
                return;


            if (lastPosition == null)
                lastPosition = flyad.Position;
            else {
                if (Math.Abs(flyad.Position - (int)lastPosition) > 100)
                {
                    IsEncoder1_OK = true;
                }
            }
        }

        void resetCheckEncoder1()
        {
            IsEncoder1_OK = false;
            lastPosition = null;
        }
        void checkEncoder2() 
        {
            if (IsEncoder2_OK)
                return;

            if (lastPosition2 == null)
                lastPosition2 = flyad.Position2;
            else
            {
                if (Math.Abs(flyad.Position2 - (int)lastPosition2) > 100)
                {
                    IsEncoder2_OK = true;
                }
            }
        }

        void resetCheckEncoder2()
        {
            IsEncoder2_OK = false;
            lastPosition2 = null;
        }
        void checkInput() 
        {
            if (IsInput_OK)
                return;

            //每位都必须变一次
            if (lastInput == null)
            {
                lastInput = flyad.IStatus;
                IStatusOk = 0;
            }
            else 
            { 
                if (flyad.IStatus != lastInput)
                {
                    UInt16 ret = (UInt16)(flyad.IStatus ^ (UInt16)lastInput);
                    IStatusOk |= ret;

                    if (HardVersion == 3)
                    {
                        if (IStatusOk == 0xffff)
                        {
                            IsInput_OK = true;
                        }
                    }
                    else
                    {
                        if (IStatusOk == 0xfff)
                        {
                            IsInput_OK = true;
                        }
                    }
                }
            }
        }
        void resetCheckInput()
        {
            IsInput_OK = false;
            lastInput = null;
            IStatusOk = 0;
        }
        void checkOutput()
        {
            if (IsOutput_OK)
                return;

            //启动走马灯
            if (outIdx >= 0)
            {
                //灭之前的灯
                flyad.SetOutputBit(outIdx, true);

                outIdx += 1;
                if (HardVersion == 3)
                {
                    if (outIdx > 7)
                        outIdx = 0;
                }
                else
                {
                    if (outIdx > 3)
                        outIdx = 0;
                }
            }
            else { 
                outIdx = 0;
            }
            
            //亮灯
            flyad.SetOutputBit(outIdx, false);
        }
        void resetCheckOutput() 
        {
            IsOutput_OK  = false;
            outIdx = -1;
        }



        void checkAdNumber() 
        {
            if (IsAd_OK)
                return;

            //1秒更新一次
            if(!hasRegistTimeGrid)
                flyad.TimeGridEvent += Flyad_TimeGridEvent;


            foreach (var adn in AdNumberArrays) {
                if (adn.SuccessCount < adn.NumberCount)
                {
                    if (adn.HasChanged)
                    {
                        adn.HasChanged = false;
                        adn.SuccessCount = adn.Numbers.Count(n => n);
                    }
                }
            }
            AdNumberSuccessCount = AdNumberArrays.Sum(n => (n.StartNumber == 0 || n.StartNumber>=65000)?0:n.SuccessCount);
            IsAd_OK = AdNumberArrays.All(n => (n.StartNumber == 0 || n.StartNumber >= 65000) ? true : n.IsSuccess);

            if (IsAd_OK)
            {
                flyad.TimeGridEvent -= Flyad_TimeGridEvent;
                hasRegistTimeGrid = false;
            }
        
        }

        private void Flyad_TimeGridEvent(object sender, TimeGridEventArgs e)
        {
            var ads = e.Data;

            if (IsAd_OK)
                return;

            for (int i = 0; i < ads.Count(); i++)
            {

                int ad = ads[i];
                if (Misc.MyBase.ISVALIDATA(ad))
                {
                    if (ad >= 0 && ad <= 65520)
                    {
                        //找到对应的组
                        int group_index = ad / 1000;

                        var adNumberArray = AdNumberArrays[group_index];

                        int number_index = (ad - adNumberArray.StartNumber) / AD_NUMBER_MIN_ACCURACY;

                        if (number_index < adNumberArray.NumberCount)
                        {
                            bool ret = AdNumberArrays[group_index].Numbers[number_index];
                            if (!ret)
                            {
                                AdNumberArrays[group_index].Numbers[number_index] = true;
                                AdNumberArrays[group_index].HasChanged = true;
                            }
                        }
                    }
                }
            }
        }

        void resetCheckAdNumber() {
            IsAd_OK = false;
            AdNumberSuccessCount = 0;
            
            
            foreach (var adn in AdNumberArrays) {
                adn.SuccessCount = 0;
                adn.Numbers = new bool[adn.NumberCount];
                adn.HasChanged = false;
            }
            if (hasRegistTimeGrid)
            {
                flyad.TimeGridEvent -= Flyad_TimeGridEvent;
                hasRegistTimeGrid = false;
            }
        }
        void initCheckAdNumber() 
        {
            
            for (int i = 0; i <= 65520 / 1000; i++) {

                int numberCount = 1000 / AD_NUMBER_MIN_ACCURACY;
                int startNumber = i * 1000;

                if ((startNumber + 1000 - 1) >= 65520)
                {
                    numberCount = (65520 - startNumber + 1) / AD_NUMBER_MIN_ACCURACY;
                }


                var adn = new AdNumberArray
                {
                    StartNumber = startNumber,
                    NumberCount = numberCount,
                    Numbers = new bool[numberCount],
                    HasChanged = false,
                    SuccessCount = 0
                };

                AdNumberArrays.Add(adn);
            }
            //第1组，最后一组，都不要
            AdNumberCount = AdNumberArrays.Sum(n=>(n.StartNumber==0 || n.StartNumber>=65000)?0:n.NumberCount);
        }


        void setAllOk() 
        {
            IsAll_OK = true;
            IsInitArea_OK = true;
            IsAdK_OK = true;
            IsEncoder1_OK = true;
            IsEncoder2_OK = true;
            IsInput_OK = true;
            IsOutput_OK = true;
            IsAd_OK = true;
            if (hasRegistTimeGrid)
            {
                flyad.TimeGridEvent -= Flyad_TimeGridEvent;
                hasRegistTimeGrid = false;
            }
        }
        private void SetOutputOK()
        {
            IsOutput_OK = true;
            if(outIdx>=0)
                flyad.SetOutputBit(outIdx, true);
        }

        private void ClearFlyAdStates()
        {
            if (MessageBox.Show("是否清空全部？", "提示", MessageBoxButton.YesNo, MessageBoxImage.Question) != MessageBoxResult.Yes) 
            {
                return;
            }

            Codes.Clear();
            SuccessCnt = 0;
            WdTestJsonDb.Save(dbpath, this);
            
        }
        void updateIsConnected() 
        {
            if (flyad.IsConnected)
            {
                IsConnected_OK = true;
            }
            else
            {
                IsConnected_OK = false;

                IsInitArea_OK = false;
                IsAll_OK = false;
                resetCheckAdK();
                resetCheckAdNumber();
                resetCheckEncoder1();
                resetCheckEncoder2();
                resetCheckInput();
                resetCheckOutput();
            }
        }

        private void ReConnect()
        {
            flyad.ReConnect();
        }

        private void InitArea()
        {
            InitAreaMessage = $"正在初始化。。。。。。。。。。。。。。。。。。。。。。。";
            initCode_step01();
        }

        void initCode_step01()
        {
            flyADClientUI.InitArea(new AsyncCBHandler(
            delegate (object AsyncState, object retData)
            {
                AccessInfo a = (AccessInfo)retData;
                Access = a.access;
                Code = a.code;
                switch (a.ret)
                {
                    case AREA_ERR.NO_ERR:
                        {
                            InitAreaMessage = "初始化成功";
                            //MessageBox.Show(Message);

                            initCode_step02();
                        }
                        break;
                    case AREA_ERR.DUP_ACCESS:
                        {
                            InitAreaMessage = "设置失败！！！！！重复使用码";
                            MessageBox.Show(InitAreaMessage, "设置失败", MessageBoxButton.OK, MessageBoxImage.Error);
                        }
                        break;
                    case AREA_ERR.ERR_ACCESS:
                        {
                            InitAreaMessage = "设置失败！！！！！无效使用码";
                            MessageBox.Show(InitAreaMessage, "设置失败", MessageBoxButton.OK, MessageBoxImage.Error);
                        }
                        break;
                }
            }), null);
        }
        void initCode_step02()
        {
            bool ret = createA.CreateAccess(Code, 8000, out byte[] access, out string message);
            if (ret)
            {
                Access = access;

                InitAreaMessage = "生成授权码 成功";
                //MessageBox.Show(Message);

                initCode_step03();
            }
            else
            {
                InitAreaMessage = $"生成授权码失败 {message}";
                MessageBox.Show(InitAreaMessage, "异常", MessageBoxButton.OK, MessageBoxImage.Error);
            }
        }

        void initCode_step03()
        {
            bool ret = false;
            if (Code != null && Code.Count() == flyad.Code.Count())
            {
                for (int i = 0; i < Code.Length; i++)
                {
                    if (flyad.Code[i] != Code[i])
                    {
                        break;
                    }
                }
                ret = true;
            }


            if (!ret)
            {

                InitAreaMessage = "异常!!!序列码与AD盒不相同";
                MessageBox.Show(InitAreaMessage, "异常", MessageBoxButton.OK, MessageBoxImage.Error);
                return;
            }



            ret = createA.CheckAccess(Code, Access, out int hours, out string message);
            //Hours = hours;

            if (!ret)
            {

                InitAreaMessage = $"授权码异常!!!{message}";
                MessageBox.Show(message, "授权码异常", MessageBoxButton.OK, MessageBoxImage.Error);
                return;
            }

            InitAreaMessage = $"正在写入授权码中。。。。。。。。。。。。。。。。。。。。。。。";
            flyADClientUI.SetAccess(
                Access,
                new AsyncCBHandler(
                    delegate (object AsyncState, object retData)
                    {
                        AccessInfo a = (AccessInfo)retData;
                        //Access = a.access;
                        Code = a.code;
                        switch (a.ret)
                        {
                            case AREA_ERR.NO_ERR:
                                {
                                    InitAreaMessage = "写入授权码成功";
                                    MessageBox.Show(InitAreaMessage);
                                    IsInitArea_OK = true;
                                }
                                break;
                            case AREA_ERR.DUP_ACCESS:
                                {
                                    InitAreaMessage = "设置失败！！！！！重复使用码";
                                    MessageBox.Show(InitAreaMessage, "设置失败", MessageBoxButton.OK, MessageBoxImage.Error);
                                }
                                break;
                            case AREA_ERR.ERR_ACCESS:
                                {
                                    InitAreaMessage = "设置失败！！！！！无效使用码";
                                    MessageBox.Show(InitAreaMessage, "设置失败", MessageBoxButton.OK, MessageBoxImage.Error);
                                }
                                break;
                        }
                    }), null);


        }
        string bytes2stirng(byte[] bytes) { 
            return (string)new BytesConverter().Convert(bytes, typeof(byte[]), null, null);
        }
        private void SetInitAreaOk()
        {
            if (!flyad.IsReady)
                return;

            IsInitArea_OK = true;
        }


    }



    /// <summary>
    /// 每1000个数值作为一组，检查哪个数值没有出现
    /// </summary>
    public class AdNumberArray : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        /// <summary>
        /// 这个组开始的数值
        /// </summary>
        public int StartNumber { get; set; }

        /// <summary>
        /// 这个组数值总数 1000个
        /// </summary>
        public int NumberCount { get; set; }

        /// <summary>
        /// 成功出现的数值总数
        /// </summary>
        public int SuccessCount { get; set; }

        public bool HasChanged;

        public bool[] Numbers;

        public bool IsSuccess => Math.Abs(SuccessCount - NumberCount) <= 1;
    }

    public class WdTestJsonDb 
    {
        public string[] Codes;

        public static bool Save(string jsonDbPath, WdTestVm src)
        {

            if (string.IsNullOrEmpty(jsonDbPath))
                return false;

            WdTestJsonDb jsonDb = new WdTestJsonDb()
            {
                Codes = src.Codes.ToArray()
            };


            try
            {
                File.WriteAllText(jsonDbPath, JsonConvert.SerializeObject(jsonDb));
                return true;
            }
            catch
            {
                //异常，没有json 编码失败
                return false;
            }
        }
        public static bool Load(string jsonDbPath, WdTestVm src)
        {
            if (string.IsNullOrEmpty(jsonDbPath))
                return false;

            try
            {
                if (File.Exists(jsonDbPath))
                {
                    string json = File.ReadAllText(jsonDbPath);

                    var jsonDb = JsonConvert.DeserializeObject<WdTestJsonDb>(json);


                    src.Codes.Clear();
                    if (jsonDb.Codes != null)
                    {
                        for (int i = 0; i < jsonDb.Codes.Count(); i++)
                        {
                            src.Codes.Add(jsonDb.Codes[i]);
                        }
                    }


                    return true;
                }
            }
            catch
            {
                //异常，没有json 解码失败
            }
            return false;
        }
    }
}
