// This is a part of the Smart PropertyGrid Library
// Copyright (C) 2001-2006 VisualHint
// All rights reserved.
//
// This source code can be used, distributed or modified
// only under terms and conditions 
// of the accompanying license agreement.

#region Using directives

using System;
using System.ComponentModel;
using System.Collections.Generic;

#endregion

namespace VisualHint.SmartPropertyGrid
{
    /// <exclude />
    public class PropertyValueIndirect : PropertyValue
    {
        private List<KeyValuePair<PropertyDescriptor, object>> _targets = new List<KeyValuePair<PropertyDescriptor,object>>(1);

        public override object TargetInstance
        {
            get
            {
                if (_targets[0].Value == null)
                    return ParentValue.UnderlyingValue;
                else
                    return GetTargetInstance(_targets[0].Value, _targets[0].Key);
            }
        }

        private object GetTargetInstance(object supposedTarget, PropertyDescriptor pd)
        {
            object target = supposedTarget;
            if (target is ICustomTypeDescriptor)
                target = ((ICustomTypeDescriptor)target).GetPropertyOwner(pd);

            return target;
        }

        public override PropertyDescriptor PropertyDescriptor
        {
            get
            {
                return _targets[0].Key;
            }
        }

        public PropertyValueIndirect(PropertyGrid grid, object target, PropertyDescriptor propertyDescriptor, Attribute[] attributes)
        {
            Grid = grid;

            _targets.Add(new KeyValuePair<PropertyDescriptor,object>(propertyDescriptor, target));

            Attributes = attributes;

//            ExtractDisplayedValues();
        }

        public PropertyValueIndirect(PropertyGrid grid, PropertyValue parentValue, PropertyDescriptor propertyDescriptor)
            :
            base(parentValue)
        {
            Grid = grid;

            _targets.Add(new KeyValuePair<PropertyDescriptor, object>(propertyDescriptor, null));

//            ExtractDisplayedValues();
        }

        public override bool HasMultipleValues
        {
            get
            {
                object value1 = null;
                foreach (KeyValuePair<PropertyDescriptor, object> pair in _targets)
                {
                    object target = pair.Value;
                    if (target is ICustomTypeDescriptor)
                        target = ((ICustomTypeDescriptor)target).GetPropertyOwner(pair.Key);

                    if (value1 == null)
                        value1 = pair.Key.GetValue(target);
                    else
                    {
                        if (value1.Equals(pair.Key.GetValue(target)) == false)
                            return true;
                    }
                }

                return false;
            }
        }

        internal override object UnderlyingValue
        {
            get
            {
                return _targets[0].Key.GetValue(TargetInstance);
            }
        }

        public override Type UnderlyingType
        {
            get
            {
                if (mUnderlyingType == null)
                    mUnderlyingType = _targets[0].Key.PropertyType;

                return mUnderlyingType;
            }
        }

        public override TypeConverter TypeConverter
        {
            get
            {
                if (mConverter == null)
                {
                    try
                    {
                        TypeConverterAttribute attrib = (TypeConverterAttribute)GetAttribute(typeof(TypeConverterAttribute));
                        if ((attrib != null) && (attrib.ConverterTypeName.Length > 0))
                            mConverter = (TypeConverter)Activator.CreateInstance(Type.GetType(attrib.ConverterTypeName), false);
                    }
                    catch (Exception)
                    {
                    }

                    if (mConverter == null)
                        mConverter = _targets[0].Key.Converter;
                }

                return mConverter;
            }
        }

        public override string DisplayName
        {
            get { return _targets[0].Key.DisplayName; }
        }

        public override string Name
        {
            get { return _targets[0].Key.Name; }
        }

        private bool SetMultipleValues(object value)
        {
            try
            {
                foreach (KeyValuePair<PropertyDescriptor, object> pair in _targets)
                {
                    pair.Key.SetValue(GetTargetInstance(pair.Value, pair.Key), value);
                }
            }
            catch (Exception)
            {
                return false;
            }

            return true;
        }

