using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.Collections;

namespace Misc
{
    public class BindingOperations
    {
        
        // 摘要:
        //     描述绑定中数据流的方向。
        public enum BindingMode
        {
            // 摘要:
            //     导致对源属性或目标属性的更改可自动更新对方。此绑定类型适用于可编辑窗体或其他完全交互式 UI 方案。
            TwoWay = 0,
            //
            // 摘要:
            //     当绑定源(源)更改时,更新绑定目标(目标)属性。如果要绑定的控件为隐式只读控件,则适用此绑定类型。例如,可以绑定到如股市代号之类的源。或者,可能目标属性没有用于进行更改(例如表的数据绑定背景色)的控件接口。如果不需要监视目标属性的更改,则使用
            //     System.Windows.Data.BindingMode.OneWay 绑定模式可避免 System.Windows.Data.BindingMode.TwoWay
            //     绑定模式的系统开销。
            OneWay = 1
        }
        /// <summary>
        /// 当绑定源(源)更改时,更新绑定目标(目标)属性
        /// </summary>
        /// <param name="src"></param>
        /// <param name="srcPropertyName"></param>
        /// <param name="target"></param>
        /// <param name="targetPropertyName"></param>
        public static void SetBinding(INotifyPropertyChanged src, string srcPropertyName, INotifyPropertyChanged target, string targetPropertyName) 
        {
            SetBinding(src, srcPropertyName, target, targetPropertyName, BindingMode.OneWay);
        }
        public static void SetBinding(INotifyPropertyChanged src, string srcPropertyName, IList target, int index) 
        {

            Type type_s = src.GetType();
            Type type_t = target.GetType();
            System.Reflection.PropertyInfo pi_s = type_s.GetProperty(srcPropertyName);
            if (pi_s == null)
                return;
            
            object obj = pi_s.GetValue(src,null);
            target[index] = obj;

            src.PropertyChanged += (s, e) =>
            {
                if (e.PropertyName == srcPropertyName)
                {
                    object o = pi_s.GetValue(src, null);
                    target[index] = obj;
                }
            };
                    
        }
        public static void SetBinding(INotifyPropertyChanged src, string srcPropertyName, Action func) 
        {
            func();
            src.PropertyChanged += (s, e) =>
            {
                if (srcPropertyName==e.PropertyName)
                {
                    func();
                }
            };
        }
        public static void SetBinding(INotifyPropertyChanged src, string[] srcPropertyNames, Action func) 
        {
            func();
            src.PropertyChanged += (s, e) =>
            {
                if (srcPropertyNames.Contains(e.PropertyName)) 
                {
                    func();
                }
            };
        }

        public static void SetBinding(INotifyPropertyChanged src, string srcPropertyName, object target, Action func)
        {
            func();
            PropertyChangedEventHandler pcevent;
            src.PropertyChanged += pcevent = (s, e) =>
            {
                if (srcPropertyName == e.PropertyName)
                {
                    func();
                }
            };
            if (IsMarkdownEvents)
            {
                if (!bindingConexts.ContainsKey(target))
                {
                    bindingConexts.Add(target, new List<PropertyChangedEventContexts>());
                }
                var list = bindingConexts[target];
                list.Add(new PropertyChangedEventContexts() { src = src, propertyChanged = pcevent });
            }
        }
        public static void SetBinding(INotifyPropertyChanged src, string[] srcPropertyNames, object target, Action func)
        {
            func();
            PropertyChangedEventHandler pcevent;
            src.PropertyChanged += pcevent = (s, e) =>
            {
                if (srcPropertyNames.Contains(e.PropertyName))
                {
                    func();
                }
            };

            if (IsMarkdownEvents)
            {
                if (!bindingConexts.ContainsKey(target))
                {
                    bindingConexts.Add(target, new List<PropertyChangedEventContexts>());
                }
                var list = bindingConexts[target];
                list.Add(new PropertyChangedEventContexts() { src = src, propertyChanged = pcevent });
            }
        }
        public class PropertyChangedEventContexts
        {
            public INotifyPropertyChanged src;
            public PropertyChangedEventHandler propertyChanged;
        }
        static Dictionary<object, List<PropertyChangedEventContexts>> bindingConexts;
        static bool IsMarkdownEvents = false;
        /// <summary>
        /// 开始记录事件
        /// </summary>
        public static void StartMarkdownEvents(Dictionary<object, List<PropertyChangedEventContexts>> bindingConexts) 
        {
            IsMarkdownEvents = true;
            BindingOperations.bindingConexts = bindingConexts;
        }
        /// <summary>
        /// 停止记录事件
        /// </summary>
        public static void StopMarkdownEvents() 
        {
            IsMarkdownEvents = false;
            BindingOperations.bindingConexts = null;
        }

