namespace PropertyGridEx
{
    using Microsoft.VisualBasic;
    using System;
    using System.Collections;
    using System.ComponentModel;
    using System.Drawing.Design;
    using System.Runtime.CompilerServices;
    using System.Runtime.InteropServices;
    using System.Data;
    using System.Xml.Serialization;

    [Serializable(), XmlRootAttribute("CustomProperty")]
    public class CustomProperty
    {				
		#region "Private variables"
		
		// Common properties
		protected string sName = "";
		protected object oValue = null;
		protected bool bIsReadOnly = false;
		protected bool bVisible = true;
		protected string sDescription = "";
		protected string sCategory = "";
		protected bool bIsPassword = false;
		protected bool bIsPercentage = false;
		protected bool bParenthesize = false;
		
		// Filename editor properties
		protected string sFilter = null;
		protected UIFilenameEditor.FileDialogType eDialogType = UIFilenameEditor.FileDialogType.LoadFileDialog;
		protected bool bUseFileNameEditor = false;
		
		// Custom choices properties
		protected CustomChoices oChoices = null;
		
		// Browsable properties
		protected bool bIsBrowsable = false;
		protected BrowsableTypeConverter.LabelStyle eBrowsablePropertyLabel = BrowsableTypeConverter.LabelStyle.lsEllipsis;
		
		// Dynamic properties
		protected bool bRef = false;
		protected object oRef = null;
		protected string sProp = "";
		
		// Databinding properties
		protected object oDatasource = null;
		protected string sDisplayMember = null;
		protected string sValueMember = null;
		protected object oSelectedValue = null;
		protected object oSelectedItem = null;
		protected bool bIsDropdownResizable = false;
		
		// 3-dots button event handler
		protected UICustomEventEditor.OnClick MethodDelegate;
		
		// Extended Attributes
		[NonSerialized()]protected AttributeCollection oCustomAttributes = null;
		protected object oTag = null;
		protected object oDefaultValue = null;
		protected Type oDefaultType = null;
		
		// Custom Editor and Custom Type Converter
		[NonSerialized()]protected UITypeEditor oCustomEditor = null;
		[NonSerialized()]protected TypeConverter oCustomTypeConverter = null;
		
		#endregion
		
		#region "Public methods"
		
		public CustomProperty()
		{
			sName = "New Property";
			oValue = new string(' ',0);
		}
		
		public CustomProperty(string strName, object objValue, bool boolIsReadOnly, string strCategory, string strDescription, bool boolVisible)
		{
			sName = strName;
			oValue = objValue;
			bIsReadOnly = boolIsReadOnly;
			sDescription = strDescription;
			sCategory = strCategory;
			bVisible = boolVisible;
			if (oValue != null)
			{
				oDefaultValue = oValue;
			}
		}
		
		public CustomProperty(string strName, ref object objRef, string strProp, bool boolIsReadOnly, string strCategory, string strDescription, bool boolVisible)
		{
			sName = strName;
			bIsReadOnly = boolIsReadOnly;
			sDescription = strDescription;
			sCategory = strCategory;
			bVisible = boolVisible;
			bRef = true;
			oRef = objRef;
			sProp = strProp;
			if (Value != null)
			{
				oDefaultValue = Value;
			}
		}

        public void RebuildAttributes()
        {
            if (bUseFileNameEditor)
            {
                BuildAttributes_FilenameEditor();
            }
            else if (oChoices != null)
            {
                BuildAttributes_CustomChoices();
            }
            else if (oDatasource != null)
            {
                BuildAttributes_ListboxEditor();
            }
            else if (bIsBrowsable)
            {
                BuildAttributes_BrowsableProperty();
            }
        }
		
		#endregion
		
		#region "Private methods"
		
