#region Copyright and GPL
/*************************************************************************
 * dataMagus - The open source database learning software
 * Copyright (C) 2007 by all authors
 * Authors: Monika Schwitter (monsy@gmx.ch)
 *          Jürg Zgraggen (j.zgraggen@ejaz.net)
 *          HSLU (Hochschule Luzern - Technik+Architektur)
 * 
 *     This program is free software: you can redistribute it and/or modify
 *     it under the terms of the GNU General Public License as published by
 *     the Free Software Foundation, either version 3 of the License, or
 *     (at your option) any later version.
 * 
 *     This program is distributed in the hope that it will be useful,
 *     but WITHOUT ANY WARRANTY; without even the implied warranty of
 *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *     GNU General Public License for more details.
 * 
 *     You should have received a copy of the GNU General Public License
 *     along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *************************************************************************/
#endregion
#region Using
using System;
using System.Collections;
using System.Collections.Generic;
using System.Windows;
using System.Drawing;
using System.Windows.Forms;
using dataMagus.Forms;
using dataMagus.Forms.Controls;
using dataMagus.Common;
using dataMagus.Common.Logical;
using dataMagus.Common.Physical;
using dataMagus.Common.Physical.Relational;
using dataMagus.Common.Physical.Objectoriented;
using dataMagus.Common.PlugIn;
using dataMagus.Common.PlugIn.Notation;
#endregion

namespace dataMagus.Forms.Handlers
{
	/// <summary>
	/// Description of SchemaControlHandler.
	/// </summary>
    [Serializable]
	public class SchemaControlHandler
	{
		#region Members
		/// <summary>
		/// Control the handler is used for.
		/// </summary>
        [NonSerialized]
		private SchemaControl _control = null;
        
        /// <summary>
        /// Contains all element controls which are selected together. This collection
        /// will be needed to move all selected controls when one of the starts moving.
        /// </summary>
        [NonSerialized]
        private List<ElementControl> _selectedElements = new List<ElementControl>();
		#endregion
        
		#region Constructors / Destructors
		/// <summary>
		/// Constructor.
		/// </summary>
		/// <param name="control">Schema control to handle.</param>
		public SchemaControlHandler(SchemaControl control)
		{
			this._control = control;
            if (null == this._control)
			{
				throw new Exception("Parameter must not be null");
			}
		}
		#endregion
		
		#region Properties
        /// <summary>
        /// Contains all element controls which are selected together. This collection
        /// will be needed to move all selected controls when one of the starts moving.
        /// </summary>
		public List<ElementControl> SelectedElements 
		{
			get { return this._selectedElements; }
		}
		#endregion
		
		#region Methods
		/// <summary>
		/// Adjust the location of an element in the middle of two others. The location
		/// will be set between BeginInit() and EndInit() not to invoke any events.
		/// This method will mostly only be used for automatically position relationships
		/// when the generator process for the physical schema must create new entities
		/// out of relationships.
		/// </summary>
		/// <param name="elementA">Element A.</param>
		/// <param name="elementB">Element B.</param>
		/// <param name="elementInTheMiddle">Element in the middle of A and B to change the location for.</param>
		private void AdjustElementInTheMiddle(IElement elementA, IElement elementB, IElement elementInTheMiddle)
		{
			if (elementA != elementB)
			{
				elementInTheMiddle.BeginInit();
				Point newMiddle = NotationHelper.EvalMiddle(elementA.Location, elementB.Location);
				elementInTheMiddle.Location = newMiddle;
				elementInTheMiddle.EndInit(false);
			}
			else
			{
				elementInTheMiddle.BeginInit();
				elementInTheMiddle.Location = new Point(elementA.Location.X + 20, elementA.Location.Y + 20);
				elementInTheMiddle.EndInit(false);
			}
		}