        protected override bool ValidateValue(object value)
        {
            // Validate the underlying value
            if (Validator != null)
            {
                if (Validator.Check(value, false) == false)
                    return false;
            }

            // Validate parent if any
            if (mOwnerEnumerator != null)
            {
                PropertyEnumerator parentEnum = mOwnerEnumerator.Parent;
                if ((parentEnum.Property != null) && (parentEnum.Property.Value != null) &&
                    (parentEnum.Property.Value is PropertyValueManaged))
                {
                    string str = "";
                    PropertyEnumerator childEnum = parentEnum.Children;
                    char separator = parentEnum.Property.Value.GroupedValueSeparator;
                    while (childEnum != childEnum.RightBound)
                    {
                        if (childEnum.Property.Value == this)
                        {
                            if (value is string)
                                str += (value as string);
                            else
                            {
                                PropertyTypeDescriptorContext context = childEnum.Property.Value.GetTypeDescriptorContext(childEnum);
                                str += (string)TypeDescriptor.GetConverter(value).ConvertTo(context,
                                    childEnum.Property.Value.CultureInfo, value, typeof(string));
                            }
                        }
                        else
                            str += childEnum.Property.Value.GetStringValue();
                        childEnum.MoveNext();

                        if (childEnum != childEnum.RightBound)
                        {
                            str += separator;
                            str += " ";
                        }
                    }

                    PropertyValidatorBase validator = parentEnum.Property.Value.Validator;
                    if ((validator != null) && (validator.Check(str, false) == false))
                        return false;
                }
            }

            return true;
        }

        protected override bool SetValueInternal(object value, ValueUpdateSource updateSource)
        {
            bool result;

            // Update the new value of this property
            try
            {
                if (value is string)
                {
                    if (UnderlyingType.IsEnum)
                    {
                        object enumValue = TypeConverter.ConvertFromString(GetTypeDescriptorContext(OwnerEnumerator),
                            CultureInfo, GetActualString(value as string));

                        // If the enum is flagged, we don't test if the value to be assigned is correct
                        if (UnderlyingType.IsDefined(typeof(FlagsAttribute), false))
                            result = SetMultipleValues(enumValue);
                        else
                        {
                            if (Enum.IsDefined(UnderlyingType, enumValue))
                                result = SetMultipleValues(enumValue);
                            else
                                result = false;
                        }
                    }
                    else
                    {
                        PropertyTypeDescriptorContext context = GetTypeDescriptorContext(OwnerEnumerator);
                        string str = GetActualString(value as string);
                        if (TypeConverter.CanConvertFrom(context, typeof(string)))
                        {
                            result = SetMultipleValues(TypeConverter.ConvertFromString(
                                context, CultureInfo, str));
                        }
                        else
                        {
                            result = SetMultipleValues(str);
                        }
                    }
                }
                else
                    result = SetMultipleValues(value);
            }
            catch (Exception)
            {
                result = false;
            }
            
            return result;
        }

        public override bool IsReadOnly(PropertyEnumerator selfEnum)
        {
            if (!Grid.Grid.Enabled && ((Grid.DisableMode & PropertyGrid.DisableModes.Simple) != 0))
                return true;

            if ((selfEnum.Parent.Property != null) && (selfEnum.Parent.Property.Value != null))
            {
                if (selfEnum.Parent.Property.Value.IsReadOnly(selfEnum.Parent))
                    return true;
            }

            ReadOnlyAttribute attr = (ReadOnlyAttribute)selfEnum.Property.Value.GetAttribute(typeof(ReadOnlyAttribute));
            return _targets[0].Key.IsReadOnly || (attr != null && attr.IsReadOnly); 
        }

        public override string GetDisplayString(string actualStr)
        {
            if (mActualStrToDisplayedStr != null)
            {
                string str = null;
                if (actualStr != null)
                    str = (string)mActualStrToDisplayedStr[actualStr];

                return (str != null ? str : actualStr);
            }
            else
                return actualStr;
        }

