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.Xml.Linq;
using static WSCF.Reflect_OBJ_INTERFACE;

namespace WSCF
{
    /// <summary>
    /// obj 服务代理;
    /// 1.如果代理的对象为 INotifyPropertyChanged, 对象内的 property 改变，能推送给客户端( [JsonIgnore] 除外);
    /// 2.代理的对象不为INotifyPropertyChanged, 但 对象内的 property 
    ///     a.是 [PropertyPush], 
    ///     b.且是 INotifyPropertyChanged, 
    ///     c.且 只能是get,不能有set,
    /// 那这个 property 内的 子property 改变，也会推送给 客户端 且 子property 是 [PropertyPush]，会继续向下全部注册 PropertyChanged,
    /// 也推送给 客户端;
    /// 3. 客户端 可以通过 Call(.....) 调用 服务 的function,  function返回的内容，需要通过[Call(...)] 定义; 且function 必须是
    /// void function(object param1, object param2,object paramN, AsyncCBHandler asyncDelegate, object asyncContext);
    /// 必须有参数 AsyncCBHandler asyncDelegate, object asyncContext， 名字不能改。 前面的 param1~paramN， 多少个都行
    /// 4. 服务中的event, 通过 [Push(...)] 可以推送给客户端， 但event 只能是 EventHandler
    /// [Push(typeof(BulkDBTempFrameChangedEventArgs))]
    ///  public event EventHandler TempFrameChanged;
    ///  
    /// </summary>
    public class WsServiceProxy : IDisposable
    {
        static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
        #region Core
        /// <summary>
        /// 对象的接口类型
        /// </summary>
        Type interfaceType;
        /// <summary>
        /// 代理对象
        /// </summary>
        INotifyPropertyChanged obj;
        /// <summary>
        /// 忽略设置
        /// </summary>
        public bool ignoreSet = false;
        /// <summary>
        /// 对象的全部event
        /// </summary>
        List<AnyEvent> anyEvents = new List<AnyEvent>();
        /// <summary>
        /// [PropertyPush] 标签树 
        /// </summary>
        SubPropertyNode rootNode;
        /// <summary>
        /// interfaceType 及其父类 的全部属性
        /// </summary>
        List<string> properties = new List<string>();
        #endregion
        public bool isAlive = false;

        //向客户端 回复数据
        public Action<string> SendEx;
        /// <summary>
        /// 服务在 服务器中
        /// </summary>
        public bool IsServiceInServer = true;

        public WsServiceProxy()
        {

        }
        public void SetSendEx(Action<string> sendEx)
        {
            this.SendEx = sendEx;
        }
        #region Core
        public void Init(Type interfaceType, INotifyPropertyChanged obj)
        {
            this.interfaceType = interfaceType;
            this.obj = obj;

            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);

            //处理[Push]
            InitEventPush();
        }

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