		/// <summary>
		/// Create a complete schema with all controls in it.
		/// </summary>
		/// <remarks>Ensure to do a cleanup of the old schema first by using CleanUpSchema().</remarks>
		/// <param name="schema">Schema to be created.</param>
		public void CreateSchema(ISchema schema)
		{
			// handle different schemas
            if (schema is LogicalSchema)
			{
                this.CreateLogicalSchema((LogicalSchema)schema);
			}
            else if (schema is PhysicalObjectorientedSchema)
			{
                this.CreatePhysicalObjectorientedSchema((PhysicalObjectorientedSchema)schema);
			}
            else if (schema is PhysicalRelationalSchema)
			{
                this.CreatePhysicalRelationalSchema((PhysicalRelationalSchema)schema);
			}
			else
			{
				throw new NotImplementedException("This type of schema is not implemented.");
			}
		}
		
		/// <summary>
		/// Clean up the schema to create a new one.
		/// </summary>
		/// <param name="schema">Old schema to clean up.</param>
		public void CleanUpSchema(ISchema schema)
		{
			// handle different schemas
            if (schema is LogicalSchema)
			{
                this.CleanUpLogicalSchema((LogicalSchema)schema);
			}
            else if (schema is PhysicalObjectorientedSchema)
			{
                this.CleanUpPhysicalObjectorientedSchema((PhysicalObjectorientedSchema)schema);
			}
            else if (schema is PhysicalRelationalSchema)
			{
                this.CleanUpPhysicalRelationalSchema((PhysicalRelationalSchema)schema);
			}
			else
			{
				throw new NotImplementedException("This type of schema is not implemented.");
			}
			
			// remove all other
            this._control.Controls.Clear();
		}
		
		/// <summary>
		/// Handle the OnPaint event and draw the relationships into the context.
		/// </summary>
		/// <param name="context">Grahpics context.</param>
		public void HandleOnPaint(Graphics context)
		{
			// let plugin draw
            if (null != this._control.Schema)
			{
				// handle different schemas
                if (this._control.Schema is LogicalSchema)
				{
                    this.DrawLogicalRelationship((LogicalSchema)this._control.Schema, context);
				}
				else if (_control.Schema is PhysicalObjectorientedSchema)
				{
                    this.DrawPhysicalObjectorientedAssociation((PhysicalObjectorientedSchema)this._control.Schema, context);
				}
				else if (_control.Schema is PhysicalRelationalSchema)
				{
                    this.DrawPhysicalRelationalRelationship((PhysicalRelationalSchema)this._control.Schema, context);
				}
				else
				{
					throw new NotImplementedException("This type of schema is not implemented.");
				}
			}
		}
		
		/// <summary>
		/// Handle the OnSchemaChanged event and adjust the schema by the given SchemaEventArgs.
		/// </summary>
		/// <param name="args">SchemaEventArgs containging the changes.</param>
		public void HandleOnSchemaChanged(SchemaEventArgs args)
		{
			// check for type of schema (at the moment, only logical schemas can be changed)
			if (this._control.Schema is LogicalSchema)
			{
	            LogicalSchema schema = (LogicalSchema)this._control.Schema;
	            ILogicalNotation notation = (ILogicalNotation)this.GetNotationInstance(schema.NotationType);
				
				if (SchemaChangedAction.AddedElement == args.Action)
				{
					if (args.Element is LogicalEntity)
					{
						LogicalEntity entity = (LogicalEntity)args.Element;
						ElementControl ctrl = notation.GetEntityControl(entity);
						this.AddElementControl(ctrl, entity);
					}
					
					// stay informed
                    args.Element.ElementChanged += new ElementChangedHandler(this.OnElementChanged);
					
					// just stay informed about attributes (on entity or relationships)
					if (args.Element is IAttributable)
					{
						IEnumerator attrEnumerator = ((IAttributable)args.Element).GetAttributes();
	                    while (attrEnumerator.MoveNext())
	                    {
	                        LogicalAttribute attrib = (LogicalAttribute)attrEnumerator.Current;
	                        attrib.ElementChanged += new ElementChangedHandler(this.OnElementChanged);
	                    }
					}
				}
				else if (SchemaChangedAction.RemovedElement == args.Action)
				{
					if (args.Element is LogicalEntity)
					{
						this.RemoveElementControl(args.Element);
					}
					
					// stop beeing informed
                    args.Element.ElementChanged -= new ElementChangedHandler(this.OnElementChanged);
					
					// just stop beeing informed about attributes (on entity or relationships)
					if (args.Element is IAttributable)
					{
						IEnumerator attrEnumerator = ((IAttributable)args.Element).GetAttributes();
	                    while (attrEnumerator.MoveNext())
	                    {
	                        LogicalAttribute attrib = (LogicalAttribute)attrEnumerator.Current;
	                        attrib.ElementChanged -= new ElementChangedHandler(this.OnElementChanged);
	                    }
					}
				}
			}
			
			// redraw for old relationships
			this._control.Invalidate(true);
			this._control.Update();
		}
		