		private void BuildAttributes_FilenameEditor()
		{
			ArrayList attrs = new ArrayList();
			UIFilenameEditor.FileDialogFilterAttribute FilterAttribute = new UIFilenameEditor.FileDialogFilterAttribute(sFilter);
			UIFilenameEditor.SaveFileAttribute SaveDialogAttribute = new UIFilenameEditor.SaveFileAttribute();
			Attribute[] attrArray;
			attrs.Add(FilterAttribute);
			if (eDialogType == UIFilenameEditor.FileDialogType.SaveFileDialog)
			{
				attrs.Add(SaveDialogAttribute);
			}
			attrArray =  (System.Attribute[]) attrs.ToArray(typeof(Attribute));
			oCustomAttributes = new AttributeCollection(attrArray);
		}
		
		private void BuildAttributes_CustomChoices()
		{
			if (oChoices != null)
			{
				CustomChoices.CustomChoicesAttributeList list = new CustomChoices.CustomChoicesAttributeList(oChoices.Items);
				ArrayList attrs = new ArrayList();
				Attribute[] attrArray;
				attrs.Add(list);
				attrArray =  (System.Attribute[]) attrs.ToArray(typeof(Attribute));
				oCustomAttributes = new AttributeCollection(attrArray);
			}
		}
		
		private void BuildAttributes_ListboxEditor()
		{
			if (oDatasource != null)
			{
				UIListboxEditor.UIListboxDatasource ds = new UIListboxEditor.UIListboxDatasource(ref oDatasource);
				UIListboxEditor.UIListboxValueMember vm = new UIListboxEditor.UIListboxValueMember(sValueMember);
				UIListboxEditor.UIListboxDisplayMember dm = new UIListboxEditor.UIListboxDisplayMember(sDisplayMember);
				UIListboxEditor.UIListboxIsDropDownResizable ddr = null;
				ArrayList attrs = new ArrayList();
				attrs.Add(ds);
				attrs.Add(vm);
				attrs.Add(dm);
				if (bIsDropdownResizable)
				{
					ddr = new UIListboxEditor.UIListboxIsDropDownResizable();
					attrs.Add(ddr);
				}
				Attribute[] attrArray;
				attrArray =  (System.Attribute[]) attrs.ToArray(typeof(Attribute));
				oCustomAttributes = new AttributeCollection(attrArray);
			}
		}

        private void BuildAttributes_BrowsableProperty()
        {
            BrowsableTypeConverter.BrowsableLabelStyleAttribute style = new BrowsableTypeConverter.BrowsableLabelStyleAttribute(eBrowsablePropertyLabel);
            oCustomAttributes = new AttributeCollection(new Attribute[] { style });
        }
		
		private void BuildAttributes_CustomEventProperty()
		{
			UICustomEventEditor.DelegateAttribute attr = new UICustomEventEditor.DelegateAttribute(MethodDelegate);
			oCustomAttributes = new AttributeCollection(new Attribute[] {attr});
		}
		
		private object DataColumn
		{
			get
			{
				DataRow oRow = (System.Data.DataRow) oRef;
				if (oRow.RowState != DataRowState.Deleted)
				{
				if (oDatasource == null)
				{
					return oRow[sProp];
				}
				else
				{
					DataTable oLookupTable = oDatasource as DataTable;
					if (oLookupTable != null)
					{
						return oLookupTable.Select(sValueMember + "=" + oRow[sProp])[0][sDisplayMember];
					}
					else
					{
						Information.Err().Raise(Constants.vbObjectError + 513, null, "Bind of DataRow with a DataSource that is not a DataTable is not possible", null, null);
						return null;
					}
				}
			}
				else
				{
					return null;
				}
			}
			set
			{
				DataRow oRow = (System.Data.DataRow) oRef;
				if (oRow.RowState != DataRowState.Deleted)
				{
				if (oDatasource == null)
				{
					oRow[sProp] = value;
				}
				else
				{
					DataTable oLookupTable = oDatasource as DataTable;
					if (oLookupTable != null)
					{
						if (oLookupTable.Columns[sDisplayMember].DataType.Equals(System.Type.GetType("System.String")))
						{
							
							oRow[sProp] = oLookupTable.Select(oLookupTable.Columns[sDisplayMember].ColumnName + " = \'" + value + "\'")[0][sValueMember];
						}
						else
						{
							oRow[sProp] = oLookupTable.Select(oLookupTable.Columns[sDisplayMember].ColumnName + " = " + value)[0][sValueMember];
						}
					}
					else
					{
						Information.Err().Raise(Constants.vbObjectError + 514, null, "Bind of DataRow with a DataSource that is not a DataTable is impossible", null, null);
					}
				}
			}
		}
		}
		