            interfaceTypes.Add(this.interfaceType);
            interfaceTypes.AddRange(this.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.eventName == eventInfo.Name))
                    continue;//已经添加了

                var anyEvent = new AnyEvent()
                {
                    eventName = eventInfo.Name,
                    PushObjInfoEx = (eventName, eventArgs) => reponse_EVENT(eventName, eventArgs)
                };
                //这个事件需要推送
                eventInfo.AddEventHandler(obj, anyEvent.eventHandler);
                anyEvents.Add(anyEvent);

            }
        }
        void Sub_PropertyChanged(SubPropertyNode node, PropertyChangedEventArgs e)
        {
            var rData = COMMON.Sub_PropertyChanged(node, e, rootNode, properties);
            if (rData == null)
                return;

            reponse_EVENT_PropertyChanged(rData);
        }

        NameData request_CALL_GetValue()
        {

            var jObject = new JObject();

            var type = obj.GetType();

            foreach (var propertyName in properties)
            {
                var propertyInfo = type.GetProperty(propertyName);
                var v = propertyInfo.GetValue(obj);
                if (v == null)
                    jObject.Add(propertyInfo.Name, null);
                else
                    jObject.Add(propertyInfo.Name, JToken.FromObject(v));
            }

            var rData = new NameData
            {
                data = jObject
            };
            return rData;
        }
        NameData request_CALL_GetValue(string nodePath)
        {
            //sub property
            var node = COMMON.FindNode(rootNode, nodePath);
            if (node == null)
            {
                //异常
                //客户端乱发过来
                //没有这个属性
                return new NameData
                {
                    name = nodePath,
                    data = new JObject()
                };
            }

            if (node == rootNode)
                return request_CALL_GetValue();
            else
            {
                var jObject = JObject.FromObject(node.Obj);
                return new NameData
                {
                    data = jObject
                };
            }
        }
        void request_CALL_SetValue(NameData rData)
        {
            //ignoreSet = true;

            //sub property
            var node = COMMON.FindNode(rootNode, rData.name);
            if (node == null)
            {
                //异常
                //客户端乱发过来
                return;
            }

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

            //ignoreSet = false;
        }

        void request_CALL_MethodInvoke(string guid, string methodName, JToken methodParams)
        {
            var type = obj.GetType();
            //当没有参数时，paramNames_req也要为[0]
            IEnumerable<string> paramNames_req;
            if (methodParams == null)
            {
                paramNames_req = new string[0];
            }
            else
                paramNames_req = methodParams.Children().OfType<JProperty>().Select(p => p.Name);

            MethodInfo methodInfo = GetMethodInfo(type, methodName, paramNames_req);
            if (methodInfo == null)
            {
                //不能找到，
                throw new Exception($"程序写错了, 不能找到 方法名={methodName} 或者 参数名称不对，没法完全匹配");
            }

            var parameterInfos = methodInfo.GetParameters();
            object[] parameters = new object[parameterInfos.Count()];
            bool hasAsyncDelegate = false;
            for (int i = 0; i < parameters.Count(); i++)
            {
                var ptype = parameterInfos[i].ParameterType;
                var pname = parameterInfos[i].Name;
                if (string.Compare(pname, Reflect_OBJ_INTERFACE.paramName_asyncDelegate, true) == 0)
                {
                    parameters[i] = new AsyncCBHandler(asyncDelegate);
                    hasAsyncDelegate = true;
                }
                else if (string.Compare(pname, Reflect_OBJ_INTERFACE.paramName_asyncContext, true) == 0)
                {
                    parameters[i] = new CC() { guid = guid, methodName = methodName };
                }
                else
                {
                    parameters[i] = methodParams[pname].ToObject(ptype);
                }
            }

            methodInfo.Invoke(obj, parameters);

            if (!hasAsyncDelegate)
            {
                //没有数据返回。 返回null，让客户端 await继续
                asyncDelegate(new CC() { guid = guid, methodName = methodName }, null);
            }
        }

        void asyncDelegate(object asyncContext, object retData)
        {
            var cc = (CC)asyncContext;

            reponse_CALL_MethodInvoke(cc.guid, cc.methodName, retData);
        }

        MethodInfo GetMethodInfo(Type type, string name, IEnumerable<string> parameterNames)
        {

            var methodInfos = from mi in type.GetMethods() where mi.Name == name select mi;
            if (methodInfos.Count() == 0)
                return null;
            if (methodInfos.Count() == 1)
                return methodInfos.First();

            //必须完全匹配
            foreach (var methodInfo in methodInfos)
            {
                var parameterInfos = methodInfo.GetParameters();
                //全部参数名称
                var names = parameterInfos.Select(pi => pi.Name).ToList();
                //删除掉 asyncDelegate，asyncContext
                names.Remove(Reflect_OBJ_INTERFACE.paramName_asyncDelegate);
                names.Remove(Reflect_OBJ_INTERFACE.paramName_asyncContext);

                var names_req = parameterNames;

                if (names.Count() != names_req.Count())
                    continue;//数量不一致，肯定不同

                var sames = names_req.Intersect(names);

                if (sames.Count() != names_req.Count())
                    continue;// names 与 names_req 的交集数量与names_req不一样，肯定不同

                //就是它
                return methodInfo;

            }
            return null;
        }

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

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

        void reponse_EVENT(string eventName, object eventArgs)
        {
            string json = JsonConvert.SerializeObject(
                new
                {
                    name = eventName,
                    data = eventArgs
                });
            SendEx?.Invoke(json);
        }
        void reponse_EVENT_PropertyChanged(NameData rdata)
        {
            string json = JsonConvert.SerializeObject(
                new
                {
                    name = "PropertyChanged",
                    data = rdata
                });
            SendEx?.Invoke(json);
        }
        void reponse_CALL_MethodInvoke(string guid, string methodName, object data)
        {
            string json = JsonConvert.SerializeObject(
                new
                {
                    guid = guid,
                    name = methodName,
                    data = data
                });
            SendEx?.Invoke(json);
        }
        void reponse_CALL_GetValue(string guid, NameData rData)
        {
            string json = JsonConvert.SerializeObject(
                new
                {
                    guid = guid,
                    name = "GetValue",
                    data = rData
                });
            SendEx?.Invoke(json);
        }
        void reponse_CALL_SetValue(string guid, string nodePath)
        {
            string json = JsonConvert.SerializeObject(
                new
                {
                    guid = guid,
                    name = "SetValue",
                    data = nodePath
                });
            SendEx?.Invoke(json);
        }


        #region 被 WsService 调用
        public void OnMessage(Reflect_Proxy proxy, PkgData pkgData)
        {
            var pkgName = pkgData.name;
            if (pkgName == "GetValue")
            {
                if ((pkgData.data == null)
                    || (pkgData.data.Type != JTokenType.String)
                    || pkgData.data.ToString() == ".")
                {
                    //获取全部属性
                    var rData = request_CALL_GetValue();
                    reponse_CALL_GetValue(pkgData.guid, rData);
                }
                else
                {
                    //获取全部属性
                    string nodePath = pkgData.data.ToString();
                    var rData = request_CALL_GetValue(nodePath);
                    reponse_CALL_GetValue(pkgData.guid, rData);
                }
            }
            else if (pkgName == "SetValue")
            {
                //ignoreSet = true;
                var rData = pkgData.data.ToObject<NameData>();
                request_CALL_SetValue(rData);
                reponse_CALL_SetValue(pkgData.guid, rData.name);
            }
            else
            {
                request_CALL_MethodInvoke(pkgData.guid, pkgName, pkgData.data);
            }
        }




        public void OnOpen()
        {
            isAlive = true;
            AfterOpen?.Invoke(this);
        }

        public Action<WsServiceProxy> AfterOpen;

        public void OnClose()
        {
            isAlive = false;
            Dispose();
        }

        #endregion
    }
    /// <summary>
    /// 事件推送包装
    /// </summary>
    class AnyEvent
    {
        /// <summary>
        /// 事件名称
        /// </summary>
        public string eventName;
        /// <summary>
        /// json包装后的ReflectData 推送
        /// </summary>
        public Action<string, object> PushObjInfoEx;
        /// <summary>
        /// 给 EventInfo 调用
        /// </summary>
        public EventHandler eventHandler;
        public AnyEvent()
        {
            eventHandler = new EventHandler(Obj_AnyEvent);
        }
        void Obj_AnyEvent(object sender, EventArgs e)
        {
            PushObjInfoEx?.Invoke(eventName, e);
        }
    }

    public class CC
    {
        public string guid;
        /// <summary>
        /// CALL_MethodInvoke 时使用
        /// </summary>
        public string methodName;

    }
}