		/// <summary>
		/// Handle the MouseDown event and redirect it to the current notation so the notation
		/// can react on it when having control which are directly painted.
		/// </summary>
		/// <param name="e">MouseEventArgs</param>
		public void HandleMouseDown(MouseEventArgs e)
		{
			// let plugin know about the event
            if (null != this._control.Schema)
			{
	            INotation notation = this.GetNotationInstance(this._control.Schema.NotationType);
	            notation.OnMouseDown(e);
			}
		}
		
		/// <summary>
		/// Get the current notation instance by a given type. If no notation has been found
		/// the method tries to find another notation for the current schema and returns this one.
		/// </summary>
		/// <param name="type">Notation type to be found.</param>
		/// <returns>Instance of INotation or null if there exist not even a default notation.</returns>
		private INotation GetNotationInstance(Type type)
		{
			// store current loaded notation somewhere
			List<INotation> foundNotations = NotationManager.Instance.FindType(type);
			
			// return unique found notation
			if (1 == foundNotations.Count)
			{
				return foundNotations[0];
			}
			
			// load default notation not available
            foundNotations = NotationManager.Instance.FindInterfaceTypeForSchema(this._control.Schema);
			return foundNotations[0];
		}
		
		/// <summary>
		/// Add a new element control to the schema.
		/// </summary>
		/// <param name="elementCtrl">Element control to add.</param>
		/// <param name="element">Element which belongs to this control.</param>
        private void AddElementControl(ElementControl elementCtrl, IElement element)
        {
        	if (null == elementCtrl)
        	{
        		return;
        	}
        	
            if (!this._control.Controls.Contains(elementCtrl))
            {
            	// get informed when an entity is moving
            	elementCtrl.Selected += new ElementControl.SelectedHandler(this.OnElementControlSelected);
            	elementCtrl.UnSelected += new ElementControl.UnSelectedHandler(this.OnElementControlUnSelected);
            	elementCtrl.Moooving += new ElementControl.MooovingEventHandler(this.OnElementControlMooooving);
			
				// set properties
            	elementCtrl.ForceRefreshOnDrag = ApplicationOptions.Instance.ForceRefreshOnDrag;
            	elementCtrl.SnapSize = ApplicationOptions.Instance.SnapSize;
            	elementCtrl.Location = element.Location;
                
            	// add it
            	this._control.Controls.Add(elementCtrl);
            }
        }
        
        /// <summary>
        /// Remove an element control from the schema.
        /// </summary>
        /// <param name="elementCtrl">Element control to remove.</param>
        private void RemoveElementControl(ElementControl elementCtrl)
        {
            if (this._control.Controls.Contains(elementCtrl))
            {
            	// stop beeing informed when an element is moving
            	elementCtrl.Selected -= new ElementControl.SelectedHandler(this.OnElementControlSelected);
            	elementCtrl.UnSelected -= new ElementControl.UnSelectedHandler(this.OnElementControlUnSelected);
            	elementCtrl.Moooving -= new ElementControl.MooovingEventHandler(this.OnElementControlMooooving);

            	// remove it
            	this._control.Controls.Remove(elementCtrl);
            }
        }
        