		#endregion
		
		#region "Public properties"

        [Category("Appearance"), DisplayName("Name"), DescriptionAttribute("Display Name of the CustomProperty."), ParenthesizePropertyName(true), XmlElementAttribute("Name")]public string Name
        {
			get
			{
				return sName;
			}
			set
			{
				sName = value;
			}
		}

        [Category("Appearance"), DisplayName("ReadOnly"), DescriptionAttribute("Set read only attribute of the CustomProperty."), XmlElementAttribute("ReadOnly")]public bool IsReadOnly
        {
			get
			{
				return bIsReadOnly;
			}
			set
			{
				bIsReadOnly = value;
			}
		}

        [Category("Appearance"), DescriptionAttribute("Set visibility attribute of the CustomProperty.")]public bool Visible
        {
			get
			{
				return bVisible;
			}
			set
			{
				bVisible = value;
			}
		}

        [Category("Appearance"), DescriptionAttribute("Represent the Value of the CustomProperty.")]public object Value
        {
			get
			{
				if (bRef)
				{
                    if (oRef.GetType() == typeof(DataRow)  || oRef.GetType().IsSubclassOf(typeof(DataRow)))
						return this.DataColumn;
                    else
                        return Interaction.CallByName(oRef, sProp, CallType.Get, null);
				}
				else
				{
					return oValue;
				}
			}
			set
			{
				if (bRef)
				{
					if (oRef.GetType() == typeof(DataRow)  || oRef.GetType().IsSubclassOf(typeof(DataRow)))
							this.DataColumn = value;
                    else
                        Interaction.CallByName(oRef, sProp, CallType.Set, value);
				}
				else
				{
					oValue = value;
				}
			}
		}

        [Category("Appearance"), DescriptionAttribute("Set description associated with the CustomProperty.")]public string Description
        {
			get
			{
				return sDescription;
			}
			set
			{
				sDescription = value;
			}
		}

        [Category("Appearance"), DescriptionAttribute("Set category associated with the CustomProperty.")]public string Category
        {
			get
			{
				return sCategory;
			}
			set
			{
				sCategory = value;
			}
		}

        [XmlIgnore()]public System.Type Type
		{
			get
			{
				if (Value != null)
				{
					return Value.GetType();
				}
				else
				{
					if (oDefaultValue != null)
					{
						return oDefaultValue.GetType();
					}
					else
					{
						return oDefaultType;
					}
				}
            }
		}

        [XmlIgnore()]public AttributeCollection Attributes
		{
			get
			{
				return oCustomAttributes;
			}
			set
			{
				oCustomAttributes = value;
			}
		}

        [Category("Behavior"), DescriptionAttribute("Indicates if the property is browsable or not."), XmlElementAttribute(IsNullable = false)]public bool IsBrowsable
		{
			get
			{
				return bIsBrowsable;
			}
			set
			{
				bIsBrowsable = value;
				if (value == true)
				{
                    BuildAttributes_BrowsableProperty();
                }
			}
		}

		[Category("Appearance"), DisplayName("Parenthesize"), DescriptionAttribute("Indicates whether the name of the associated property is displayed with parentheses in the Properties window."), DefaultValue(false), XmlElementAttribute("Parenthesize")]public bool Parenthesize
		{
			get
			{
				return bParenthesize;
			}
			set
			{
				bParenthesize = value;
			}
		}

