using Aitex.Core.Util;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using System.Windows;
using Aitex.Core.RT.Log;
using Cali = Caliburn.Micro.Core;

namespace SicUI
{
    public class TimeredMainViewModel : Cali.Conductor<Cali.Screen>.Collection.OneActive
    {
        PeriodicJob _timer;
        ConcurrentBag<string> _subscribedKeys = new ConcurrentBag<string>();

        Func<object, bool> _isSubscriptionAttribute;
        Func<MemberInfo, bool> _hasSubscriptionAttribute;

        public TimeredMainViewModel()
        {
            _timer = new PeriodicJob(1000, this.OnTimer, "UIUpdaterThread - " + GetType().Name);

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

            SubscribeKeys(this);
        }

        [StructLayout(LayoutKind.Sequential)]
        internal struct LASTINPUTINFO
        {
            [MarshalAs(UnmanagedType.U4)]
            public int cbSize;
            [MarshalAs(UnmanagedType.U4)]
            public int dwTime;
        }

        [DllImport("user32.dll")]
        internal static extern bool GetLastInputInfo(ref LASTINPUTINFO plii);
        /// <summary>
        /// 获取鼠标键盘不活动的时间
        /// </summary>
        /// <returns>结果</returns>
        public static int GetLastInputTime()
        {
            LASTINPUTINFO lastInputInfo = new LASTINPUTINFO();
            lastInputInfo.cbSize = Marshal.SizeOf(lastInputInfo);
            lastInputInfo.dwTime = 0;

            int idleTime = 0;
            if (GetLastInputInfo(ref lastInputInfo))
            {
                idleTime = Environment.TickCount - lastInputInfo.dwTime;
            }

            return ((idleTime > 0) ? (idleTime / 1000) : 0);
        }

        protected virtual bool OnTimer()
        {
            try
            {
                Poll();
            }
            catch (Exception ex)
            {
                LOG.Error(ex.Message);
            }

            return true;
        }


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


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

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

                if (result.Count != _subscribedKeys.Count)
                {
                    string unknowKeys = string.Empty;
                    foreach (string key in _subscribedKeys)
                    {
                        if (!result.ContainsKey(key))
                        {
                            unknowKeys += key + "\r\n";
                        }
                    }
                    //System.Diagnostics.Debug.Assert(false, unknowKeys);
                }

                InvokeBeforeUpdateProperty(result);

                UpdateValue(result);

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

                    InvokeAfterUpdateProperty(result);
                }));


            }
        }

        private void InvokePropertyChanged()
        {
            Refresh();
        }

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

        }

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

        }

        void UpdateValue(Dictionary<string, object> data)
        {
            if (data == null)
                return;

            UpdateSubscribe(data, this);

            var properties = GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance).Where(p => p.GetCustomAttribute<SubscriptionModuleAttribute>() != null);
            foreach (var property in properties)
            {
                var moduleAttr = property.GetCustomAttribute<SubscriptionModuleAttribute>();
                UpdateSubscribe(data, property.GetValue(this), moduleAttr.Module);
            }
        }

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

        public void SubscribeKeys(TimeredMainViewModel target)
        {
            Parallel.ForEach(target.GetType().GetProperties().Where(_hasSubscriptionAttribute),
                property =>
                {
                    SubscriptionAttribute subscription = property.GetCustomAttributes(false).First(_isSubscriptionAttribute) as SubscriptionAttribute;
                    string key = subscription.ModuleKey;
                    if (!_subscribedKeys.Contains(key))
                        _subscribedKeys.Add(key);
                });

            Parallel.ForEach(target.GetType().GetFields().Where(_hasSubscriptionAttribute),
                method =>
                {
                    SubscriptionAttribute subscription = method.GetCustomAttributes(false).First(_isSubscriptionAttribute) as SubscriptionAttribute;
                    string key = subscription.ModuleKey;
                    if (!_subscribedKeys.Contains(key))
                        _subscribedKeys.Add(key);
                });
        }

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

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

                        }

                    }
                });

            Parallel.ForEach(target.GetType().GetFields().Where(_hasSubscriptionAttribute),
                property =>
                {
                    FieldInfo pi = (FieldInfo)property;
                    SubscriptionAttribute subscription = property.GetCustomAttributes(false).First(_isSubscriptionAttribute) as SubscriptionAttribute;
                    string key = subscription.ModuleKey;

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

                        }

                    }
                });
        }

    }
}