        /// <summary>
        /// Remove an element control from the schema which is linked to the 
        /// given element.
        /// </summary>
        /// <param name="element">Element to search for.</param>
        private void RemoveElementControl(IElement element)
        {
        	ElementControl elementCtrl = this.FindControl(element);
        	this.RemoveElementControl(elementCtrl);
        }
		
        /// <summary>
        /// Event handler.
        /// </summary>
        /// <param name="sender">Sender object.</param>
        /// <param name="e">MouseEventArgs</param>
        private void OnElementControlMooooving(object sender, MouseEventArgs e)
        {
        	foreach (ElementControl elementCtrl in this.SelectedElements)
        	{
        		// move all other selected elements too
        		if (elementCtrl != sender)
        		{
        			elementCtrl.Location = new Point(elementCtrl.Location.X + e.X, elementCtrl.Location.Y + e.Y);
        			// set the new location for the element
	            	if (null != elementCtrl.Element)
	            	{
	        			elementCtrl.Element.Location = elementCtrl.Location;
	            	}
        		}
        		else
        		{
        			// set the location for the element
	            	if (null != elementCtrl.Element)
	            	{
	        			elementCtrl.Element.Location = elementCtrl.Location;
	            	}
        		}
        	}
        }
        
        /// <summary>
        /// Event handler.
        /// </summary>
        /// <param name="sender">Sender object.</param>
        /// <param name="e">EventArgs</param>
        private void OnElementControlSelected(object sender, EventArgs e)
        {
        	ElementControl ctrl = (ElementControl)sender;
        	if (!this.SelectedElements.Contains(ctrl))
        	{
        		this.SelectedElements.Add(ctrl);
        	}
        }
        
        /// <summary>
        /// Event handler.
        /// </summary>
        /// <param name="sender">Sender object.</param>
        /// <param name="e">EventArgs</param>
        private void OnElementControlUnSelected(object sender, EventArgs e)
        {
        	ElementControl ctrl = (ElementControl)sender;
        	if (this.SelectedElements.Contains(ctrl))
        	{
        		this.SelectedElements.Remove(ctrl);
        	}
        	
        	this.UnSelectAll();
        }
        
        /// <summary>
        /// Event handler.
        /// </summary>
        /// <param name="sender">Sender object.</param>
        /// <param name="e">EventArgs</param>
		private void OnElementChanged(object sender, ElementEventArgs e)
		{
			// data changed -> refresh control
            if (e.Element is IElement)
            {
                ElementControl ctrl = this.FindControl((IElement)e.Element);
                if (null != ctrl)
                {
                    ctrl.Size = ctrl.CalcControlSize();
                    ctrl.Invalidate();
                    ctrl.Update();
                }
                else if (null != ((IElement)e.Element).GetParent())
                {
                    IElement parent = ((IElement)e.Element).GetParent();
                    ElementControl parentCtrl = this.FindControl(parent);
                    if (null != parentCtrl)
                    {
                        parentCtrl.Size = parentCtrl.CalcControlSize();
    	                parentCtrl.Invalidate();
						parentCtrl.Update();
                    }
                    else
                    {
	                    // update if item has not parent-control (ex. attrib on a relationship)
	                    this._control.Invalidate();
	                    this._control.Update();
                    }
                }
                else
                {
                    // update all other (ex. LogicalRelationship)
                    this._control.Invalidate();
                    this._control.Update();
                }

                // add or remove events
                if (ElementChangedAction.AddedElement == e.Action)
                {
                    e.ActionElement.ElementChanged += new ElementChangedHandler(this.OnElementChanged);
                }
                else if (ElementChangedAction.RemovedElement == e.Action)
                {
                    e.ActionElement.ElementChanged -= new ElementChangedHandler(this.OnElementChanged);
                }
            }
		}
		