        [Category("Behavior"), DescriptionAttribute("Indicates the style of the label when a property is browsable."), XmlElementAttribute(IsNullable = false)]public BrowsableTypeConverter.LabelStyle BrowsableLabelStyle
        {
			get
			{
				return eBrowsablePropertyLabel;
			}
			set
			{
				bool Update = false;
				if (value != eBrowsablePropertyLabel)
				{
					Update = true;
				}
				eBrowsablePropertyLabel = value;
				if (Update)
				{
					BrowsableTypeConverter.BrowsableLabelStyleAttribute style = new BrowsableTypeConverter.BrowsableLabelStyleAttribute(value);
					oCustomAttributes = new AttributeCollection(new Attribute[] {style});
				}
			}
		}

        [Category("Behavior"), DescriptionAttribute("Indicates if the property is masked or not."), XmlElementAttribute(IsNullable = false)]public bool IsPassword
        {
			get
			{
				return bIsPassword;
			}
			set
			{
				bIsPassword = value;
			}
		}

        [Category("Behavior"), DescriptionAttribute("Indicates if the property represents a value in percentage."), XmlElementAttribute(IsNullable = false)]public bool IsPercentage
        {
			get
			{
				return bIsPercentage;
			}
			set
			{
				bIsPercentage = value;
			}
		}

        [Category("Behavior"), DescriptionAttribute("Indicates if the property uses a FileNameEditor converter."), XmlElementAttribute(IsNullable = false)]public bool UseFileNameEditor
        {
			get
			{
				return bUseFileNameEditor;
			}
			set
			{
				bUseFileNameEditor = value;
			}
		}

        [Category("Behavior"), DescriptionAttribute("Apply a filter to FileNameEditor converter."), XmlElementAttribute(IsNullable = false)]public string FileNameFilter
        {
			get
			{
				return sFilter;
			}
			set
			{
				bool UpdateAttributes = false;
				if (value != sFilter)
				{
					UpdateAttributes = true;
				}
				sFilter = value;
				if (UpdateAttributes)
				{
					BuildAttributes_FilenameEditor();
				}
			}
		}

        [Category("Behavior"), DescriptionAttribute("DialogType of the FileNameEditor."), XmlElementAttribute(IsNullable = false)]public UIFilenameEditor.FileDialogType FileNameDialogType
        {
			get
			{
				return eDialogType;
			}
			set
			{
				bool UpdateAttributes = false;
				if (value != eDialogType)
				{
					UpdateAttributes = true;
				}
				eDialogType = value;
				if (UpdateAttributes)
				{
					BuildAttributes_FilenameEditor();
				}
			}
		}

        [Category("Behavior"), DescriptionAttribute("Custom Choices list."), XmlIgnore()]public CustomChoices Choices
        {
			get
			{
				return oChoices;
			}
			set
			{
				oChoices = value;
				BuildAttributes_CustomChoices();
			}
		}

        [Category("Databinding"), XmlIgnore()]public object Datasource
        {
			get
			{
				return oDatasource;
			}
			set
			{
				oDatasource = value;
				BuildAttributes_ListboxEditor();
			}
		}

        [Category("Databinding"), XmlElementAttribute(IsNullable = false)]
        public string ValueMember
		{
			get
			{
				return sValueMember;
			}
			set
			{
				sValueMember = value;
				BuildAttributes_ListboxEditor();
			}
		}

        [Category("Databinding"), XmlElementAttribute(IsNullable = false)]
        public string DisplayMember
		{
			get
			{
				return sDisplayMember;
			}
			set
			{
				sDisplayMember = value;
				BuildAttributes_ListboxEditor();
			}
		}

        [Category("Databinding"), XmlElementAttribute(IsNullable = false)]
        public object SelectedValue
		{
			get
			{
				return oSelectedValue;
			}
			set
			{
				oSelectedValue = value;
			}
		}

