using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using Newtonsoft.Json;

namespace InABox.Core
{
    public class UserProperty
    {
        [DoNotSerialize]
        public Type Type { get; set; }

        public object? Value { get; set; }
    }


    [Serializable]
    public class UserProperties
    {
        public delegate void PropertyChangedEventHandler(object sender, string name, object? before, object? after);

        public UserProperties()
        {
            Dictionary = new Dictionary<string, UserProperty>();
            //_dictionary = new Dictionary<string, UserProperty>();
        }

        public Dictionary<string, UserProperty> Dictionary { get; set; }

        //public Type ParentType { get; set; }

        public object? this[string key]
        {
            get => Get(key);
            set => Set(key, value);
        }

        [JsonIgnore]
        public Dictionary<string, object?> AsDictionary
        {
            get
            {
                var result = new Dictionary<string, object?>();
                foreach (var key in Dictionary.Keys)
                    result[key] = this[key];
                return result;
            }
            set
            {
                Clear();
                foreach (var key in value.Keys)
                    this[key] = value[key];
            }
        }

        [JsonIgnore]
        public int Count => Dictionary.Count;

        public override bool Equals(object obj)
        {
            if (!(obj is UserProperties other))
                return false;

            if (Dictionary.Count != other.Count) // Require equal count.
                return false;

            foreach (var pair in Dictionary)
                try
                {
                    var value = other[pair.Key];
                    if (value == null)
                    {
                        if (pair.Value != null)
                            return false;
                    }
                    else if (!value.Equals(pair.Value))
                    {
                        return false;
                    }
                }
                catch
                {
                    return false;
                }

            return true;
        }

        public event PropertyChangedEventHandler? OnPropertyChanged;

        private object? Get(string key)
        {
            var prop = new UserProperty { Type = typeof(string), Value = "" };
            if (Dictionary.ContainsKey(key))
                prop = Dictionary[key];

            if (prop.Type == null)
                return prop.Value;

            if (prop.Value == null)
                return prop.Type == typeof(object) ? null : Activator.CreateInstance(prop.Type);

            if (prop.Value.GetType() == prop.Type)
                return prop.Value;

            var tc = TypeDescriptor.GetConverter(prop.Type);
            return tc.ConvertFrom(prop.Value);
        }

        private void Set(string key, object? obj)
        {
            var before = Dictionary.GetValueOrDefault(key);
            Dictionary[key] = new UserProperty { Type = obj?.GetType() ?? typeof(object), Value = obj };
            OnPropertyChanged?.Invoke(this, key, before, obj);
        }

        public bool ContainsKey(string key)
        {
            return Dictionary.ContainsKey(key);
        }

        public void Clear()
        {
            Dictionary.Clear();
        }

        public string[] GetKeys()
        {
            return Dictionary.Keys.ToArray();
        }

        internal void Load(IEnumerable<KeyValuePair<string, object?>> pairs)
        {
            foreach (var pair in pairs)
                Dictionary[pair.Key] = new UserProperty { Type = pair.Value != null ? pair.Value.GetType() : typeof(object), Value = pair.Value };
        }

        internal void LoadFromDictionary(Dictionary<string, object?> defaultProperties)
        {
            foreach (var pair in defaultProperties)
                Dictionary[pair.Key] = new UserProperty { Type = pair.Value != null ? pair.Value.GetType() : typeof(object), Value = pair.Value };
        }
    }

    public class UserPropertiesJsonConverter : JsonConverter
    {
        public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer)
        {
            if (!(value is UserProperties props)) return;
            writer.WriteStartObject();
            foreach (var key in props.GetKeys())
                if (props[key] != null)
                {
                    writer.WritePropertyName(key);
                    writer.WriteValue(props[key] is string ? props[key] as string : props[key]);
                }

            writer.WriteEndObject();
        }

        public override object ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer)
        {
            var result = new UserProperties();
            while (reader.TokenType != JsonToken.EndObject && reader.Read())
                if (reader.Value != null)
                {
                    var key = reader.Value.ToString();
                    reader.Read();
                    result[key] = reader.Value;
                }

            return result;
        }

        public override bool CanConvert(Type objectType)
        {
            return objectType == typeof(UserProperties);
        }
    }
}