		/// <summary>
		/// Select all element controls on the schema.
		/// </summary>
		public void SelectAll()
		{
			foreach (Control ctrl in this._control.Controls)
			{
				if (ctrl is ElementControl)
				{
					((ElementControl)ctrl).IsSelected = true;
				}
			}
		}
		
		/// <summary>
		/// Unselect all selected element controls on the schema.
		/// </summary>
		public void UnSelectAll()
		{
			// memorize all controls to remove
			ElementControl[] toRemove = new ElementControl[this.SelectedElements.Count];
			for (int i=0; i < this.SelectedElements.Count; i++)
			{
				toRemove[i] = this.SelectedElements[i];
			}
			
			// -> don't go directly over the SelectedElements-List because chaning the
			// selected state on ElementControl can affect the list!
			foreach (ElementControl ctrl in toRemove)
			{
				ctrl.IsSelected = false;
			}
		}

		/// <summary>
		/// Find the element control corresponding to the given element.
		/// </summary>
		/// <param name="element">Element to search the control for.</param>
		/// <returns>The found control or null if nothing has been found.</returns>
		public ElementControl FindControl(IElement element)
		{
			foreach (Control ctrl in this._control.Controls)
			{
				if (ctrl is ElementControl)
				{
					ElementControl elementCtrl = (ElementControl)ctrl;
					if (null != elementCtrl.Element && elementCtrl.Element.Id == element.Id)
					{
						return elementCtrl;
					}
				}
			}
			return null;
		}
		
		#region Logical Things
		/// <summary>
		/// Create a complete logical schema with all controls in it.
		/// </summary>
		/// <remarks>Do not use this method directly. Use CreateSchema() instead.</remarks>
		/// <param name="schema">Schema to be created.</param>
		private void CreateLogicalSchema(LogicalSchema schema)
		{
            ILogicalNotation notation = (ILogicalNotation)this.GetNotationInstance(schema.NotationType);
			// create entities
            IEnumerator entitiesEnumerator = schema.GetEntities();
            while (entitiesEnumerator.MoveNext())
            {
                LogicalEntity entity = (LogicalEntity)entitiesEnumerator.Current;
                ElementControl ctrl = notation.GetEntityControl(entity);
				this.AddElementControl(ctrl, entity);
				
				// stay informed
                entity.ElementChanged += new ElementChangedHandler(this.OnElementChanged);
                IEnumerator attribEnumerator = entity.GetAttributes();
                while (attribEnumerator.MoveNext())
                {
                    LogicalAttribute attrib = (LogicalAttribute)attribEnumerator.Current;
                    attrib.ElementChanged += new ElementChangedHandler(this.OnElementChanged);
                }
			}
				
			// stay informed
            IEnumerator relEnumerator = schema.GetRelationships();
            while (relEnumerator.MoveNext())
            {
                // stay informed
                LogicalRelationship rel = (LogicalRelationship)relEnumerator.Current;
                rel.ElementChanged += new ElementChangedHandler(this.OnElementChanged);

                if (rel is IAttributable)
                {
                    IEnumerator attribEnumerator = ((IAttributable)rel).GetAttributes();
                    while (attribEnumerator.MoveNext())
                    {
                        LogicalAttribute attrib = (LogicalAttribute)attribEnumerator.Current;
                        attrib.ElementChanged += new ElementChangedHandler(this.OnElementChanged);
                    }
                }
			}
		}
		