        public override string GetStringValue()
        {
            if (HasMultipleValues)
                return "";

            try
            {
                return TypeConverter.ConvertToString(GetTypeDescriptorContext(OwnerEnumerator), CultureInfo, UnderlyingValue);
            }
            catch (Exception e)
            {
                if (e.InnerException != null)
                    return e.InnerException.Message;
                else
                    return e.Message;
            }
        }

        public override Attribute GetAttribute(Type attributeType)
        {
            // Let's see if we have an attribute that has been passed with AppendProperty
            Attribute attr = base.GetAttribute(attributeType);
            if (attr != null)
                return attr;

            // If not then metadata is searched
            return _targets[0].Key.Attributes[attributeType];
        }

        public override List<Attribute> GetAttributes(Type attributeType)
        {
            List<Attribute> attrList = base.GetAttributes(attributeType);

            if (attrList.Count == 0)
            {
                AttributeUsageAttribute attrUsage = (AttributeUsageAttribute)TypeDescriptor.GetAttributes(attributeType)
                    [typeof(AttributeUsageAttribute)];
                if (attrUsage.AllowMultiple)
                {
                    foreach (Attribute attr in _targets[0].Key.Attributes)
                    {
                        if (attr.GetType() == attributeType)
                            attrList.Add(attr);
                    }
                }
            }

            return attrList;
        }

        public override PropertyTypeDescriptorContext GetTypeDescriptorContext(PropertyEnumerator propEnum)
        {
            return new PropertyTypeDescriptorContext(_targets[0].Key, TargetInstance, propEnum, Grid);
        }

        public override void Recreate()
        {
            RecreateChildProperties();

            PropertyValue parentValue = null;
            if (OwnerEnumerator.Parent.Property != null)
                parentValue = OwnerEnumerator.Parent.Property.Value;

            if ((parentValue == null) ||
                !parentValue.TypeConverter.GetPropertiesSupported() || parentValue.TypeConverter.GetCreateInstanceSupported())
            {
                PropertyDescriptor pd = null;

                object target = TargetInstance;
                if (target != null)
                    pd = TypeDescriptor.GetProperties(target,
                        new Attribute[1] { BrowsableAttribute.Yes })[_targets[0].Key.Name];

                if (pd != null)
                {
                    _targets[0] = new KeyValuePair<PropertyDescriptor, object>(pd, _targets[0].Value);
                    ResetDisplayedValues(true);

                    Grid.ShowProperty(mOwnerEnumerator, true);
                }
                else
                    // Hide properties that are suddenly not browsable
                    Grid.ShowProperty(mOwnerEnumerator, false);
            }

            // Handle ReadOnly
            bool readOnly = IsReadOnly(mOwnerEnumerator);
            if (readOnly)
            {
                if (Grid.ReadOnlyVisual == PropertyGrid.ReadOnlyVisuals.Disabled)
                    Grid.EnableProperty(mOwnerEnumerator, false);
                else
                    mOwnerEnumerator.Property.ForeColor = DisabledForeColor;
            }
            else
            {
                Grid.EnableProperty(mOwnerEnumerator, true);
                mOwnerEnumerator.Property.ForeColor = Grid.ForeColor;
            }

            if (Feel == null)
            {
                if (!readOnly || (((Look == null) || !Look.NoFeelForReadOnly) && (Grid.ReadOnlyVisual == PropertyGrid.ReadOnlyVisuals.ReadOnlyFeel)))
                    Feel = Grid.Grid.FindFeel(mOwnerEnumerator.Parent, mOwnerEnumerator);
            }

            Grid.Refresh();
        }

        internal void AddTargetInstance(object targetInstance, PropertyDescriptor propertyDescriptor)
        {
            if (propertyDescriptor != null)
                _targets.Add(new KeyValuePair<PropertyDescriptor, object>(propertyDescriptor, targetInstance));
        }

        public void AddTargetInstance(object targetInstance, string memberName)
        {
            PropertyDescriptor propertyDescriptor = TypeDescriptor.GetProperties(targetInstance, new Attribute[1] { BrowsableAttribute.Yes })[memberName];
            AddTargetInstance(targetInstance, propertyDescriptor);
        }
    }
}
