//
// System.ComponentModel.PropertyDescriptor.cs
//
// Author:
//  Miguel de Icaza (miguel@ximian.com)
//  Andreas Nahr (ClassDevelopment@A-SoftTech.com)
//  Ivan N. Zlatev (contact@i-nz.net)
//
// (C) Ximian, Inc.  http://www.ximian.com
// (C) 2003 Andreas Nahr
// (C) 2008 Novell, Inc.  http://www.novell.com
//

//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
// 
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//

using System;
using System.Collections;
using System.Reflection;
using System.Runtime.InteropServices;

namespace System.ComponentModel
{
	[ComVisible (true)]
	public abstract class PropertyDescriptor : MemberDescriptor
	{
		TypeConverter converter;

		protected PropertyDescriptor (MemberDescriptor reference)
		: base (reference)
		{
		}

		protected PropertyDescriptor (MemberDescriptor reference, Attribute [] attrs)
		: base (reference, attrs)
		{
		}

		protected PropertyDescriptor (string name, Attribute [] attrs)
		: base (name, attrs)
		{
		}

		public abstract Type ComponentType { get; }

		public virtual TypeConverter Converter {
			get {
				if (converter == null && PropertyType != null) {
					TypeConverterAttribute at = (TypeConverterAttribute) Attributes [typeof(TypeConverterAttribute)];
					if (at != null && at != TypeConverterAttribute.Default) {
						Type converterType = GetTypeFromName (at.ConverterTypeName);
						if (converterType != null && typeof (TypeConverter).IsAssignableFrom (converterType))
							converter = (TypeConverter)CreateInstance (converterType);
					}
					if (converter == null)
						converter = TypeDescriptor.GetConverter (PropertyType);
				}
				return converter;
			}
		}

		public virtual bool IsLocalizable {
			get {
				foreach (Attribute attr in AttributeArray){
					if (attr is LocalizableAttribute)
						return ((LocalizableAttribute) attr).IsLocalizable;
				}

				return false;
			}
		}

		public abstract bool IsReadOnly { get; }

		public abstract Type PropertyType { get; }

#if NET_2_0
		public virtual bool SupportsChangeEvents {
			get { return false; }
		}
#endif

		public DesignerSerializationVisibility SerializationVisibility {
			get {
				foreach (Attribute attr in AttributeArray) {
					if (attr is DesignerSerializationVisibilityAttribute){
						DesignerSerializationVisibilityAttribute a;

						a = (DesignerSerializationVisibilityAttribute) attr;

						return a.Visibility;
					}
				}

				return DesignerSerializationVisibility.Visible;
			}
		}

		Hashtable notifiers;

		public virtual void AddValueChanged (object component, EventHandler handler)
		{
			EventHandler component_notifiers;

			if (component == null)
				throw new ArgumentNullException ("component");

			if (handler == null)
				throw new ArgumentNullException ("handler");

			if (notifiers == null)
				notifiers = new Hashtable ();

			component_notifiers = (EventHandler) notifiers [component];

			if (component_notifiers != null) {
				component_notifiers += handler;
				notifiers [component] = component_notifiers;
			} else {
				notifiers [component] = handler;
			}
		}

		public virtual void RemoveValueChanged (object component, System.EventHandler handler)
		{
			EventHandler component_notifiers;

			if (component == null)
				throw new ArgumentNullException ("component");

			if (handler == null)
				throw new ArgumentNullException ("handler");

			if (notifiers == null) return;

			component_notifiers = (EventHandler) notifiers [component];
			component_notifiers -= handler;
			
			if (component_notifiers == null)
				notifiers.Remove (component);
			else
				notifiers [component] = component_notifiers;
		}

#if NET_2_0
		protected override void FillAttributes (IList attributeList)
		{
			base.FillAttributes (attributeList);
		}

		protected override object GetInvocationTarget (Type type, object instance)
		{
			if (type == null)
				throw new ArgumentNullException ("type");
			if (instance == null)
				throw new ArgumentNullException ("instance");

			if (instance is CustomTypeDescriptor) {
				CustomTypeDescriptor ctd = (CustomTypeDescriptor) instance;
				return ctd.GetPropertyOwner (this);
			}

			return base.GetInvocationTarget (type, instance);
		}

		protected internal EventHandler GetValueChangedHandler (object component)
		{
			if (component == null || notifiers == null)
				return null;

			return (EventHandler) notifiers [component];
		}
#endif

		protected virtual void OnValueChanged (object component, EventArgs e)
		{
			if (notifiers == null)
				return;

			EventHandler component_notifiers = (EventHandler) notifiers [component];

			if (component_notifiers == null)
				return;

			component_notifiers (component, e);
		}

		public abstract object GetValue (object component);

		public abstract void SetValue (object component, object value);

		public abstract void ResetValue (object component);

		public abstract bool CanResetValue (object component);

		public abstract bool ShouldSerializeValue (object component);

		protected object CreateInstance (Type type)
		{
			if (type == null || PropertyType == null)
				return null;

			object instance = null;
			Type[] paramTypes = new Type[] { typeof (Type) };
			ConstructorInfo ctor = type.GetConstructor (paramTypes);
			if (ctor != null) {
				object[] parameters = new object[] { PropertyType };
#if NET_2_0
				instance = TypeDescriptor.CreateInstance (null, type, paramTypes, parameters);
#else
				instance = ctor.Invoke (parameters);
#endif
			} else {
#if NET_2_0
				instance = TypeDescriptor.CreateInstance (null, type, null, null);
#else
				instance = Activator.CreateInstance (type);
#endif
			}
			return instance;
		}

		public override bool Equals(object obj)
		{
			if (!base.Equals (obj)) return false;
			PropertyDescriptor other = obj as PropertyDescriptor;
			if (other == null) return false;
			return other.PropertyType == PropertyType;
		}

		public PropertyDescriptorCollection GetChildProperties()
		{
			return GetChildProperties (null, null);
		}

		public PropertyDescriptorCollection GetChildProperties(object instance)
		{
			return GetChildProperties (instance, null);
		}

		public PropertyDescriptorCollection GetChildProperties(Attribute[] filter)
		{
			return GetChildProperties (null, filter);
		}

		public override int GetHashCode() 
		{
			return base.GetHashCode ();
		}

		public virtual PropertyDescriptorCollection GetChildProperties (object instance, Attribute[] filter)
		{
			return TypeDescriptor.GetProperties (instance, filter);
		}

		public virtual object GetEditor (Type editorBaseType)
		{
			Type t = null;
			Attribute [] atts = AttributeArray;
			
			if (atts != null && atts.Length != 0) {
				foreach (Attribute a in atts) {
					EditorAttribute ea = a as EditorAttribute;
					
					if (ea == null)
						continue;
					
					t = GetTypeFromName (ea.EditorTypeName);
					if (t != null && t.IsSubclassOf(editorBaseType))
						break;
				}
			}

			object editor = null;
			if (t != null)
				editor = CreateInstance (t);
			if (editor == null)
				editor = TypeDescriptor.GetEditor (PropertyType, editorBaseType);
			return editor;
		}

		protected Type GetTypeFromName (string typeName)
		{
			if (typeName == null || ComponentType == null || typeName.Trim ().Length == 0)
				return null;

			Type type = Type.GetType (typeName);
			if (type == null) {
				// Try to strip the type typeName only
				int index = typeName.IndexOf (",");
				if (index != -1)
					typeName = typeName.Substring (0, index);
				type = ComponentType.Assembly.GetType (typeName);
			} 
			return type;
		}
	}
}


