//
// System.ComponentModel.Design.ComponentDesigner
//
// Authors:
//	  Ivan N. Zlatev (contact i-nZ.net)
//
// (C) 2006-2007 Ivan N. Zlatev

//
// 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.ComponentModel;

namespace System.ComponentModel.Design
{

	public class ComponentDesigner : ITreeDesigner, IDesigner, IDisposable, IDesignerFilter, IComponentInitializer
	{

#region ShadowPropertyCollection

		protected sealed class ShadowPropertyCollection
		{

			private Hashtable _properties = null;
			private IComponent _component;

			internal ShadowPropertyCollection (IComponent component)
			{
				_component = component;
			}

			// Returns Control's property value (if available) if there is no shadowed one.
			//
			public object this[string propertyName]
			{
				get {
					if (propertyName == null)
						throw new System.ArgumentNullException("propertyName");

					if (_properties != null && _properties.ContainsKey (propertyName))
						return _properties[propertyName];

					PropertyDescriptor property = TypeDescriptor.GetProperties (_component.GetType ())[propertyName];
					if (property != null)
						return property.GetValue (_component);
					else
						throw new System.Exception ("Propery not found!");
				}
				set {
					if (_properties == null)
						_properties = new Hashtable ();
					_properties[propertyName] = value;
				}
			}

			public bool Contains (string propertyName)
			{
				if (_properties != null)
					return _properties.ContainsKey (propertyName);
				else
					return false;
			}

		} // ShadowPropertyCollection
#endregion

		public ComponentDesigner ()
		{
		}


		private IComponent _component;
		private DesignerVerbCollection _verbs;
		private ShadowPropertyCollection _shadowPropertyCollection;
		private DesignerActionListCollection _designerActionList;

		// This property indicates any components to copy or move along with the component managed
		// by the designer during a copy, drag, or move operation.
		// If this collection contains references to other components in the current design mode document,
		// those components will be copied along with the component managed by the designer during a copy operation.
		// When the component managed by the designer is selected, this collection is filled with any nested controls.
		// This collection can also include other components, such as the buttons of a toolbar.
		//
		// supposedly contains all the children of the component, thus used for ITreeDesigner.Children
		//
		public virtual ICollection AssociatedComponents {
			get { return new IComponent[0]; }
		}

		public IComponent Component {
			get { return _component; }
		}

		public virtual DesignerVerbCollection Verbs {
			get {
				if (_verbs == null)
					_verbs = new DesignerVerbCollection ();

				return _verbs;
			}
		}

		protected virtual InheritanceAttribute InheritanceAttribute {
			get {
				IInheritanceService service = (IInheritanceService) this.GetService (typeof (IInheritanceService));
				if (service != null)
					return service.GetInheritanceAttribute (_component);
				else
					return InheritanceAttribute.Default;
			}
		}

		protected bool Inherited {
			get { return !this.InheritanceAttribute.Equals (InheritanceAttribute.NotInherited); }
		}

		//Gets a collection of property values that override user settings.
		//
		protected ShadowPropertyCollection ShadowProperties {
			get {
				if (_shadowPropertyCollection == null) {
					_shadowPropertyCollection = new ShadowPropertyCollection(_component);
				}
				return _shadowPropertyCollection;
			}
		}

		public virtual DesignerActionListCollection ActionLists {
			get {
				if (_designerActionList == null)
					_designerActionList = new DesignerActionListCollection ();

				return _designerActionList;
			}
		}

		protected virtual IComponent ParentComponent {
			get {
				IDesignerHost host = GetService (typeof (IDesignerHost)) as IDesignerHost;
				if (host != null) {
					IComponent rootComponent = host.RootComponent;
					if (rootComponent != _component)
						return rootComponent;
				}
				return null;
			}
		}

		public virtual void InitializeNewComponent (IDictionary defaultValues)
		{
			// Reset
			//
			OnSetComponentDefaults ();
		}

		// MSDN: The default implementation of this method does nothing.
		//
		public virtual void InitializeExistingComponent (IDictionary defaultValues)
		{
			InitializeNonDefault ();
		}


		public virtual void Initialize (IComponent component)
		{
			if (component == null)
				throw new ArgumentNullException ("component");
								
			_component = component;
		}

		[Obsolete ("This method has been deprecated. Use InitializeExistingComponent instead.")]
		public virtual void InitializeNonDefault ()
		{
		}


		// This method is called when a user double-clicks (the representation of) a component.
		// Tries to bind the default event to a method or creates a new one.
		// 
		public virtual void DoDefaultAction()
		{
			IDesignerHost host = (IDesignerHost) this.GetService(typeof(IDesignerHost));
			DesignerTransaction transaction = null;
			if (host != null)
				transaction = host.CreateTransaction ("ComponentDesigner_AddEvent");

			IEventBindingService eventBindingService = GetService (typeof(IEventBindingService)) as IEventBindingService;
			EventDescriptor defaultEventDescriptor = null;

			if (eventBindingService != null) {
				ISelectionService selectionService = this.GetService (typeof (ISelectionService)) as ISelectionService;
				try {
					if (selectionService != null) {
						ICollection selectedComponents = selectionService.GetSelectedComponents ();

						foreach (IComponent component in selectedComponents) {
							EventDescriptor eventDescriptor = TypeDescriptor.GetDefaultEvent (component);
							if (eventDescriptor != null) {
								PropertyDescriptor eventProperty = eventBindingService.GetEventProperty (eventDescriptor);
								if (eventProperty != null && !eventProperty.IsReadOnly) {
									string methodName = eventProperty.GetValue (component) as string;
									bool newMethod = true;

									if (methodName != null || methodName != String.Empty) {
										ICollection compatibleMethods = eventBindingService.GetCompatibleMethods (eventDescriptor);
										foreach (string signature in compatibleMethods) {
											if (signature == methodName) {
												newMethod = false;
												break;
											}
										}
									}
									if (newMethod) {
										if (methodName == null)
											methodName = eventBindingService.CreateUniqueMethodName (component, eventDescriptor);
															
										eventProperty.SetValue (component, methodName);
									}

									if (component == _component)
										defaultEventDescriptor = eventDescriptor;
								}
							}
						}

					}
				}
				catch {
					if (transaction != null) {
						transaction.Cancel ();
						transaction = null;
					}
				}
				finally {
					if (transaction != null)
						transaction.Commit ();
				}

				if (defaultEventDescriptor != null)
					eventBindingService.ShowCode (_component, defaultEventDescriptor);
			}
		}



