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

namespace FObjBase.Reflect
{
    /// <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 Reflect_Proxy : FObj
    {
        #region Core
        /// <summary>
        /// 对象的接口类型
        /// </summary>
        Type interfaceType;
        /// <summary>
        /// 代理对象
        /// </summary>
        object 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 Reflect_Proxy(int objsys_idx, UInt32 id, Type interfaceType, object obj) : base(objsys_idx)
        {
            ID = id;
            Init(interfaceType, obj);
        }

        #region Core
        public void Init(Type interfaceType, object obj)
        {
            this.interfaceType = interfaceType;
            this.obj = obj;

            rootNode = new SubPropertyNode
            {
                Obj = obj,
                InterfaceType = interfaceType,
                SubPropertyChanged = Sub_PropertyChanged
            };

            //注册 obj 的PropertyChanged 事件，获取 interfaceType 全部属性名称，包括它的父类的全部属性名称
            InitPropertyChanged();

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

            //处理[Push]
            InitEventPush();
        }
        void InitPropertyChanged()
        {
            if (!typeof(INotifyPropertyChanged).IsAssignableFrom(interfaceType))
                return;

            //继承了INotifyPropertyChanged
            ((INotifyPropertyChanged)(this.obj)).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(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 = (rd) => reponse_PushObjInfoEx(rd, true)
                };
                //这个事件需要推送
                eventInfo.AddEventHandler(obj, anyEvent.eventHandler);
                anyEvents.Add(anyEvent);

            }
        }
        void Sub_PropertyChanged(SubPropertyNode node, PropertyChangedEventArgs e)
        {
            //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
            };
            reponse_PushObjInfoEx(rData, false);
        }

        Reflect_OBJ_INTERFACE.ReflectData request_CALL_GetAllProperties()
        {

            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 Reflect_OBJ_INTERFACE.ReflectData
            {
                data = jObject
            };
            return rData;
        }

        void request_CALL_SetProperty(Reflect_OBJ_INTERFACE.ReflectData 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(Reflect_OBJ_INTERFACE.ReflectData rData, CC cc)
        {
            var type = obj.GetType();
            var paramNames_req = rData.data.Children().OfType<JProperty>().Select(p => p.Name);
            MethodInfo methodInfo = GetMethodInfo(type, rData.name, paramNames_req);
            if (methodInfo == null)
            {
                //不能找到，
                throw new Exception($"程序写错了, 不能找到 rData.name={rData.name} 或者 参数名称不对，没法完全匹配");
            }

            var parameterInfos = methodInfo.GetParameters();
            object[] parameters = new object[parameterInfos.Count()];
            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.asyncDelegate, true) == 0)
                {
                    parameters[i] = new AsyncCBHandler(asyncDelegate);
                }
                else if (string.Compare(pname, Reflect_OBJ_INTERFACE.asyncContext, true) == 0)
                {
                    cc.methodName = rData.name;
                    parameters[i] = cc;
                }
                else
                {
                    parameters[i] = rData.data[pname].ToObject(ptype);
                }
            }
            methodInfo.Invoke(obj, parameters);
        }

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

            var rData = new Reflect_OBJ_INTERFACE.ReflectData()
            {
                name = cc.methodName,
                data = retData == null ? null : JToken.FromObject(retData)
            };

            reponse_CALL_MethodInvoke(rData, cc);
        }

        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.asyncDelegate);
                names.Remove(Reflect_OBJ_INTERFACE.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_PushObjInfoEx(Reflect_OBJ_INTERFACE.ReflectData rData, bool isEvent)
        {
            //数据推送！！！
            //再嵌套
            var pkgName = isEvent ? Reflect_OBJ_INTERFACE.PUSH_Event : Reflect_OBJ_INTERFACE.PUSH_PropertyChanged;

            string json = JsonConvert.SerializeObject(rData);

            CurrObjSys.PushObjInfoEx(this,
                pkgName,
                Misc.Converter.StringToBytes(json));
        }

        void reponse_CALL_MethodInvoke(Reflect_OBJ_INTERFACE.ReflectData rData, CC cc)
        {
            string json = JsonConvert.SerializeObject(rData);
            CurrObjSys.PushCallFunctionEx(
                cc.from, cc.srcid, ID, cc.magic,
                Reflect_OBJ_INTERFACE.CALL_MethodInvoke,
                Misc.Converter.StringToBytes(json));
        }

        void reponse_CALL_GetAllProperties(Reflect_OBJ_INTERFACE.ReflectData rData, CC cc)
        {
            string json = JsonConvert.SerializeObject(rData);

            CurrObjSys.PushCallFunctionEx(
                cc.from, cc.srcid, ID, cc.magic, 
                Reflect_OBJ_INTERFACE.CALL_GetAllProperties, 
                Misc.Converter.StringToBytes(json));
        }

        public override void CallFunction(IFConn from, uint srcid, uint magic, ushort funcid, byte[] infodata)
        {
            switch (funcid)
            {
                case Reflect_OBJ_INTERFACE.CALL_GetAllProperties:
                    {
                        var rData = request_CALL_GetAllProperties();

                        reponse_CALL_GetAllProperties(rData, new CC() {  from = from, srcid = srcid, magic = magic});
                    }
                    break;
                
                case Reflect_OBJ_INTERFACE.CALL_SetProperty:
                    {
                        string json = Misc.Converter.BytesToString(infodata);
                        var rData = JsonConvert.DeserializeObject<Reflect_OBJ_INTERFACE.ReflectData>(json);

                        request_CALL_SetProperty(rData);
                    }
                    break;

                case Reflect_OBJ_INTERFACE.CALL_MethodInvoke:
                    {
                        string json = Misc.Converter.BytesToString(infodata);
                        var rData = JsonConvert.DeserializeObject<Reflect_OBJ_INTERFACE.ReflectData>(json);

                        request_CALL_MethodInvoke(rData, new CC() { from = from, srcid = srcid, magic = magic });
                    }
                    break;
            }
        }

        class CC
        {
            public IFConn from;
            public UInt32 srcid;
            public UInt32 magic;

            /// <summary>
            /// CALL_MethodInvoke 时使用
            /// </summary>
            public string methodName;
        }
    }

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