using Aitex.Core.RT.Log;
using Aitex.Core.Util;
using MECF.Framework.Common.DataCenter;
using OpenSEMI.Ctrlib.Controls;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;

namespace MECF.Framework.UI.Client.ClientBase
{
    public class UiViewModelBase : BaseModel
    {
        #region Variables
        private int UnknowKeysCount = 0;
        private readonly PeriodicJob _timer;
        protected Func<object, bool> _isSubscriptionAttribute;
        protected Func<MemberInfo, bool> _hasSubscriptionAttribute;
        protected ConcurrentBag<string> _subscribedKeys = new ConcurrentBag<string>();

        #endregion

        #region Constructors

        public UiViewModelBase()
        {
            _timer = new PeriodicJob(1000, OnTimer, "UIUpdateThread-" + GetType().Name);

            _isSubscriptionAttribute = attribute => attribute is SubscriptionAttribute;
            _hasSubscriptionAttribute = mi => mi.GetCustomAttributes(false).Any(_isSubscriptionAttribute);
        }

        #endregion

        #region Properties

        public string SystemName { get; set; }

        public bool ActiveUpdateData { get; set; }

        #endregion

        #region Methods

        protected override void OnActivate()
        {
            base.OnActivate();

            EnableTimer(true);
        }

        protected override void OnDeactivate(bool close)
        {
            base.OnDeactivate(close);

            if(ActiveUpdateData)
            {
                EnableTimer(false);
            }
        }

        public virtual void EnableTimer(bool enable)
        {
            if (enable)
            {
                _timer.Start();
            }
            else
            {
                _timer.Pause();
            }
        }

        protected override void OnInitialize()
        {
            base.OnInitialize();

            SubscribeKeys();
        }

        protected void Subscribe(string key)
        {
            if (!string.IsNullOrEmpty(key))
            {
                _subscribedKeys.Add(key);
            }
        }

        public virtual void SubscribeKeys()
        {
            SubscribeKeys(this);
        }

        public void SubscribeKeys(UiViewModelBase vm)
        {
            Parallel.ForEach(vm.GetType().GetProperties().Where(_hasSubscriptionAttribute),
                property =>
                {
                    SubscriptionAttribute subscription = property.GetCustomAttributes(false).First(_isSubscriptionAttribute) as SubscriptionAttribute;
                    string key = subscription.ModuleKey;
                    if (!string.IsNullOrEmpty(SystemName))
                    {
                        key = $"{SystemName}.{key}";
                        subscription.SetModule(SystemName);
                    }
                    if (!_subscribedKeys.Contains(key))
                    {
                        _subscribedKeys.Add(key);
                    }  
                });

            Parallel.ForEach(vm.GetType().GetFields().Where(_hasSubscriptionAttribute),
               field =>
               {
                   SubscriptionAttribute subscription = field.GetCustomAttributes(false).First(_isSubscriptionAttribute) as SubscriptionAttribute;

                   string key = subscription.ModuleKey;
                   if (!string.IsNullOrEmpty(SystemName))
                   {
                       key = $"{SystemName}.{key}";
                       subscription.SetModule(SystemName);
                   }
                   if (!_subscribedKeys.Contains(key))
                   {
                       _subscribedKeys.Add(key);
                   }
               });
        }

        protected virtual bool OnTimer()
        {
            try
            {
                Poll();

                return true;
            }
            catch (Exception)
            {
                return false;
            }
        }

        protected virtual void Poll()
        {
            int _unknowKeysCount = 0;
            if (_subscribedKeys.Count > 0)
            {
                Dictionary<string, object> data = QueryDataClient.Instance.Service.PollData(_subscribedKeys);

                if (data == null)
                {
                    LOG.Error("获取RT数据失败");
                    return;
                }

                if (data.Count != _subscribedKeys.Count)
                {
                    string unknowKeys = string.Empty;
                    foreach (string key in _subscribedKeys)
                    {
                        if (!data.ContainsKey(key))
                        {
                            unknowKeys += key + "\r\n";
                            _unknowKeysCount += 1;
                        }
                    }
                    if (UnknowKeysCount != _unknowKeysCount)
                    {
                        LOG.Error("获取如下SubscribeKey数据失败" + unknowKeys);
                        UnknowKeysCount = _unknowKeysCount;
                    }
                }

                InvokeBeforeUpdateProperty(data);

                UndateData(data);

                Application.Current?.Dispatcher.Invoke(new Action(() =>
                {
                    InvokePropertyChanged();

                    InvokeAfterUpdateProperty(data);
                }));
            }
        }