		/// <summary>
		/// Clean up the logical schema to create a new one.
		/// </summary>
		/// <remarks>Do not use this method directly. Use CleanUpSchema() instead.</remarks>
		/// <param name="schema">Old schema to clean up.</param>
		private void CleanUpLogicalSchema(LogicalSchema schema)
		{
			// loop entities
            IEnumerator entitiesEnumerator = schema.GetEntities();
            while (entitiesEnumerator.MoveNext())
            {
                LogicalEntity entity = (LogicalEntity)entitiesEnumerator.Current;
				ElementControl ctrl = this.FindControl(entity);
				this.RemoveElementControl(ctrl);
				
				// stop beeing informed
                entity.ElementChanged -= new ElementChangedHandler(this.OnElementChanged);
                IEnumerator attribEnumerator = entity.GetAttributes();
                while (attribEnumerator.MoveNext())
                {
                    LogicalAttribute attrib = (LogicalAttribute)attribEnumerator.Current;
                    attrib.ElementChanged -= new ElementChangedHandler(this.OnElementChanged);
                }
			}
				
			// stop beeing informed
            IEnumerator relEnumerator = schema.GetRelationships();
            while (relEnumerator.MoveNext())
            {
                // stop beeing informed
                LogicalRelationship rel = (LogicalRelationship)relEnumerator.Current;
                rel.ElementChanged -= new ElementChangedHandler(this.OnElementChanged);

                if (rel is IAttributable)
                {
                    IEnumerator attribEnumerator = ((IAttributable)rel).GetAttributes();
                    while (attribEnumerator.MoveNext())
                    {
                        LogicalAttribute attrib = (LogicalAttribute)attribEnumerator.Current;
                        attrib.ElementChanged -= new ElementChangedHandler(this.OnElementChanged);
                    }
                }
			}
		}
		
		/// <summary>
		/// Draw all logical relationships from a given schema into a graphics context.
		/// </summary>
		/// <param name="schema">Schema containging the relationships.</param>
		/// <param name="context">Graphics context.</param>
		private void DrawLogicalRelationship(LogicalSchema schema, Graphics context)
		{
            ILogicalNotation notation = (ILogicalNotation)this.GetNotationInstance(schema.NotationType);
			notation.BeginDrawingRelationships();
            IEnumerator relEnumerator = schema.GetRelationships();
            while (relEnumerator.MoveNext())
            {
                LogicalRelationship rel = (LogicalRelationship)relEnumerator.Current;
                
                // set relationship location -> for physical conversion (link table)
                this.AdjustElementInTheMiddle(rel.Entity1, rel.Entity2, rel);

                ElementControl c1 = this.FindControl(rel.Entity1);
				ElementControl c2 = this.FindControl(rel.Entity2);
				if (null != c1 && null != c2)
				{
					notation.DrawRelationship(rel, c1, c2,
						ApplicationOptions.Instance.ShowRelationshipName,
						ApplicationOptions.Instance.ShowRolenames,
						ApplicationOptions.Instance.ShowCardinalities,
						context);
				}
			}
		}
		#endregion
		
		#region Physical Relational Things
		/// <summary>
		/// Create a complete physical relational schema with all controls in it.
		/// </summary>
		/// <remarks>Do not use this method directly. Use CreateSchema() instead.</remarks>
		/// <param name="schema">Schema to be created.</param>
		private void CreatePhysicalRelationalSchema(PhysicalRelationalSchema schema)
		{
			IPhysicalRelationalNotation notation = (IPhysicalRelationalNotation)this.GetNotationInstance(schema.NotationType);
			// create tables
            IEnumerator tablesEnumerator = schema.GetTables();
            while (tablesEnumerator.MoveNext())
            {
                PhysicalRelationalTable table = (PhysicalRelationalTable)tablesEnumerator.Current;
				ElementControl ctrl = notation.GetTableControl(table);
				this.AddElementControl(ctrl, table);
				
				// stay informed
                table.ElementChanged += new ElementChangedHandler(this.OnElementChanged);
                IEnumerator columnsEnumerator = table.GetColumns();
                while (columnsEnumerator.MoveNext())
                {
                    PhysicalRelationalColumn column = (PhysicalRelationalColumn)columnsEnumerator.Current;
                    column.ElementChanged += new ElementChangedHandler(this.OnElementChanged);
                }
			}
			
			// stay informed
            IEnumerator relEnumerator = schema.GetRelationships();
            while (relEnumerator.MoveNext())
            {
                PhysicalRelationalRelationship rel = (PhysicalRelationalRelationship)relEnumerator.Current;
                rel.ElementChanged += new ElementChangedHandler(this.OnElementChanged);
			}
		}
		
