using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace FObjBase.Reflect
{
    public abstract class Reflect_SeviceClient : FObjServiceClient
    {
        #region Core
        /// <summary>
        /// 忽略设置
        /// </summary>
        public bool ignoreSet;
        /// <summary>
        /// 代理对象 接口类型
        /// </summary>
        protected abstract Type InterfaceType { get; }

        /// <summary>
        /// 已经同步完成;
        /// 已经收到了 CALL_GetAllProperties, 全部属性都与服务器一致
        /// </summary>
        public bool IsSynced { get; private set; }

        /// <summary>
        /// 当前 INotifyPropertyChanged 对象 引发了 PropertyChanged,  是由于 接收到数据导致的
        /// </summary>
        public bool IsInPushValue { get; protected set; }

        class AnyEvent
        {
            public string name;
            public string triggerName;
            public Type retType;
            public MethodInfo methodInfo;
        }
        class AnyCall
        {
            public string name;
            public Type retType;
        }


        List<AnyEvent> anyEvents = new List<AnyEvent>();
        List<AnyCall> anyCalls = new List<AnyCall>();

        SubPropertyNode rootNode;
        List<string> properties = new List<string>();


        /// <summary>
        /// 缓存 发出 CALL_GetAllProperties指令，到收到回复 之间时段的 全部推送
        /// </summary>
        List<PushInfoData> pushInfoList = new List<PushInfoData>();
        class PushInfoData
        {
            public bool isPushPropertyChanged;
            public Reflect_OBJ_INTERFACE.ReflectData rData;
        }

        #endregion

        public Reflect_SeviceClient(UInt32 id) : base(id) {
            init();
        }

        public Reflect_SeviceClient(UInt32 serviceId, string connName) : base(serviceId, connName) {

            init();
        }

        #region Core
        void init()
        {
            rootNode = new SubPropertyNode
            {
                Obj = this,
                InterfaceType = InterfaceType,
                SubPropertyChanged = Sub_PropertyChanged
            };

            InitPropertyChanged();
            COMMON.InitPropertyPush(rootNode);
            InitEventPush();
            InitCall();

            this.PropertyChanged += (s, e) =>
            {
                if (e.PropertyName == nameof(IsConnected))
                {
                    if (IsConnected == false)
                    {
                        IsSynced = false;
                        pushInfoList.Clear();
                    }
                }
            };
        }
        void InitPropertyChanged()
        {
            if (!typeof(INotifyPropertyChanged).IsAssignableFrom(InterfaceType))
                return;

            //继承了INotifyPropertyChanged
            this.PropertyChanged += rootNode.PropertyChanged;

            var propertyInfos = COMMON.GetAllPropertyInfos(this.InterfaceType);//获取全部属性， 包括父类的属性

            foreach (var propertyInfo in propertyInfos)
            {

                if (propertyInfo.GetCustomAttribute<JsonIgnoreAttribute>() != null)
                    continue;//这个不需要推送

                if (properties.Contains(propertyInfo.Name))
                    continue;
                properties.Add(propertyInfo.Name);
            }
        }

        void InitEventPush()
        {
            var interfaceTypes = new List<Type>();//获取全部接口

            interfaceTypes.Add(InterfaceType);
            interfaceTypes.AddRange(InterfaceType.GetInterfaces());


            var eventInfos = new List<EventInfo>();//获取全部接口的全部event
            foreach (var ifaceType in interfaceTypes)
            {
                eventInfos.AddRange(ifaceType.GetEvents());
            }

            foreach (var eventInfo in eventInfos)
            {
                var pushAttribute = eventInfo.GetCustomAttribute<PushAttribute>();//有[Push]特性的event
                if (pushAttribute == null)
                    continue;

                if (anyEvents.Any(ae => ae.name == eventInfo.Name))
                    continue;//已经添加了

                string triggerName;
                if (string.IsNullOrEmpty(pushAttribute.TriggerName))//没有定义触发方法的名字，使用默认组合
                {
                    triggerName = PushAttribute.DefaultTriggerNameHeader + eventInfo.Name;
                }
                else
                {
                    triggerName = pushAttribute.TriggerName;
                }


                //测试触发方法是否存在，没有抛出异常
                var methodInfo = GetType().GetMethod(triggerName);
                if (methodInfo == null)
                {
                    throw new Exception($"客户端 {GetType()} 忘记写 {triggerName}");
                }





                var anyEvent = new AnyEvent()
                {
                    name = eventInfo.Name,
                    triggerName = triggerName,
                    retType = pushAttribute.EventArgsType,
                    methodInfo = methodInfo
                };
                anyEvents.Add(anyEvent);

            }
        }

        void InitCall()
        {
            var interfaceTypes = new List<Type>();

            interfaceTypes.Add(InterfaceType);
            interfaceTypes.AddRange(InterfaceType.GetInterfaces());


            var methodInfos = new List<MethodInfo>();
            foreach (var ifaceType in interfaceTypes)
            {
                methodInfos.AddRange(ifaceType.GetMethods());
            }

            foreach (var methodInfo in methodInfos)
            {
                var callAttribute = methodInfo.GetCustomAttribute<CallAttribute>();
                if (callAttribute == null)
                    continue;

                if (anyCalls.Any(ae => ae.name == methodInfo.Name))
                    continue;//已经添加了

                var anyCall = new AnyCall()
                {
                    name = methodInfo.Name,
                    retType = callAttribute.ReponseType
                };
                anyCalls.Add(anyCall);

            }
        }

        private void Sub_PropertyChanged(SubPropertyNode node, PropertyChangedEventArgs e)
        {
            if (!IsConnected)
                return;

            if (ignoreSet)//从服务器接收的数据，不用再推送给服务器
                return;

            var type = node.Obj.GetType();

            var propertyInfo = type.GetProperty(e.PropertyName);
            if (propertyInfo == null)
                return;//获取失败！！！

            if (propertyInfo.GetCustomAttribute<JsonIgnoreAttribute>() != null)
                return;//这个不需要推送

            var v = propertyInfo.GetValue(node.Obj);
            var jObject = new JObject();
            if (v == null)
                jObject.Add(propertyInfo.Name, null);
            else
                jObject.Add(propertyInfo.Name, JToken.FromObject(v));

            var rData = new Reflect_OBJ_INTERFACE.ReflectData
            {
                name = COMMON.GetNodePath(node),
                data = jObject
            };
            send_CALL_SetProperty(rData);
        }


        public override void Dispose()
        {
            base.Dispose();

            if (typeof(INotifyPropertyChanged).IsAssignableFrom(InterfaceType))
            {
                //继承了INotifyPropertyChanged
                ((INotifyPropertyChanged)(this)).PropertyChanged -= rootNode.PropertyChanged;
            }

            //释放subProperties
            COMMON.NodeDispose(rootNode);
        }

        protected void Call(string methodName, object parameters, AsyncCBHandler asyncDelegate, object asyncContext)
        {
            var rData = new Reflect_OBJ_INTERFACE.ReflectData()
            {
                name = methodName,
                data = parameters == null ? null : JObject.FromObject(parameters)
            };
            send_CALL_MethodInvoke(rData, asyncDelegate, asyncContext);
        }

        protected void Call(string methodName, object parameters)
        {
            Call(methodName, parameters, null, null);
        }

        protected void Call(string methodName)
        {
            Call(methodName, null, null, null);
        }


        /// <summary>
        /// 处理 从服务器 回复的 CALL_GetAllProperties
        /// </summary>
        /// <param name="rData"></param>
        void receive_CALL_GetAllProperties(Reflect_OBJ_INTERFACE.ReflectData rData)
        {
            receive_PUSH_PropertyChanged(rData);
            IsSynced = true;
            DealPushInfoList();
        }

        /// <summary>
        /// 处理全部 缓存的推送数据
        /// </summary>
        void DealPushInfoList()
        {
            for (int i = 0; i < pushInfoList.Count(); i++)
            {
                var pushInfoData = pushInfoList[i];
                if (pushInfoData.isPushPropertyChanged)
                    receive_PUSH_PropertyChanged(pushInfoData.rData);
                else
                    receive_PUSH_Event(pushInfoData.rData);
            }

            pushInfoList.Clear();
        }

        /// <summary>
        /// 处理 从服务器 回复的 PUSH_PropertyChanged
        /// </summary>
        /// <param name="rData"></param>
        void receive_PUSH_PropertyChanged(Reflect_OBJ_INTERFACE.ReflectData rData)
        {
            ignoreSet = true;
            IsInPushValue = true;
            var node = COMMON.FindNode(rootNode, rData.name);
            if (node == null)
            {
                //异常
                //服务器乱发过来
                return;
            }

            string json = rData.data.ToString();
            JsonConvert.PopulateObject(json, node.Obj);

            IsInPushValue = false;
            ignoreSet = false;
        }

        /// <summary>
        /// 处理 从服务器 回复的 PUSH_Event
        /// </summary>
        /// <param name="rData"></param>
        void receive_PUSH_Event(Reflect_OBJ_INTERFACE.ReflectData rData)
        {
            if (InterfaceType == null)
                return;

            var anyEvent = anyEvents.Find(ae => ae.name == rData.name);
            if (anyEvent == null)
                return;//异常！！！, 在构造客户端时，已经检查完了，现在服务器乱发东西过来。

            //触发事件！！！
            var methodInfo = anyEvent.methodInfo;

            //事件的数据转为 EventArgs 类型 
            var obj = rData.data.ToObject(anyEvent.retType);

            //出错，就提示，肯定是客户端忘记写 "Trigger_XXXX"
            methodInfo.Invoke(this, new object[] { obj });
        }

        /// <summary>
        /// 处理 从服务器 回复的 CALL_MethodInvoke
        /// </summary>
        /// <param name="rData"></param>
        /// <param name="asyncDelegate"></param>
        /// <param name="asyncContext"></param>
        void receive_CALL_MethodInvoke(Reflect_OBJ_INTERFACE.ReflectData rData, AsyncCBHandler asyncDelegate, object asyncContext)
        {
            var anyCall = anyCalls.Find(ac => ac.name == rData.name);
            if (anyCall == null)
            {
                //异常！！！应该有[Call]注明返回的类型
                throw new Exception($"{rData.name} 异常！！！应该有[Call]注明返回的类型");
            }

            var retData = rData.data.ToObject(anyCall.retType);

            asyncDelegate.Invoke(asyncContext, retData);
        }
        #endregion


        /// <summary>
        /// 发送 CALL_MethodInvoke 请求到服务器
        /// </summary>
        /// <param name="rData"></param>
        /// <param name="asyncDelegate"></param>
        /// <param name="asyncContext"></param>
        void send_CALL_MethodInvoke(Reflect_OBJ_INTERFACE.ReflectData rData, AsyncCBHandler asyncDelegate, object asyncContext)
        {
            string json = JsonConvert.SerializeObject(rData);

            CurrObjSys.CallFunctionEx(mConn, mServerID, ID,
                Reflect_OBJ_INTERFACE.CALL_MethodInvoke,
                Misc.Converter.StringToBytes(json),
                asyncDelegate, asyncContext);
        }

        /// <summary>
        /// 发送 CALL_SetProperty 请求到服务器
        /// </summary>
        /// <param name="rData"></param>
        void send_CALL_SetProperty(Reflect_OBJ_INTERFACE.ReflectData rData)
        {
            string json = JsonConvert.SerializeObject(rData);

            CurrObjSys.CallFunctionEx(mConn, mServerID, ID,
                Reflect_OBJ_INTERFACE.CALL_SetProperty,
                Misc.Converter.StringToBytes(json));
        }

        #region FObj
        public override void ConnectNotify(IFConn from)
        {
            base.ConnectNotify(from);

            if (from.IsConnected)
            {
                //获取所有数据，设置推送
                 CurrObjSys.CallFunctionEx(
                    mConn, mServerID, ID,
                    Reflect_OBJ_INTERFACE.CALL_GetAllProperties, null);

                CurrObjSys.SenseConfigEx(
                    mConn, mServerID, ID,
                    0xffffffff,
                    SENSE_CONFIG.ADD);
            }
        }




        public override void PushInfo(IFConn from, uint srcid, ushort infoid, byte[] infodata)
        {
            switch (infoid)
            {
                case Reflect_OBJ_INTERFACE.PUSH_PropertyChanged:
                case Reflect_OBJ_INTERFACE.PUSH_Event:
                    {
                        string json = Misc.Converter.BytesToString(infodata);
                        var rData = JsonConvert.DeserializeObject<Reflect_OBJ_INTERFACE.ReflectData>(json);

                        if (!IsSynced)
                        {
                            //还没同步完成，先缓存
                            pushInfoList.Add(
                                new PushInfoData() { 
                                    isPushPropertyChanged = (infoid == Reflect_OBJ_INTERFACE.PUSH_PropertyChanged),
                                    rData = rData });
                            return;
                        }

                        if(infoid == Reflect_OBJ_INTERFACE.PUSH_PropertyChanged)
                            receive_PUSH_PropertyChanged(rData);
                        else
                            receive_PUSH_Event(rData);

                    }
                    break;
            }

        }


        public override void PushCallFunction(IFConn from, uint srcid, uint magic, ushort funcid, byte[] retdata, AsyncCBHandler asyncDelegate, object asyncContext)
        {
            switch (funcid)
            {
                case Reflect_OBJ_INTERFACE.CALL_GetAllProperties:
                    {
                        string json = Misc.Converter.BytesToString(retdata);
                        var rData = JsonConvert.DeserializeObject<Reflect_OBJ_INTERFACE.ReflectData>(json);

                        receive_CALL_GetAllProperties(rData);
                    }
                    break;
                case Reflect_OBJ_INTERFACE.CALL_MethodInvoke:
                    {
                        if (InterfaceType == null)
                            return;

                        string json = Misc.Converter.BytesToString(retdata);
                        var rData = JsonConvert.DeserializeObject<Reflect_OBJ_INTERFACE.ReflectData>(json);

                        receive_CALL_MethodInvoke(rData, asyncDelegate, asyncContext);
                    }
                    break;
            }
        }
        
        #endregion
    }
}
