using FObjBase;
using FObjBase.Reflect;
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;
using static WSCF.Reflect_OBJ_INTERFACE;

namespace WSCF
{
    class ReflectSeviceClientAssistant : IDisposable
    {
        public event PropertyChangedEventHandler PropertyChanged;

        #region Core
        /// <summary>
        /// 忽略设置
        /// </summary>
        public bool ignoreSet;
        /// <summary>
        /// 对象的接口类型
        /// </summary>
        Type interfaceType;
        /// <summary>
        /// 代理对象
        /// </summary>
        IReflectSeviceClientCore obj;

        class AnyEvent
        {
            public string name;
            public string triggerName;
            public Type retType;
        }
        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<NameData> pushInfoList = new List<NameData>();


        #endregion

        /// <summary>
        /// 发送 数据 到 服务
        /// </summary>
        Action<string> SendEx;

        class AnyMethodInvoke
        {
            public string guid;
            public AsyncCBHandler asyncDelegate;
            public object asyncContext;
        }


        List<AnyMethodInvoke> anyMethodInvokes = new List<AnyMethodInvoke>();

        public void SetSendEx(Action<string> sendEx)
        {
            this.SendEx = sendEx;
        }


        public ReflectSeviceClientAssistant()
        {

        }
        public void Init(Type interfaceType, IReflectSeviceClientCore obj)
        {
            if (interfaceType == null)
                throw new Exception($"ServiceClient 写错了, {obj.GetType()} 没有 赋值 InterfaceType");
            this.interfaceType = interfaceType;
            this.obj = obj;
            init();
        }
        #region Core
        void init()
        {
            rootNode = new SubPropertyNode
            {
                Obj = obj,
                InterfaceType = interfaceType,
                SubPropertyChanged = Sub_PropertyChanged
            };

            //获取 interfaceType 全部属性名称，包括它的父类的全部属性名称
            COMMON.InitProperties(interfaceType, properties);

            //注册 obj 的PropertyChanged 事件
            obj.PropertyChanged += rootNode.PropertyChanged;

            //处理[PropertyPush]
            COMMON.InitPropertyPush(rootNode);

            InitEventPush();
            InitCall();

            obj.PropertyChanged += (s, e) =>
            {
                if (e.PropertyName == nameof(obj.IsConnected))
                {
                    if (obj.IsConnected == false)
                    {
                        obj.IsSynced = false;
                        pushInfoList.Clear();
                    }
                }
            };
        }

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

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


            var eventInfos = new List<EventInfo>();
            foreach (var ifaceType in interfaceTypes)
            {
                eventInfos.AddRange(ifaceType.GetEvents());
            }

            foreach (var eventInfo in eventInfos)
            {
                var pushAttribute = eventInfo.GetCustomAttribute<PushAttribute>();
                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 anyEvent = new AnyEvent()
                {
                    name = eventInfo.Name,
                    triggerName = triggerName,
                    retType = pushAttribute.EventArgsType
                };
                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 (!obj.IsConnected)
                return;

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

            var rData = COMMON.Sub_PropertyChanged(node, e, rootNode, properties);
            if (rData == null)
                return;

            send_CALL_SetValue(rData.name, rData.data);
        }


        public void Dispose()
        {

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

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

        public void Call(string methodName, object parameters, AsyncCBHandler asyncDelegate, object asyncContext)
        {
            send_CALL_MethodInvoke(methodName, parameters, asyncDelegate, asyncContext);
        }



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

        /// <summary>
        /// 处理全部 缓存的推送数据
        /// </summary>
        void DealPushInfoList()
        {
            for (int i = 0; i < pushInfoList.Count(); i++)
            {
                var pushInfoData = pushInfoList[i];
                if (pushInfoData.name == "PropertyChanged")
                {
                    var data = pushInfoData.data.ToObject<NameData>();
                    receive_EVENT_PropertyChanged(data);
                }
                else
                {
                    receive_EVENT(pushInfoData.name, pushInfoData.data);
                }
            }

            pushInfoList.Clear();
        }

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

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

            obj.IsInPushValue = false;
            ignoreSet = false;
        }