		/// <summary>
		/// Clean up the physical relational schema to create a new one.
		/// </summary>
		/// <remarks>Do not use this method directly. Use CleanUpSchema() instead.</remarks>
		/// <param name="schema">Old schema to clean up.</param>
		private void CleanUpPhysicalRelationalSchema(PhysicalRelationalSchema schema)
		{
			// loop tables
            IEnumerator tablesEnumerator = schema.GetTables();
            while (tablesEnumerator.MoveNext())
            {
                PhysicalRelationalTable table = (PhysicalRelationalTable)tablesEnumerator.Current;
				ElementControl ctrl = this.FindControl(table);
				this.RemoveElementControl(ctrl);
				
				// stop beeing informed
                table.ElementChanged -= new ElementChangedHandler(this.OnElementChanged);
                IEnumerator columnsEnumerator = table.GetColumns();
                while (columnsEnumerator.MoveNext())
                {
                    PhysicalRelationalColumn column = (PhysicalRelationalColumn)columnsEnumerator.Current;
                    column.ElementChanged -= new ElementChangedHandler(this.OnElementChanged);
                }
			}
			
			// stop beeing informed
            IEnumerator relEnumerator = schema.GetRelationships();
            while (relEnumerator.MoveNext())
            {
                PhysicalRelationalRelationship rel = (PhysicalRelationalRelationship)relEnumerator.Current;
                rel.ElementChanged -= new ElementChangedHandler(this.OnElementChanged);
			}
		}

		/// <summary>
		/// Draw all physical relational relationships from a given schema into a graphics context.
		/// </summary>
		/// <param name="schema">Schema containging the relationships.</param>
		/// <param name="context">Graphics context.</param>
		private void DrawPhysicalRelationalRelationship(PhysicalRelationalSchema schema, Graphics context)
		{
            IPhysicalRelationalNotation notation = (IPhysicalRelationalNotation)this.GetNotationInstance(schema.NotationType);
			notation.BeginDrawingRelationships();
            IEnumerator relEnumerator = schema.GetRelationships();
            while (relEnumerator.MoveNext())
            {
                PhysicalRelationalRelationship rel = (PhysicalRelationalRelationship)relEnumerator.Current;

				ElementControl c1 = this.FindControl(rel.Table1);
				ElementControl c2 = this.FindControl(rel.Table2);
				if (null != c1 && null != c2)
				{
					notation.DrawRelationship(rel, c1, c2, 
						ApplicationOptions.Instance.ShowRelationshipName,
						ApplicationOptions.Instance.ShowRolenames,
						ApplicationOptions.Instance.ShowCardinalities,
						context);
				}
			}
		}
		#endregion
		
