using System;
using System.Collections.Generic;
using System.Text;
using System.ComponentModel;
using System.Windows.Forms;
using System.Globalization;
using System.Drawing;
using System.Security;
using System.Reflection;
using System.Collections;
using System.ComponentModel.Design.Serialization;
namespace DevComponents.DotNetBar
{
    /// 
    /// Defines class which described single binding.
    /// 
    [TypeConverter(typeof(BindingDefConverer)), DesignTimeVisible(false), ToolboxItem(false)]
    public class BindingDef : INotifyPropertyChanged
    {
        #region Dependency Properties & Events
        public event DataConvertEventHandler Format;
        /// 
        /// Raises Format event.
        /// 
        /// Provides event arguments.
        protected virtual void OnFormat(DataConvertEventArgs e)
        {
            DataConvertEventHandler handler = Format;
            if (handler != null)
                handler(this, e);
        }
        #endregion
        #region Constructor
        /// 
        /// Initializes a new instance of the BindingDef class.
        /// 
        public BindingDef()
        {
        }
        /// 
        /// Initializes a new instance of the BindingDef class.
        /// 
        /// 
        /// 
        public BindingDef(string propertyName, string dataMember)
        {
            _PropertyName = propertyName;
            _PropertyPath = propertyName.Split('.');
            _DataMember = dataMember;
            _BindingMemberInfo = new BindingMemberInfo(dataMember);
        }
        #endregion
        #region Implementation
        /// 
        /// Updates specified item PropertyName with the data from data object property specified by DataMember. 
        /// 
        /// Item to set PropertyName on.
        /// Data to retrieve DataMember value from.
        public void Update(object item, object data)
        {
            Update(null, item, data);
        }
        /// 
        /// Updates specified item PropertyName with the data from data object property specified by DataMember. 
        /// 
        /// CurrencyManager to use
        /// Item to set PropertyName on.
        /// Data to retrieve DataMember value from.
        public void Update(ItemVisualGenerator generator, object item, object data)
        {
            string propertyName = _PropertyName;
            if (item is BaseItem && _PropertyPath.Length > 1)
            {
                // Dig into child items
                BaseItem parent = (BaseItem)item;
                for (int i = 0; i < _PropertyPath.Length - 1; i++)
                {
                    parent = parent.SubItems[_PropertyPath[i]];
                }
                item = parent;
                propertyName = _PropertyPath[_PropertyPath.Length - 1];
            }
            
            PropertyInfo targetProp = item.GetType().GetProperty(propertyName);
            if (targetProp.PropertyType == typeof(string))
                targetProp.SetValue(item, GetDataText(generator, data, _DataMember), null);
            else
                targetProp.SetValue(item, GetDataValue(generator, data, _DataMember, GetPropertyValue(generator, data, _DataMember)), null);
        }
        private static TypeConverter stringTypeConverter;
        private string GetDataText(ItemVisualGenerator generator, object data, string fieldName)
        {
            object propertyValue = GetPropertyValue(generator, data, fieldName);
            return GetDataText(generator, data, fieldName, propertyValue);
        }
        private string GetDataText(ItemVisualGenerator generator, object data, string fieldName, object propertyValue)
        {
            if (!_FormattingEnabled)
            {
                if (data == null)
                {
                    return string.Empty;
                }
                if (propertyValue == null)
                {
                    return "";
                }
                return Convert.ToString(propertyValue, CultureInfo.CurrentCulture);
            }
            DataConvertEventArgs e = new DataConvertEventArgs(propertyValue, typeof(string), data, fieldName);
            this.OnFormat(e);
            if ((e.Value != data) && (e.Value is string))
            {
                return (string)e.Value;
            }
            if (stringTypeConverter == null)
            {
                stringTypeConverter = TypeDescriptor.GetConverter(typeof(string));
            }
            try
            {
                return (string)FormatHelper.FormatObject(propertyValue, typeof(string), generator.GetFieldConverter(fieldName), stringTypeConverter, _FormatString, _FormatInfo, null, DBNull.Value);
            }
            catch (Exception ex)
            {
                if (ex is SecurityException || IsCriticalException(ex))
                {
                    throw;
                }
                return ((propertyValue != null) ? Convert.ToString(data, CultureInfo.CurrentCulture) : "");
            }
        }
        private object GetDataValue(ItemVisualGenerator generator, object data, string fieldName, object propertyValue)
        {
            if (!_FormattingEnabled)
            {
                return propertyValue;
            }
            DataConvertEventArgs e = new DataConvertEventArgs(propertyValue, typeof(object), data, fieldName);
            this.OnFormat(e);
            return e.Value;
        }
        private static bool IsCriticalException(Exception ex)
        {
            return (((((ex is NullReferenceException) || (ex is StackOverflowException)) || ((ex is OutOfMemoryException) || (ex is System.Threading.ThreadAbortException))) || ((ex is ExecutionEngineException) || (ex is IndexOutOfRangeException))) || (ex is AccessViolationException));
        }
        private object GetPropertyValue(ItemVisualGenerator generator, object data, string fieldName)
        {
            if ((data != null) && (fieldName.Length > 0))
            {
                try
                {
                    PropertyDescriptor descriptor = null;
                    if (generator.DataManager != null)
                    {
                        descriptor = generator.DataManager.GetItemProperties().Find(fieldName, true);
                    }
                    if(descriptor == null)
                    {
                        descriptor = TypeDescriptor.GetProperties(data).Find(fieldName, true);
                    }
                    if (descriptor != null)
                    {
                        data = descriptor.GetValue(data);
                    }
                }
                catch
                {
                }
            }
            if (data == DBNull.Value && _NullValue != null)
                return _NullValue;
            return data;
        }
        #endregion
        #region Properties
        private string[] _PropertyPath = new string[0];
        private string _PropertyName = "";
        /// 
        /// Gets or sets the property name binding is attached to.
        /// 
        [DefaultValue(""), Category("Data"), Description("Indicates property name binding is attached to.")]
        public string PropertyName
        {
            get { return _PropertyName; }
            set
            {
                if (value != _PropertyName)
                {
                    string oldValue = _PropertyName;
                    _PropertyName = value;
                    OnPropertyNameChanged(oldValue, value);
                }
            }
        }
        /// 
        /// Called when PropertyName property has changed.
        /// 
        /// Old property value
        /// New property value
        protected virtual void OnPropertyNameChanged(string oldValue, string newValue)
        {
            _PropertyPath = newValue.Split('.');
            OnPropertyChanged(new PropertyChangedEventArgs("PropertyName"));
        }
        /// 
        /// Gets the reference to BindingMemberInfo created based on DataMember.
        /// 
        [Browsable(false)]
        public BindingMemberInfo BindingMemberInfo
        {
            get
            {
                return _BindingMemberInfo;
            }
        }
        private BindingMemberInfo _BindingMemberInfo;
        private string _DataMember = "";
        /// 
        /// Gets or sets the data member name which holds data that PropertyName is populated with.
        /// 
        [DefaultValue(""), Category("Data"), Description("Indicates data member name which holds data that PropertyName is populated with.")]
        public string DataMember
        {
            get { return _DataMember; }
            set
            {
                if (value == null) value = "";
                if (value != _DataMember)
                {
                    string oldValue = _DataMember;
                    _DataMember = value;
                    OnDataMemberChanged(oldValue, value);
                }
            }
        }
        /// 
        /// Called when DataMember property has changed.
        /// 
        /// Old property value
        /// New property value
        protected virtual void OnDataMemberChanged(string oldValue, string newValue)
        {
            if (!string.IsNullOrEmpty(newValue))
                _BindingMemberInfo = new BindingMemberInfo(newValue);
            else
                _BindingMemberInfo = new BindingMemberInfo();
            OnPropertyChanged(new PropertyChangedEventArgs("DataMember"));
        }
        private bool _FormattingEnabled = false;
        /// 
        /// Gets or sets whether type conversion and formatting is applied to the property data.
        /// 
        [DefaultValue(false), Category("Data"), Description("Indicates whether type conversion and formatting is applied to the property data.")]
        public bool FormattingEnabled
        {
            get { return _FormattingEnabled; }
            set
            {
                if (value != _FormattingEnabled)
                {
                    bool oldValue = _FormattingEnabled;
                    _FormattingEnabled = value;
                    OnFormattingEnabledChanged(oldValue, value);
                }
            }
        }
        /// 
        /// Called when FormattingEnabled property has changed.
        /// 
        /// Old property value
        /// New property value
        protected virtual void OnFormattingEnabledChanged(bool oldValue, bool newValue)
        {
            OnPropertyChanged(new PropertyChangedEventArgs("FormattingEnabled"));
        }
        private string _FormatString = "";
        /// 
        /// Gets or sets format specifier characters that indicate how a value is to be displayed.
        /// For more information, see Formatting Overview: http://msdn.microsoft.com/en-us/library/26etazsy%28v=vs.71%29.aspx 
        /// 
        [DefaultValue(""), Category("Data"), Description("Indicates format specifier characters that indicate how a value is to be displayed.")]
        public string FormatString
        {
            get { return _FormatString; }
            set
            {
                if (value != _FormatString)
                {
                    string oldValue = _FormatString;
                    _FormatString = value;
                    OnFormatStringChanged(oldValue, value);
                }
            }
        }
        /// 
        /// Called when FormatString property has changed.
        /// 
        /// Old property value
        /// New property value
        protected virtual void OnFormatStringChanged(string oldValue, string newValue)
        {
            OnPropertyChanged(new PropertyChangedEventArgs("FormatString"));
        }
        private IFormatProvider _FormatInfo = null;
        /// 
        /// Gets or sets the IFormatProvider that provides custom formatting behavior. 
        /// 
        [DefaultValue(null), Category("Data"), Description("Indicates IFormatProvider that provides custom formatting behavior.")]
        public IFormatProvider FormatInfo
        {
            get { return _FormatInfo; }
            set
            {
                if (value != _FormatInfo)
                {
                    IFormatProvider oldValue = _FormatInfo;
                    _FormatInfo = value;
                    OnFormatInfoChanged(oldValue, value);
                }
            }
        }
        /// 
        /// Called when FormatInfo property has changed.
        /// 
        /// Old property value
        /// New property value
        protected virtual void OnFormatInfoChanged(IFormatProvider oldValue, IFormatProvider newValue)
        {
            OnPropertyChanged(new PropertyChangedEventArgs("FormatInfo"));
        }
        private object _NullValue = null;
        /// 
        /// Gets or sets the Object to be set as the target property when the data source contains a DBNull value. 
        /// The data source must contain DBNull for the NullValue property to be correctly applied. 
        /// If the data source type is a type such as a string or integer the value of the NullValue property will be ignored. 
        /// Also, the NullValue property is ignored if it is set to null. 
        /// 
        [DefaultValue(null), Category("Data"), Description("Indicates Object to be set as the control property when the data source contains a DBNull value. ")]
        [System.ComponentModel.TypeConverter(typeof(System.ComponentModel.StringConverter))]
        public object NullValue
        {
            get { return _NullValue; }
            set
            {
                if (value != _NullValue)
                {
                    object oldValue = _NullValue;
                    _NullValue = value;
                    OnNullValueChanged(oldValue, value);
                }
            }
        }
        /// 
        /// Called when NullValue property has changed.
        /// 
        /// Old property value
        /// New property value
        protected virtual void OnNullValueChanged(object oldValue, object newValue)
        {
            OnPropertyChanged(new PropertyChangedEventArgs("NullValue"));
        }
        #endregion
        #region INotifyPropertyChanged Members
        /// 
        /// Raises the PropertyChanged event.
        /// 
        /// Provides event arguments.
        protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null) handler(this, e);
        }
        /// 
        /// Occurs when property on BindingDef object has changed.
        /// 
        [Description("Occurs when property on BindingDef object has changed.Occurs when property on BindingDef object has changed.")]
        public event PropertyChangedEventHandler PropertyChanged;
        #endregion
    }
    /// 
    /// Represents the method that will handle converting for bindings.
    /// 
    /// 
    /// 
    public delegate void DataConvertEventHandler(object sender, DataConvertEventArgs e);
    public class DataConvertEventArgs : ConvertEventArgs
    {
        // Fields
        private object _DataItem;
        // Methods
        public DataConvertEventArgs(object value, Type desiredType, object dataItem, string fieldName)
            : base(value, desiredType)
        {
            _DataItem = dataItem;
            _FieldName = fieldName;
        }
        /// 
        /// Gets the reference to the item being converted.
        /// 
        public object DataItem
        {
            get
            {
                return _DataItem;
            }
        }
        private string _FieldName = "";
        /// 
        /// Get the reference to the name of the field or property on the item that needs conversion.
        /// 
        public string FieldName
        {
            get { return _FieldName; }
        }
    }
    /// 
    /// Represents BindingDefConverer converter.
    /// 
    public class BindingDefConverer : TypeConverter
    {
        /// 
        /// Creates new instance of the class.
        /// 
        public BindingDefConverer() { }
        /// 
        /// Checks whether conversion can be made to specified type.
        /// 
        /// Context Information.
        /// Destination type.
        /// 
        public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
        {
            if (destinationType == typeof(InstanceDescriptor))
                return true;
            return base.CanConvertTo(context, destinationType);
        }
        /// 
        /// Converts object to specified type.
        /// 
        /// Context information.
        /// Culture information.
        /// Object to convert.
        /// Destination type.
        /// Object converted to destination type.
        public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
        {
            if (destinationType == null)
                throw new ArgumentNullException("destinationType");
            if ((destinationType == typeof(InstanceDescriptor)) && (value is BindingDef))
            {
                BindingDef info = (BindingDef)value;
                Type[] constructorParams = null;
                MemberInfo constructorMemberInfo = null;
                object[] constructorValues = null;
                constructorParams = new Type[] { typeof(string), typeof(string) };
                constructorMemberInfo = typeof(BindingDef).GetConstructor(constructorParams);
                constructorValues = new object[] { info.PropertyName, info.DataMember };
                if (constructorMemberInfo != null)
                {
                    return new InstanceDescriptor(constructorMemberInfo, constructorValues);
                }
            }
            return base.ConvertTo(context, culture, value, destinationType);
        }
    }
}