        protected virtual void InvokeBeforeUpdateProperty(Dictionary<string, object> data)
        {

        }

        protected virtual void InvokeAfterUpdateProperty(Dictionary<string, object> data)
        {

        }

        protected virtual void InvokePropertyChanged()
        {
            Refresh();
        }

        private void UndateData(Dictionary<string, object> data)
        {
            if (data != null)
            {
                UpdateSubscribe(data, this);
            }
        }

        public void UpdateSubscribe(Dictionary<string, object> data, object vm)
        {
            Parallel.ForEach(vm.GetType().GetProperties().Where(_hasSubscriptionAttribute),
               property =>
               {
                   PropertyInfo pi = (PropertyInfo)property;
                   SubscriptionAttribute subscription = property.GetCustomAttributes(false).First(_isSubscriptionAttribute) as SubscriptionAttribute;
                   string key = SystemName == null ? subscription.ModuleKey : $"{SystemName}.{subscription.ModuleKey}";

                   if (_subscribedKeys.Contains(key) && data.ContainsKey(key))
                   {
                       try
                       {
                           var convertedValue = Convert.ChangeType(data[key], pi.PropertyType);
                           var originValue = Convert.ChangeType(pi.GetValue(vm, null), pi.PropertyType);
                           if (originValue != convertedValue)
                           {
                               pi.SetValue(vm, convertedValue, null);
                           }
                       }
                       catch (Exception ex)
                       {
                           LOG.Error("由RT返回的数据更新失败" + key, ex);
                       }
                   }
               });

            Parallel.ForEach(vm.GetType().GetFields().Where(_hasSubscriptionAttribute),
             field =>
             {
                 FieldInfo fi = (FieldInfo)field;
                 SubscriptionAttribute subscription = field.GetCustomAttributes(false).First(_isSubscriptionAttribute) as SubscriptionAttribute;
                 string key = SystemName == null ? subscription.ModuleKey : $"{SystemName}.{subscription.ModuleKey}";

                 if (_subscribedKeys.Contains(key) && data.ContainsKey(key))
                 {
                     try
                     {
                         var convertedValue = Convert.ChangeType(data[key], fi.FieldType);
                         var originValue = Convert.ChangeType(fi.GetValue(vm), fi.FieldType);
                         if (originValue != convertedValue)
                         {
                             fi.SetValue(vm, convertedValue);
                         }
                     }
                     catch (Exception ex)
                     {
                         LOG.Error("由RT返回的数据更新失败" + key, ex);
                     }
                 }
             });
        }

        public virtual void OnWaferTransfer(DragDropEventArgs args)
        {
            try
            {
                WaferMoveManager.Instance.TransferWafer(args.TranferFrom, args.TranferTo);
            }
            catch (Exception ex)
            {
                LOG.Write(ex);
            }
        }

        public void OnMouseUp(object sender, MouseButtonEventArgs e)
        {
            if (e.ChangedButton != MouseButton.Right)
                return;

            if (sender is Slot slot)
            {
                ContextMenu cm = ContextMenuManager.Instance.GetSlotMenus(slot);
                if (cm != null)
                {
                    ((FrameworkElement)e.Source).ContextMenu = cm;
                }

                return;
            }

            if (sender is CarrierContentControl carrier)
            {
                ContextMenu cm = ContextMenuManager.Instance.GetCarrierMenus(carrier);
                if (cm != null)
                {
                    ((FrameworkElement)e.Source).ContextMenu = cm;
                }

                return;
            }
        }

        public string GetUnitStatusBackground(string status)
        {
            return ModuleStatusBackground.GetStatusBackground(status);
        }


        #endregion

    }
}
