using Newtonsoft.Json.Linq;
using System;
using System.CodeDom;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using static Misc.BindingOperations;

namespace Misc
{
    public class ParamDictionary
    {

        List<KeyValueCell> cells = new List<KeyValueCell>();

        /// <summary>
        /// 获取值
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        public void SetValue(string key, object value) 
        {
            var cell = cells.Find(c => c.Key == key);
            if (cell == null)
            {
                cell = new KeyValueCell() { Key = key, Value = value };//, Type = value.GetType() };
                cell.IsConverted = true;
                cells.Add(cell);
                ValueChanged?.Invoke(this, new ParamDictionaryValueChangedEventArgs() { Key = key, Value = value });
                Save();
            }
            else
            {
                cell.IsConverted = true;
                if (cell.Value != value)
                {
                    cell.Value = value;
                    ValueChanged?.Invoke(this, new ParamDictionaryValueChangedEventArgs() { Key = key, Value = value });
                    Save();
                }
            }
        }

        /// <summary>
        /// 获取值
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <returns></returns>
        public T GetValue<T>(string key) 
        {
            return GetValue<T>(key, default(T));
        }
        public object GetValue(Type type, string key, object defaultValue)
        {
            var cell = cells.Find(c => c.Key == key);
            if (cell == null)
            {
                cell = new KeyValueCell() { Key = key, Value = defaultValue };
                cell.IsConverted = true;
                cells.Add(cell);
                return cell.Value;
            }
            else
            {
                if (cell.IsConverted)
                    return cell.Value;
                else
                {
                    cell.ToValue(type);
                    return cell.Value;
                }
            }
        }
        public object GetValue(Type type, string key)
        {
            var cell = cells.Find(c => c.Key == key);
            if (cell == null)
            {
                return null;
            }
            else
            {
                if (cell.IsConverted)
                    return cell.Value;
                else
                {
                    cell.ToValue(type);
                    return cell.Value;
                }
            }
        }

        /// <summary>
        /// 获取值,如果没有返回默认值
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <param name="defaultValue"></param>
        /// <returns></returns>
        public T GetValue<T>(string key, T defaultValue)
        {
            var cell = cells.Find(c => c.Key == key);
            if (cell == null)
            {
                cell = new KeyValueCell() { Key = key, Value = defaultValue };
                cell.IsConverted = true;
                cells.Add(cell);
                return (T)cell.Value;
            }
            else
            {
                if (cell.IsConverted)
                    return (T)cell.Value;
                else
                {
                    cell.ToValue<T>();
                    //var type = cell.Value.GetType();

                    return (T)cell.Value;
                }
            }
        }

        /// <summary>
        /// 双向绑定
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="src"></param>
        /// <param name="target"></param>
        /// <param name="propertyName"></param>
        /// <param name="defaultValue"></param>
        public void SetBinding<T>(INotifyPropertyChanged target, string propertyName, T defaultValue)
        {
            SetBinding<T>(target, propertyName, propertyName, defaultValue);
        }

        /// <summary>
        /// 双向绑定
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="src"></param>
        /// <param name="target"></param>
        /// <param name="propertyName"></param>
        /// <param name="key"></param>
        /// <param name="defaultValue"></param>
        public void SetBinding<T>(INotifyPropertyChanged target, string propertyName, string key, T defaultValue)
        {
            Type type_s = target.GetType();
            System.Reflection.PropertyInfo pi_t = type_s.GetProperty(propertyName);
            if (pi_t == null)
                return;
            object obj = this.GetValue<T>(key, defaultValue);
            pi_t.SetValue(target, obj);

            target.PropertyChanged += (s, e) =>
            {
                if (e.PropertyName == propertyName)
                {
                    object o = pi_t.GetValue(target);
                    this.SetValue(key, o);
                }
            };

            ValueChanged += (s, e) =>
            {
                if (e.Key == key)
                {
                    pi_t.SetValue(target, e.Value);
                }
            };
        }

        private string path;

        public event UiParamDictionaryValueChangedHandler ValueChanged;
        public ParamDictionary(string path = "paramDict.json")
        {
            this.path = path;
            Load();
        }
        void Load()
        {
            if (!File.Exists(path))
                return;
            try
            {
                string json = File.ReadAllText(path);
                var cells = Newtonsoft.Json.JsonConvert.DeserializeObject<List<KeyValueCell>>(json);
                this.cells = cells;
            }
            catch (Exception e)
            {
                //logger.Error(e, "Load 出错");
            }
        }
        public void Save()
        {
            
            foreach (var cell in cells)
            {
                try
                {
                    cell.ToToken();
                }
                catch (Exception e)
                { 
                    
                }
            }
            try
            {
                string json = Newtonsoft.Json.JsonConvert.SerializeObject(cells, Newtonsoft.Json.Formatting.Indented);
                File.WriteAllText(path, json);
            }
            catch (Exception e)
            {
                //logger.Error(e, "Save 出错");
            }
        }
    }
    public delegate void UiParamDictionaryValueChangedHandler(object sender, ParamDictionaryValueChangedEventArgs e);

    public class ParamDictionaryValueChangedEventArgs : EventArgs
    {
        public string Key;
        public object Value;
    }
    
    
    public class KeyValueCell : INotifyPropertyChanged
    {
        public string Key { get; set; }

        [Newtonsoft.Json.JsonIgnore]
        public object Value { get; set; }

        /// <summary>
        /// 已经转换为object 非 JObject 与 JArray
        /// </summary>
        [Newtonsoft.Json.JsonIgnore]
        public bool IsConverted { get; set; }

        [Newtonsoft.Json.JsonProperty("Value")]
        public JToken Token { get; set; }

        public event PropertyChangedEventHandler PropertyChanged;

        public override string ToString()
        {
            return $"[{Key}]={Newtonsoft.Json.JsonConvert.SerializeObject(Value)}";
        }

        /// <summary>
        /// Value转为类型T
        /// </summary>
        /// <typeparam name="T"></typeparam>
        public void ToValue<T>() 
        {
            IsConverted = true;
            if (Token == null)
                return;
            Value = Token.ToObject<T>();
        }
        public void ToValue(Type type)
        {
            IsConverted = true;
            if (Token == null)
                return;
            Value = Token.ToObject(type);
        }
        public void ToToken() 
        {
            Token = JToken.FromObject(Value);
        }
    }
}