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

namespace LLVM.ClangTidy
{
    public class DynamicPropertyDescriptor<T> : PropertyDescriptor
    {
        T Value_;
        DynamicPropertyDescriptor<T> Parent_;
        bool IsInheriting_;
        object Component_;

        public DynamicPropertyDescriptor(object Component, DynamicPropertyDescriptor<T> Parent, string Name, Attribute[] Attrs)
            : base(Name, Attrs)
        {
            foreach (DefaultValueAttribute Attr in Attrs.OfType<DefaultValueAttribute>())
            {
                Value_ = (T)Attr.Value;
            }
            Parent_ = Parent;
            IsInheriting_ = true;
            Component_ = Component;
        }

        public bool IsInheriting { get { return IsInheriting_; } set { IsInheriting_ = value; } }
        public DynamicPropertyDescriptor<T> Parent { get { return Parent_; } }

        /// <summary>
        /// Determines whether this property's value should be considered "default" (e.g.
        /// displayed in bold in the property grid).  Root properties are unmodifiable and
        /// always default.  Non-root properties are default iff they are inheriting.
        /// That is to say, if a property is explicitly set to False, the property should
        /// be serialized even if the parent is also False.  It would only not be serialized
        /// if the user had explicitly chosen to inherit it.
        /// </summary>
        /// <param name="component"></param>
        /// <returns></returns>
        public override bool ShouldSerializeValue(object component)
        {
            return (Parent_ != null) && !IsInheriting;
        }

        /// <summary>
        /// Set the value back to the default.  For root properties, this essentially does
        /// nothing as they are read-only anyway.  For non-root properties, this only means
        /// that the property is now inheriting.
        /// </summary>
        /// <param name="component"></param>
        public override void ResetValue(object component)
        {
            IsInheriting_ = true;
        }

        public override void SetValue(object component, object value)
        {
            // This is a bit of a trick.  If the user chose the inheritance option from the
            // dropdown, we will try to set the value to that string.  So look for that and
            // then just reset the value.
            if (value.Equals(MagicInheritance.Text))
                ResetValue(component);
            else
            {
                // By explicitly setting the value, this property is no longer inheriting,
                // even if the value the property is being set to is the same as that of
                // the parent.
                IsInheriting_ = false;
                Value_ = (T)value;
            }
        }

        public override TypeConverter Converter
        {
            get
            {
                // We need to return a DynamicPropertyConverter<> that can deal with our requirement
                // to inject the inherit property option into the dropdown.  But we still need to use
                // the "real" converter to do the actual work for the underlying type.  Therefore,
                // we need to look for a TypeConverter<> attribute on the property, and if it is present
                // forward an instance of that converter to the DynamicPropertyConverter<>.  Otherwise,
                // forward an instance of the default converter for type T to the DynamicPropertyConverter<>.
                TypeConverter UnderlyingConverter = null;
                var ConverterAttr = this.Attributes.OfType<TypeConverterAttribute>().LastOrDefault();
                if (ConverterAttr != null)
                {
                    Type ConverterType = Type.GetType(ConverterAttr.ConverterTypeName);
                    UnderlyingConverter = (TypeConverter)Activator.CreateInstance(ConverterType);
                }
                else
                    UnderlyingConverter = TypeDescriptor.GetConverter(typeof(T));

                return new DynamicPropertyConverter<T>(this, UnderlyingConverter);
            }
        }

        public override bool IsReadOnly
        {
            get
            {
                return (Parent_ == null);
            }
        }

        public override Type ComponentType
        {
            get
            {
                return Component_.GetType();
            }
        }

        public override object GetValue(object component)
        {
            // Return either this property's value or the parents value, depending on
            // whether or not this property is inheriting.
            if (IsInheriting_ && Parent != null)
                return Parent.GetValue(component);
            return Value_;
        }

        public override bool CanResetValue(object component)
        {
            return !IsReadOnly;
        }

        public override Type PropertyType
        {
            get
            {
                return typeof(T);
            }
        }
    }
}