		#region Physical Objectoriented Things
		/// <summary>
		/// Create a complete physical objectoriented schema with all controls in it.
		/// </summary>
		/// <remarks>Do not use this method directly. Use CreateSchema() instead.</remarks>
		/// <param name="schema">Schema to be created.</param>
		private void CreatePhysicalObjectorientedSchema(PhysicalObjectorientedSchema schema)
		{
            IPhysicalObjectorientedNotation notation = (IPhysicalObjectorientedNotation)this.GetNotationInstance(schema.NotationType);
			// create classes
            IEnumerator classEnumerator = schema.GetClasses();
            while (classEnumerator.MoveNext())
            {
                PhysicalObjectorientedClass classItem = (PhysicalObjectorientedClass)classEnumerator.Current;
				ElementControl ctrl = notation.GetClassControl(classItem);
				this.AddElementControl(ctrl, classItem);
				
				// stay informed
                classItem.ElementChanged += new ElementChangedHandler(this.OnElementChanged);
                IEnumerator propEnumerator = classItem.GetProperties();
                while (propEnumerator.MoveNext())
                {
                    PhysicalObjectorientedProperty propItem = (PhysicalObjectorientedProperty)propEnumerator.Current;
                    propItem.ElementChanged += new ElementChangedHandler(this.OnElementChanged);
				}
			}
			
			// stay informed
            IEnumerator assocEnumerator = schema.GetAssociations();
            while (assocEnumerator.MoveNext())
            {
                PhysicalObjectorientedAssociation assocItem = (PhysicalObjectorientedAssociation)assocEnumerator.Current;
                assocItem.ElementChanged += new ElementChangedHandler(this.OnElementChanged);
            }
		}
		
		/// <summary>
		/// Clean up the physical objectoriented schema to create a new one.
		/// </summary>
		/// <remarks>Do not use this method directly. Use CleanUpSchema() instead.</remarks>
		/// <param name="schema">Old schema to clean up.</param>
		private void CleanUpPhysicalObjectorientedSchema(PhysicalObjectorientedSchema schema)
		{
			// loop classes
            IEnumerator classEnumerator = schema.GetClasses();
            while (classEnumerator.MoveNext())
            {
                PhysicalObjectorientedClass classItem = (PhysicalObjectorientedClass)classEnumerator.Current;
				ElementControl ctrl = this.FindControl(classItem);
				this.RemoveElementControl(ctrl);
				
				// stop beeing informed
                classItem.ElementChanged -= new ElementChangedHandler(this.OnElementChanged);
                IEnumerator propEnumerator = classItem.GetProperties();
                while (propEnumerator.MoveNext())
                {
                    PhysicalObjectorientedProperty propItem = (PhysicalObjectorientedProperty)propEnumerator.Current;
                    propItem.ElementChanged -= new ElementChangedHandler(this.OnElementChanged);
				}
			}
			
			// stop beeing informed
            IEnumerator assocEnumerator = schema.GetAssociations();
            while (assocEnumerator.MoveNext())
            {
                PhysicalObjectorientedAssociation assocItem = (PhysicalObjectorientedAssociation)assocEnumerator.Current;
                assocItem.ElementChanged -= new ElementChangedHandler(this.OnElementChanged);
            }
		}

		/// <summary>
		/// Draw all physical objectoriented associations from a given schema into a graphics context.
		/// </summary>
		/// <param name="schema">Schema containging the associations.</param>
		/// <param name="context">Graphics context.</param>
		private void DrawPhysicalObjectorientedAssociation(PhysicalObjectorientedSchema schema, Graphics context)
		{
            IPhysicalObjectorientedNotation notation = (IPhysicalObjectorientedNotation)this.GetNotationInstance(schema.NotationType);
			notation.BeginDrawingAssociations();
            IEnumerator assocEnumerator = schema.GetAssociations();
            while (assocEnumerator.MoveNext())
            {
                PhysicalObjectorientedAssociation assoc = (PhysicalObjectorientedAssociation)assocEnumerator.Current;

                ElementControl c1 = this.FindControl(assoc.Class1);
				ElementControl c2 = this.FindControl(assoc.Class2);
				if (null != c1 && null != c2)
				{
					notation.DrawAssociation(assoc, c1, c2, 
						ApplicationOptions.Instance.ShowRelationshipName,
						ApplicationOptions.Instance.ShowRolenames,
						ApplicationOptions.Instance.ShowCardinalities,
						context);
				}
			}
		}
		#endregion
		#endregion
	}
}