		[Obsolete ("This method has been deprecated. Use InitializeNewComponent instead.")]
		// The default implementation of this method sets the default property of the component to
		// the name of the component if the default property is a string and the property is not already set.
		// This method can be implemented in a derived class to customize the initialization of the component
		// that this designer is designing.
		//
		public virtual void OnSetComponentDefaults ()
		{
			if (_component != null && _component.Site != null) {
				PropertyDescriptor property = TypeDescriptor.GetDefaultProperty (_component);
				if (property != null && property.PropertyType.Equals (typeof (string))) {
					string propertyValue = (string)property.GetValue (_component);
					if (propertyValue != null && propertyValue.Length != 0)
						property.SetValue (_component, _component.Site.Name);
				}
			}
		}




		protected InheritanceAttribute InvokeGetInheritanceAttribute (ComponentDesigner toInvoke)
		{
			return toInvoke.InheritanceAttribute;
		}

#region IDesignerFilter

		// TypeDescriptor queries the component's site for ITypeDescriptorFilterService 
		// then invokes ITypeDescriptorFilterService.XXXX before retrieveing props/event/attributes, 
		// which then invokes the IDesignerFilter implementation of the component
		// 
		protected virtual void PostFilterAttributes (IDictionary attributes)
		{
		}

		protected virtual void PostFilterEvents (IDictionary events)
		{
		}

		protected virtual void PostFilterProperties (IDictionary properties)
		{
		}

		protected virtual void PreFilterAttributes (IDictionary attributes)
		{
		}

		protected virtual void PreFilterEvents (IDictionary events)
		{
		}

		protected virtual void PreFilterProperties (IDictionary properties)
		{
		}
#endregion

		protected void RaiseComponentChanged (MemberDescriptor member, object oldValue, object newValue)
		{
			IComponentChangeService service = GetService (typeof (IComponentChangeService)) as IComponentChangeService;
			if (service != null)
				service.OnComponentChanged (_component, member, oldValue, newValue);
		}

		protected void RaiseComponentChanging (MemberDescriptor member)
		{
			IComponentChangeService service = GetService (typeof (IComponentChangeService)) as IComponentChangeService;
			if (service != null)
				service.OnComponentChanging (_component, member);
		}

#region Implementation of IDesignerFilter

		void IDesignerFilter.PostFilterAttributes (IDictionary attributes)
		{
			PostFilterAttributes (attributes);
		}

		void IDesignerFilter.PostFilterEvents (IDictionary events)
		{
			PostFilterEvents (events);
		}

		void IDesignerFilter.PostFilterProperties (IDictionary properties)
		{
			PostFilterProperties (properties);
		}

		void IDesignerFilter.PreFilterAttributes (IDictionary attributes)
		{
			PreFilterAttributes (attributes);
		}

		void IDesignerFilter.PreFilterEvents (IDictionary events)
		{
			PreFilterEvents (events);
		}

		void IDesignerFilter.PreFilterProperties (IDictionary properties)
		{
			PreFilterProperties (properties);
		}

#endregion


#region ITreeDesigner
		// Returns a collection of the designers of the associated components
		//
		ICollection ITreeDesigner.Children {
			get {
				ICollection components = this.AssociatedComponents;
				IDesignerHost host = GetService (typeof (IDesignerHost)) as IDesignerHost;
				
				if (host != null) {
					ArrayList designers = new ArrayList ();
					foreach (IComponent component in components) {
						IDesigner designer = host.GetDesigner (component);
						if (designer != null)
							designers.Add (designer);
					}
					IDesigner[] result = new IDesigner[designers.Count];
					designers.CopyTo (result);
					return result;
				}
				return new IDesigner[0];
			}
		}

		IDesigner ITreeDesigner.Parent {
			get {
				IDesignerHost host = GetService (typeof (IDesignerHost)) as IDesignerHost;
				if (host != null && this.ParentComponent != null)
					return host.GetDesigner (this.ParentComponent);
	
				return null;
			}
		}
#endregion

		// Helper method - not an ISerivceProvider
		//
		protected virtual object GetService (Type service)
		{
			if (_component != null && _component.Site != null)
				return _component.Site.GetService (service);

			return null;
		}

		public void Dispose ()
		{
			this.Dispose (true);
			GC.SuppressFinalize (this);
		}


		protected virtual void Dispose (bool disposing)
		{
			if (disposing)
				_component = null;
		}

		~ComponentDesigner ()
		{
			this.Dispose (false);
		}
	}
}