        [Category("Databinding"), XmlElementAttribute(IsNullable = false)]
        public object SelectedItem
		{
			get
			{
				return oSelectedItem;
			}
			set
			{
				oSelectedItem = value;
			}
		}

        [Category("Databinding"), XmlElementAttribute(IsNullable = false)]
        public bool IsDropdownResizable
		{
			get
			{
				return bIsDropdownResizable;
			}
			set
			{
				bIsDropdownResizable = value;
				BuildAttributes_ListboxEditor();
			}
		}

        [XmlIgnore()]public UITypeEditor CustomEditor
		{
			get
			{
				return oCustomEditor;
			}
			set
			{
				oCustomEditor = value;
			}
		}

        [XmlIgnore()]public TypeConverter CustomTypeConverter
		{
			get
			{
				return oCustomTypeConverter;
			}
			set
			{
				oCustomTypeConverter = value;
			}
		}
				
		[XmlIgnore()]public object Tag
		{
			get
			{
				return oTag;
			}
			set
			{
				oTag = value;
			}
		}
		
		[XmlIgnore()]public object DefaultValue
		{
			get
			{
				return oDefaultValue;
			}
			set
			{
				oDefaultValue = value;
			}
		}
		
		[XmlIgnore()]public Type DefaultType
		{
			get
			{
				return oDefaultType;
			}
			set
			{
				oDefaultType = value;
			}
		}
		
		[XmlIgnore()]public UICustomEventEditor.OnClick OnClick
		{
			get
			{
				return MethodDelegate;
			}
			set
			{
				MethodDelegate = value;
				BuildAttributes_CustomEventProperty();
			}
		}
		
		#endregion
		
		#region "CustomPropertyDescriptor"
		public class CustomPropertyDescriptor : PropertyDescriptor
		{
			
			protected CustomProperty oCustomProperty;
			
			public CustomPropertyDescriptor(CustomProperty myProperty, Attribute[] attrs) : base(myProperty.Name, attrs)
			{
				if (myProperty == null)
				{
					oCustomProperty = null;
				}
				else
				{
					
					oCustomProperty = myProperty;
				}
			}
			
			public override bool CanResetValue(object component)
			{
				if ((oCustomProperty.DefaultValue != null)|| (oCustomProperty.DefaultType != null))
				{
					return true;
				}
				else
				{
					return false;
				}
			}
			
			public override System.Type ComponentType
			{
				get
				{
					return this.GetType();
				}
			}
			
			public override object GetValue(object component)
			{
				return oCustomProperty.Value;
			}
			
			public override bool IsReadOnly
			{
				get
				{
					return oCustomProperty.IsReadOnly;
				}
			}
			
			public override System.Type PropertyType
			{
				get
				{
					return oCustomProperty.Type;
				}
			}
			
			public override void ResetValue(object component)
			{
				oCustomProperty.Value = oCustomProperty.DefaultValue;
				this.OnValueChanged(component, EventArgs.Empty);
			}
			
			public override void SetValue(object component, object value)
			{
				oCustomProperty.Value = value;
				this.OnValueChanged(component, EventArgs.Empty);
			}
			
			public override bool ShouldSerializeValue(object component)
			{
				object oValue = oCustomProperty.Value;
				if ((oCustomProperty.DefaultValue != null)&& (oValue != null))
				{
					return ! oValue.Equals(oCustomProperty.DefaultValue);
				}
				else
				{
					return false;
				}
			}
			
			public override string Description
			{
				get
				{
					return oCustomProperty.Description;
				}
			}
			
			public override string Category
			{
				get
				{
					return oCustomProperty.Category;
				}
			}
			
			public override string DisplayName
			{
				get
				{
					return oCustomProperty.Name;
				}
			}
			
			public override bool IsBrowsable
			{
				get
				{
					return oCustomProperty.IsBrowsable;
				}
			}
			
			public object CustomProperty
			{
				get
				{
					return oCustomProperty;
				}
			}
		}
		#endregion		
	}	
}