        /// <summary>
        /// 处理 从服务器 回复的 EVENT
        /// </summary>
        /// <param name="rData"></param>
        void receive_EVENT(string eventName, JToken data)
        {
            var anyEvent = anyEvents.Find(ae => ae.name == eventName);
            if (anyEvent == null)
            {
                //没有这个事件，异常
                throw new Exception($"{eventName} 异常！！！没有这个事件处理");
            }

            //触发事件！！！
            var methodInfo = this.obj.GetType().GetMethod(anyEvent.triggerName);
            if (methodInfo == null)
            {
                throw new Exception($"客户端 {this.obj.GetType()} 忘记写 {anyEvent.triggerName}");
            }
            var obj = data.ToObject(anyEvent.retType);
            //出错，就提示，肯定是客户端忘记写 "Trigger_XXXX"
            methodInfo.Invoke(this.obj, new object[] { obj });
        }

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

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

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

        /// <summary>
        /// 发送 CALL_MethodInvoke 请求到服务
        /// </summary>
        /// <param name="rData"></param>
        void send_CALL_MethodInvoke(string methodName, object methodParams, AsyncCBHandler asyncDelegate, object asyncContext)
        {
            var pkgData = new PkgData(
                    methodName,
                    Guid.NewGuid().ToString(),
                    methodParams == null ? null : JObject.FromObject(methodParams)
                );

            anyMethodInvokes.Add(new AnyMethodInvoke()
            {
                guid = pkgData.guid,
                asyncDelegate = asyncDelegate,
                asyncContext = asyncContext
            });

            string json = JsonConvert.SerializeObject(pkgData);
            SendEx(json);
        }

        /// <summary>
        /// 发送 CALL_SetValue 请求到服务
        /// </summary>
        /// <param name="rData"></param>
        void send_CALL_SetValue(string nodePath, JToken data)
        {
            var pkgData = new PkgData(
                "SetValue",
                Guid.NewGuid().ToString(),
                JToken.FromObject(new NameData() { 
                    name = nodePath,
                    data = data
                }));
            string json = JsonConvert.SerializeObject(pkgData);
            SendEx(json);
        }


        #region 被 WsServiceSys 调用

        /// <summary>
        /// 第1次连接成功
        /// </summary>
        public void OnOpen()
        {
            obj.IsConnected = true;

            //获取全部属性
            var pkgData = new PkgData(
                "GetValue",
                Guid.NewGuid().ToString(),
                null
                );
            string json = JsonConvert.SerializeObject(pkgData);
            SendEx(json);
        }
        public void OnClose()
        {
            obj.IsConnected = false;
            //Dispose();
        }

        public void OnMessage(PkgData pkgData)
        {
            var guid = pkgData.guid;

            if (pkgData.name == "PropertyChanged")
            {
                if (!obj.IsSynced)
                {
                    //还没同步完成，先缓存
                    pushInfoList.Add(
                        new NameData()
                        {
                            name = pkgData.name,
                            data = pkgData.data
                        });
                    return;
                }
                else
                {
                    var rData = pkgData.data.ToObject<NameData>();
                    receive_EVENT_PropertyChanged(rData);
                }
            }
            else if (pkgData.name == "GetValue")
            {
                var rData = pkgData.data.ToObject<NameData>();
                receive_CALL_GetAllProperties(rData);
            }
            else if (pkgData.name == "SetValue")
            {
                //没内容

            }
            else
            {
                //可能是 Event， 也可能是 Method
                if (pkgData.guid == null)
                {
                    //EVENT
                    if (!obj.IsSynced)
                    {
                        //还没同步完成，先缓存
                        pushInfoList.Add(
                            new NameData()
                            {
                                name = pkgData.name,
                                data = pkgData.data
                            });
                        return;
                    }
                    else
                    {
                        receive_EVENT(pkgData.name, pkgData.data);
                    }
                }
                else 
                {
                    //METHOD
                    var anyMethodInvoke = anyMethodInvokes.Find(ami => ami.guid == pkgData.guid);
                    if (anyMethodInvoke == null)
                    {
                        //没有这个GUID
                        //异常！！！应该有对应的回调
                        throw new Exception($"{pkgData.name} 异常！！！应该有回调");
                    }
                    else
                    {
                        receive_CALL_MethodInvoke(pkgData.name, pkgData.data, anyMethodInvoke.asyncDelegate, anyMethodInvoke.asyncContext);

                        anyMethodInvokes.Remove(anyMethodInvoke);
                    }
                }

            }
        }

        #endregion


    }
}