        /// <summary>
        /// 释放事件目标对象
        /// </summary>
        /// <param name="src"></param>
        public static void DisposeEventTargetObject(Dictionary<object, List<PropertyChangedEventContexts>> bindingConexts, object target) 
        {
            if (!bindingConexts.ContainsKey(target))
                return;
            var list = bindingConexts[target];
            foreach(var context in list)
            {
                context.src.PropertyChanged -= context.propertyChanged;
            }
            bindingConexts.Remove(target);
        }
        /// <summary>
        /// 释放事件目标对象
        /// </summary>
        /// <param name="src"></param>
        public static void DisposeEventTargetObject(Dictionary<object, List<PropertyChangedEventContexts>> bindingConexts)
        {
            foreach (var list in bindingConexts.Values) 
            {
                foreach (var context in list)
                {
                    context.src.PropertyChanged -= context.propertyChanged;
                }
            }
            bindingConexts.Clear();
        }

        public static void SetBinding(INotifyPropertyChanged src, string srcPropertyName, INotifyPropertyChanged target, string targetPropertyName, BindingMode mode) 
        {
            Type type_s = src.GetType();
            Type type_t = target.GetType();
            System.Reflection.PropertyInfo pi_s = type_s.GetProperty(srcPropertyName);
            System.Reflection.PropertyInfo pi_t = type_t.GetProperty(targetPropertyName);
            if (pi_s == null || pi_t == null)
                return;
      
            switch (mode) 
            {
                case BindingMode.OneWay://src->target
                    {
                        object obj = pi_s.GetValue(src,null);
                        pi_t.SetValue(target, obj, null);

                        PropertyChangedEventHandler pcevent;
                        src.PropertyChanged += pcevent = (s, e) =>
                        {
                            if (e.PropertyName == srcPropertyName)
                            {
                                object o = pi_s.GetValue(src, null);
                                pi_t.SetValue(target, o, null);
                            }
                        };

                        if (IsMarkdownEvents) {
                            if (!bindingConexts.ContainsKey(target)) {
                                bindingConexts.Add(target, new List<PropertyChangedEventContexts>());
                            }
                            var list = bindingConexts[target];
                            list.Add(new PropertyChangedEventContexts() { src = src, propertyChanged = pcevent });
                        }
                    }break;
                case BindingMode.TwoWay://src->target then target->src
                    {
                        object obj = pi_s.GetValue(src,null);
                        pi_t.SetValue(target, obj, null);

                        PropertyChangedEventHandler pcevent;
                        src.PropertyChanged += pcevent = (s, e) =>
                        {
                            if (e.PropertyName == srcPropertyName)
                            {
                                object o = pi_s.GetValue(s, null);
                                pi_t.SetValue(target, o, null);
                            }
                        };
                        
                        if (IsMarkdownEvents)
                        {
                            if (!bindingConexts.ContainsKey(target))
                            {
                                bindingConexts.Add(target, new List<PropertyChangedEventContexts>());
                            }
                            var list = bindingConexts[target];
                            list.Add(new PropertyChangedEventContexts() { src = src, propertyChanged = pcevent });
                        }

                        target.PropertyChanged += pcevent = (s, e) =>
                        {
                            if (e.PropertyName == targetPropertyName)
                            {
                                object o = pi_t.GetValue(s, null);
                                pi_s.SetValue(src, o, null);
                            }
                        };

                        if (IsMarkdownEvents)
                        {
                            if (!bindingConexts.ContainsKey(src))
                            {
                                bindingConexts.Add(src, new List<PropertyChangedEventContexts>());
                            }
                            var list = bindingConexts[src];
                            list.Add(new PropertyChangedEventContexts() { src = target, propertyChanged = pcevent });
                        }
                    }
                    break;
            }
        }

        public static void SetBinding(INotifyPropertyChanged src, string srcPropertyName, ParamDictionary target, string targetPropertyName, BindingMode mode)
        {
            Type type_s = src.GetType();
            System.Reflection.PropertyInfo pi_s = type_s.GetProperty(srcPropertyName);
            if (pi_s == null)
                return;

            switch (mode)
            {
                case BindingMode.OneWay://src->target
                    {
                        object obj = pi_s.GetValue(src, null);
                        target.SetValue(targetPropertyName, obj);
                        
                        src.PropertyChanged += (s, e) =>
                        {
                            if (e.PropertyName == srcPropertyName)
                            {
                                object o = pi_s.GetValue(src, null);
                                target.SetValue(targetPropertyName, obj);
                            }
                        };
                    }
                    break;
                case BindingMode.TwoWay://src->target then target->src
                    {
                        object obj = pi_s.GetValue(src, null);
                        target.SetValue(targetPropertyName, obj);

                        src.PropertyChanged += (s, e) =>
                        {
                            if (e.PropertyName == srcPropertyName)
                            {
                                object o = pi_s.GetValue(s, null);
                                target.SetValue(targetPropertyName, obj);
                            }
                        };

                        target.ValueChanged += (s, e) =>
                        {
                            if (e.Key == targetPropertyName)
                            {
                                object o = target.GetValue(pi_s.PropertyType, targetPropertyName);
                                pi_s.SetValue(src, o, null);
                            }
                        };
                    }
                    break;
            }
        }
    }

}