#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 System.Reflection;
using dataMagus.Forms;
using dataMagus.Forms.Elements;
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 SchemaNavigationControlHandler.
	/// </summary>
    [Serializable]
	public class SchemaNavigationControlHandler
	{
		#region Structures
		/// <summary>
		/// This structure can hold additional information about a node.
		/// </summary>
		public struct NodeInfo
		{
			/// <summary>
			/// Defines an empty NodeInfo structure to be used for initializing or
			/// comparing.
			/// </summary>
			public static readonly NodeInfo Empty = new NodeInfo(null, null, false, null);
			
			/// <summary>
			/// Element object.
			/// </summary>
			public object Element;
			
			/// <summary>
			/// Identifyer for nodes identification.
			/// </summary>
			public string Identifyer;
			
			/// <summary>
			/// Indicator if the node is a group.
			/// </summary>
			public bool IsGroup;
			
			/// <summary>
			/// Additional data.
			/// </summary>
			public object Data;

			/// <summary>
			/// 
			/// </summary>
			/// <param name="element">Element object.</param>
			/// <param name="identifyer">Identifyer for nodes identification..</param>
			/// <param name="isGroup">Indicator if the node is a group.</param>
			/// <param name="data">Additional data.</param>
			public NodeInfo(object element, string identifyer, bool isGroup, object data)
			{
				this.Element = element;
				this.Identifyer = identifyer;
				this.IsGroup = isGroup;
				this.Data = data;
			}
			
			/// <summary>
	        /// Check if a node info is equal to another one. Equality is when then
	        /// all elements in the node info are equal.
	        /// </summary>
	        /// <param name="obj">Object to compare.</param>
	        /// <returns>true if the object is equal, otherwise false.</returns>
			public override bool Equals(object obj)
			{
				if (obj is NodeInfo)
				{
					NodeInfo other = (NodeInfo)obj;
					return this.Element == other.Element
						&& this.Identifyer == other.Identifyer
						&& this.IsGroup == other.IsGroup
						&& this.Data == other.Data;
				}
				return false;
			}
			
			/// <summary>
			/// Returns a hashcode for the node info.
			/// </summary>
			/// <remarks>Beware: This hashcode may not be unique.</remarks>
			/// <returns>Hashcode.</returns>
			public override int GetHashCode()
			{
				return base.GetHashCode();
			}
		}
		#endregion
		
		#region Constants
		private const int NoImageIndex = 0;
		private const int DottedGroup = 1;
		private const int DottedLine = 2;
		private const int EntityImageIndex = 3;
		private const int TableImageIndex = 4;
		private const int ClassImageIndex = 5;
		private const int RelationshipImageIndex = 6;
		private const int IndexImageIndex = 7;
		private const int AttributeImageIndex = 8;
		private const int DataTypeImageIndex = 9;
        
        private const int EntityGroupNodeIndex = 0;
        private const int RelationshipGroupNodeIndex = 1;
        private const int AttributeGroupNodeIndex = 0;
        private const int TableRelationshipGroupNodeIndex = 1;
        private const int TableIndexGroupNodeIndex = 1;
        private const int ClassAssociationGroupNodeIndex = 1;
        private const int DataTypeGroupNodeIndex = 2;

        private const string EntityGroup = "EntityGroup";
        private const string AttributeGroup = "AttributeGroup";
        private const string RelationshipGroup = "RelationshipGroup";
        private const string TableGroup = "TableGroup";
        private const string TableRelationshipGroup = "TableRelationshipGroup";
        private const string IndexGroup = "IndexGroup";
        private const string ColumnGroup = "ColumnGroup";
        private const string ClassGroup = "ClassGroup";
        private const string PropertyGroup = "PropertyGroup";
        private const string AssociationGroup = "AssociationGroup";
        private const string ClassAssociationGroup = "ClassAssociationGroup";
        private const string DataTypeGroup = "DataTypeGroup";
		#endregion
		
		#region Members
		/// <summary>
		/// Control the handler is used for.
		/// </summary>
        [NonSerialized]
		private SchemaNavigationControl _control = null;
        
		/// <summary>
		/// Tree view control the handler must handle.
		/// </summary>
		[NonSerialized]
        private TreeView _tree = null;
        
        /// <summary>
        /// Delegated building schema tree handling.
        /// </summary>
        [NonSerialized]
		private BuildSchemaTreeHandler _buildSchemaTree;
        
		/// <summary>
        /// Delegated building context menu handling.
		/// </summary>
		[NonSerialized]
		private BuildContextMenuHandler _buildContextMenu;
		#endregion

		#region Delegates
		/// <summary>
		/// Handler when the schema tree will be built.
		/// </summary>
		public delegate void BuildSchemaTreeHandler(ISchema schema);
		
		/// <summary>
		/// Handler when the context menu will be built based on the selected node.
		/// </summary>
		public delegate void BuildContextMenuHandler(ContextMenuStrip contextMenu, TreeNode selectedNode);
		#endregion

		#region Constructors / Destructors
		/// <summary>
		/// Constructor.
		/// </summary>
		/// <param name="control">Control the handler is used for.</param>
		/// <param name="tree">Tree view control the handler must handle.</param>
		public SchemaNavigationControlHandler(SchemaNavigationControl control, TreeView tree)
		{
            this._control = control;
            this._tree = tree;
            if (null == this._control || null == this._tree)
			{
				throw new Exception("Parameter must not be null");
			}
		}
		#endregion

		#region Properties
        /// <summary>
        /// Delegated building schema tree handling.
        /// </summary>
		public BuildSchemaTreeHandler BuildSchemaTree
		{
			get { return _buildSchemaTree; }
		}

		/// <summary>
        /// Delegated building context menu handling.
		/// </summary>
		public BuildContextMenuHandler BuildContextMenu 
		{
			get { return _buildContextMenu; }
		}
		#endregion
		
		#region Methods
		/// <summary>
		/// Handle the OnSchemaChanged event and adjust the tree 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;
	            
	            if (SchemaChangedAction.AddedElement == args.Action)
	            {
	                if (args.Element is LogicalEntity)
	                {
	                    LogicalEntity entity = (LogicalEntity)args.Element;
	                    TreeNode entityNd = this.CreateElementNode(entity, SchemaNavigationControlHandler.DottedGroup,
							SchemaNavigationControlHandler.AttributeGroup, SchemaNavigationControlHandler.AttributeImageIndex);
	                    this._tree.Nodes[SchemaNavigationControlHandler.EntityGroupNodeIndex].Nodes.Add(entityNd);
	                    this._tree.SelectedNode = entityNd;
	                    
	                    // stay informed
	                    IEnumerator attrEnumerator = entity.GetAttributes();
	                    while (attrEnumerator.MoveNext())
	                    {
	                        LogicalAttribute attrib = (LogicalAttribute)attrEnumerator.Current;
	                	    TreeNode attribNd = this.CreateElementNode(attrib, SchemaNavigationControlHandler.DottedLine);
	                	    entityNd.Nodes[SchemaNavigationControlHandler.AttributeGroupNodeIndex].Nodes.Add(attribNd);
	                    }
	                }
	                else if (args.Element is LogicalRelationship)
	                {
	                    LogicalRelationship rel = (LogicalRelationship)args.Element;
	                    string group = (rel is IAttributable ? SchemaNavigationControlHandler.AttributeGroup : null);
	                    int imageIndex = (rel is IAttributable ? SchemaNavigationControlHandler.DottedGroup : SchemaNavigationControlHandler.DottedLine);
	                    TreeNode relNd = this.CreateElementNode(rel, imageIndex, group, SchemaNavigationControlHandler.AttributeImageIndex);
	                    this._tree.Nodes[SchemaNavigationControlHandler.RelationshipGroupNodeIndex].Nodes.Add(relNd);
	                    this._tree.SelectedNode = relNd;
	
	                    // stay informed
	                    if (rel is IAttributable)
	                    {
	                        IEnumerator attrEnumerator = ((IAttributable)rel).GetAttributes();
	                        while (attrEnumerator.MoveNext())
	                        {
	                            LogicalAttribute attrib = (LogicalAttribute)attrEnumerator.Current;
		                	    TreeNode attribNd = this.CreateElementNode(attrib, SchemaNavigationControlHandler.DottedLine);
		                	    relNd.Nodes[SchemaNavigationControlHandler.AttributeGroupNodeIndex].Nodes.Add(attribNd);
	                        }
	                    }
	                }
	            }
	            else if (SchemaChangedAction.RemovedElement == args.Action)
	            {
	            	this.RemoveElementNode(args.Element);
	            }
        	}
        }

        /// <summary>
        /// Set all specified handlers depending on the set schema of the control.
        /// </summary>
		public void SetSchemaHandler()
		{
			if (null == _control.Schema)
			{
				throw new Exception("Schema property on SchemaControl must not be null");
			}
			
			// clear old handler items
			this.CleanUpSchemaTree();
			
			// handle different schemas
            if (this._control.Schema is LogicalSchema)
			{
				this._buildSchemaTree = this.BuildLogicalSchemaTree;
				this._buildContextMenu = this.BuildLogicalContextMenu;
			}
            else if (this._control.Schema is PhysicalRelationalSchema)
			{
				this._buildSchemaTree = this.BuildPhysicalRelationalSchemaTree;
				this._buildContextMenu = this.BuildPhysicalRelationalContextMenu;
			}
            else if (this._control.Schema is PhysicalObjectorientedSchema)
			{
				this._buildSchemaTree = this.BuildPhysicalObjectorientedSchemaTree;
				this._buildContextMenu = this.BuildPhysicalObjectorientedContextMenu;
			}
			else
			{
				throw new NotImplementedException("This type of schema is not implemented.");
			}
		}
	
        /// <summary>
        /// Event handler.
        /// </summary>
        /// <param name="sender">Sender object.</param>
        /// <param name="e">EventArgs</param>
		private void OnElementChanged(object sender, ElementEventArgs e)
		{
			try
			{
	            if (e.Element is IElement)
	            {
	                TreeNode elementNode = this.FindTreeNode((IElement)e.Element);
	                if (ElementChangedAction.AddedElement == e.Action)
	                {
	                    // create attributes for entity and relationships
	                    if (e.ActionElement is LogicalAttribute)
	                    {
	                        TreeNode attribNd = this.CreateElementNode(e.ActionElement, SchemaNavigationControlHandler.DottedLine);
	                        TreeNode nd = this.FindTreeNode((IElement)e.Element);
	                        nd.Nodes[SchemaNavigationControlHandler.AttributeGroupNodeIndex].Nodes.Add(attribNd);
	                        this._tree.SelectedNode = attribNd;
	                    }
	                }
	                else if (ElementChangedAction.RemovedElement == e.Action)
	                {
	                    this.RemoveElementNode(e.ActionElement);
	                }
	                else if (ElementChangedAction.ChangedProperty == e.Action)
	                {
	                    if (elementNode.Text != ((IElement)e.Element).Name)
	                    {
	                        elementNode.Text = ((IElement)e.Element).Name;
	                    }
	                }
	            }
			}
			catch (Exception ex)
			{
				MessageForm.ShowDialog(ex);
			}
		}
		
		/// <summary>
		/// Find the node corresponding to the given element.
		/// </summary>
		/// <param name="element">Element to search the node for.</param>
		/// <returns>The found node or null if nothing has been found.</returns>
		private TreeNode FindTreeNode(IElement element)
		{
			return this.FindTreeNode(this._tree.Nodes, element);
		}
		
		/// <summary>
		/// Find the node corresponding to the given element startin on a given nodes 
		/// collection.
		/// </summary>
		/// <param name="root">Nodes collection root to start from.</param>
		/// <param name="element">Element to search the node for.</param>
		/// <returns>The found node or null if nothing has been found.</returns>
		private TreeNode FindTreeNode(TreeNodeCollection root, IElement element)
		{
			foreach (TreeNode nd in root)
			{
				NodeInfo info = this.GetNodeInfo(nd);
				
				object searchElement = element;
				object infoElement = info.Element;
				if (null != searchElement && null != infoElement 
					&& searchElement is IElement)
				{
					searchElement = ((IElement)element).Id;
					infoElement = ((IElement)info.Element).Id;
				}
				
				if (searchElement == infoElement)
				{
					return nd;
				}
				else 
				{
					if (0 != nd.Nodes.Count)
					{
						TreeNode foundNode = this.FindTreeNode(nd.Nodes, element);
						if (null != foundNode)
						{
							return foundNode;
						}
					}
				}
			}
			return null;
		}
		
		/// <summary>
		/// Get the node info from a given node.
		/// </summary>
		/// <param name="node">Node to look for the node info.</param>
		/// <returns>Node info object or NodeInfo.Empty if there is no info on the node.</returns>
		private NodeInfo GetNodeInfo(TreeNode node)
		{
			if (null != node && null != node.Tag)
			{
				if (node.Tag is NodeInfo)
				{
					return (NodeInfo)node.Tag;
				}
			}
			return NodeInfo.Empty;
		}
		
		/// <summary>
		/// Get the node info from the current selected node.
		/// </summary>
		/// <returns>Node info object or NodeInfo.Empty if there is no info on the node.</returns>
		private NodeInfo GetSelectedNodeInfo()
		{
            return GetNodeInfo(this._tree.SelectedNode);
		}
		
		/// <summary>
		/// Rebuild the tree completely.
		/// </summary>
		public void RebuildTree()
		{
			this.CleanUpSchemaTree();
            this.BuildSchemaTree(this._control.Schema);
		}
		
		/// <summary>
		/// Create a group node with node info.
		/// </summary>
		/// <param name="groupResName">Name of the group in the resource file.</param>
		/// <param name="imageIndex">Index of the image to attach.</param>
		/// <returns>TreeNode of the group.</returns>
		private TreeNode CreateGroupNode(string groupResName, int imageIndex)
		{
            TreeNode group = new TreeNode(global::dataMagus.Forms.Handlers.HandlerResource.ResourceManager.GetString(groupResName));
            group.ImageIndex = imageIndex;
            group.SelectedImageIndex = imageIndex;
			group.Tag = new NodeInfo(null, groupResName, true, null);
			return group;
		}
		
		/// <summary>
		/// Create an element node with node info.
		/// </summary>
		/// <param name="element">Element the node is based on.</param>
		/// <param name="imageIndex">Index of the image to attach.</param>
		/// <returns>TreeNode of the element.</returns>
		private TreeNode CreateElementNode(IElement element, int imageIndex)
		{
            return this.CreateElementNode(element, imageIndex, null, SchemaNavigationControlHandler.DottedLine);
		}

		/// <summary>
		/// Create a datatype node with node info.
		/// </summary>
		/// <param name="datatype">Datatype the node is based on.</param>
		/// <param name="imageIndex">Index of the image to attach.</param>
		/// <returns>TreeNode of the datatype.</returns>
        private TreeNode CreateDataTypeNode(DataType datatype, int imageIndex)
        {
            TreeNode node = new TreeNode(datatype.ToString());
			node.ImageIndex = imageIndex;
			node.SelectedImageIndex = imageIndex;
            node.Tag = new NodeInfo(datatype, null, false, null);
            return node;
        }
		
		/// <summary>
		/// Create an element node with node info and one sub group.
		/// </summary>
		/// <param name="element">Element the node is based on.</param>
		/// <param name="imageIndex">Index of the image to attache.</param>
		/// <param name="subGroupResName">Name of the sub group in the resource file.</param>
		/// <param name="groupImageIndex">Index of the sub group image to attach.</param>
		/// <returns>TreeNode of the element.</returns>
		private TreeNode CreateElementNode(IElement element, int imageIndex, string subGroupResName, int groupImageIndex)
		{
			TreeNode node = new TreeNode(element.Name);
            node.ImageIndex = imageIndex;
            node.SelectedImageIndex = imageIndex;
			node.Tag = new NodeInfo(element, null, false, null);
            element.ElementChanged += new ElementChangedHandler(this.OnElementChanged);
			
			if (null != subGroupResName)
			{
                node.Nodes.Add(this.CreateGroupNode(subGroupResName, groupImageIndex));
			}
			return node;
		}
		
		/// <summary>
		/// Remove an element node from the tree.
		/// </summary>
		/// <param name="node">Node to remove.</param>
		private void RemoveElementNode(TreeNode node)
		{
			if (null == node)
			{
				return;
			}
			
			NodeInfo info = this.GetNodeInfo(node);
            if (null != info.Element && info.Element is IElement)
            {
				((IElement)info.Element).ElementChanged -= new ElementChangedHandler(this.OnElementChanged);
            }
            node.Remove();
		}
		
		/// <summary>
		/// Remove an element node from the tree based on the given element.
		/// </summary>
		/// <param name="element">Element to search for the node.</param>
		private void RemoveElementNode(IElement element)
		{
            TreeNode nd = FindTreeNode(element);
            if (null != nd)
            {
            	this.RemoveElementNode(nd);
            }
		}
		
		/// <summary>
		/// Clean up the tree.
		/// </summary>
		public void CleanUpSchemaTree()
		{
			this.CleanUpSchemaTree(this._tree.Nodes);
            this._tree.Nodes.Clear();
		}
		
		/// <summary>
		/// Clean up the from a given collection.
		/// </summary>
		/// <param name="nodes">Root node collection to clean up.</param>
		private void CleanUpSchemaTree(TreeNodeCollection nodes)
		{
			if (null != nodes)
			{
				foreach (TreeNode nd in nodes)
				{
					this.CleanUpSchemaTree(nd.Nodes);
					this.RemoveElementNode(nd);
				}
			}
		}
		
		/// <summary>
		/// Creating the node for datatypes.
		/// </summary>
		/// <returns>TreeNode for datatypes.</returns>
        private TreeNode CreateDataTypes()
        {
            // create datatypes
            TreeNode datatypeGroupNd = this.CreateGroupNode(SchemaNavigationControlHandler.DataTypeGroup, SchemaNavigationControlHandler.DataTypeImageIndex);
            IEnumerator datatypeEnumerator = DataContainer.Instance.PhysicalData.GetDataTypes();
            while (datatypeEnumerator.MoveNext())
            {
                DataType datatype = (DataType)datatypeEnumerator.Current;
                TreeNode datatypeNd = this.CreateDataTypeNode(datatype, SchemaNavigationControlHandler.DottedLine);
                datatypeGroupNd.Nodes.Add(datatypeNd);
            }
            return datatypeGroupNd;
        }
		
		#region Logical Handler Methods
		/// <summary>
		/// Implementation for building the logical tree.
		/// </summary>
		/// <param name="schema">Logical schema to build the tree with.</param>
		private void BuildLogicalSchemaTree(ISchema schema)
		{
			this.CleanUpSchemaTree();
			LogicalSchema logSchema = (LogicalSchema)schema;
			
			// create entities
			TreeNode entityGroupNd = this.CreateGroupNode(SchemaNavigationControlHandler.EntityGroup, SchemaNavigationControlHandler.EntityImageIndex);
            IEnumerator entitiesEnumerator = logSchema.GetEntities();
            while (entitiesEnumerator.MoveNext())
            {
                LogicalEntity entity = (LogicalEntity)entitiesEnumerator.Current;

				TreeNode entityNd = this.CreateElementNode(entity, SchemaNavigationControlHandler.DottedGroup,
					SchemaNavigationControlHandler.AttributeGroup, SchemaNavigationControlHandler.AttributeImageIndex);
				entityGroupNd.Nodes.Add(entityNd);
                IEnumerator attribEnumerator = entity.GetAttributes();
                while (attribEnumerator.MoveNext())
                {
                    LogicalAttribute attrib = (LogicalAttribute)attribEnumerator.Current;
                    TreeNode attribNd = this.CreateElementNode(attrib, SchemaNavigationControlHandler.DottedLine);
                    entityNd.Nodes[SchemaNavigationControlHandler.AttributeGroupNodeIndex].Nodes.Add(attribNd);
                }
			}
			
			// create relationships
			TreeNode relGroupNd = this.CreateGroupNode(SchemaNavigationControlHandler.RelationshipGroup, SchemaNavigationControlHandler.RelationshipImageIndex);
            IEnumerator relEnumerator = logSchema.GetRelationships();
            while (relEnumerator.MoveNext())
            {
                LogicalRelationship rel = (LogicalRelationship)relEnumerator.Current;

                if (rel is IAttributable)
                {
                    TreeNode relNd = this.CreateElementNode(rel, SchemaNavigationControlHandler.DottedGroup,
						SchemaNavigationControlHandler.AttributeGroup, SchemaNavigationControlHandler.AttributeImageIndex);
                    relGroupNd.Nodes.Add(relNd);

                    IEnumerator attributesEnumerator = ((IAttributable)rel).GetAttributes();
                    while (attributesEnumerator.MoveNext())
                    {
                        LogicalAttribute attrib = (LogicalAttribute)attributesEnumerator.Current;
                        TreeNode attribNd = this.CreateElementNode(attrib, SchemaNavigationControlHandler.DottedLine);
                        relNd.Nodes[SchemaNavigationControlHandler.AttributeGroupNodeIndex].Nodes.Add(attribNd);
                    }
                }
                else 
                {
                    TreeNode relNd = this.CreateElementNode(rel, SchemaNavigationControlHandler.DottedLine);
                    relGroupNd.Nodes.Add(relNd);
                }
			}

   		    // add root nodes
			this._tree.Nodes.Add(entityGroupNd);
			this._tree.Nodes.Add(relGroupNd);
		}
		
		/// <summary>
		/// Implementation for building the logical context menu.
		/// </summary>
		/// <param name="contextMenu">Context menu to be used.</param>
		/// <param name="selectedNode">Selected node for building the context menu.</param>
		private void BuildLogicalContextMenu(ContextMenuStrip contextMenu, TreeNode selectedNode)
		{
			contextMenu.Items.Clear();
			NodeInfo info = this.GetSelectedNodeInfo();

            if (SchemaNavigationControlHandler.EntityGroup == info.Identifyer)
			{
				ToolStripItem item = contextMenu.Items.Add(global::dataMagus.Forms.Handlers.HandlerResource.AddNewEntity);
				item.Click += new EventHandler(this.OnLogicalAdd);
			}
            else if (SchemaNavigationControlHandler.RelationshipGroup == info.Identifyer)
			{
				ToolStripItem item = contextMenu.Items.Add(global::dataMagus.Forms.Handlers.HandlerResource.AddNewRelationship);
				item.Click += new EventHandler(this.OnLogicalAdd);
			}
            else if (SchemaNavigationControlHandler.AttributeGroup == info.Identifyer)
			{
                ToolStripItem item = contextMenu.Items.Add(global::dataMagus.Forms.Handlers.HandlerResource.AddNewAttribute);
				item.Click += new EventHandler(this.OnLogicalAdd);
			}
			else if (null != info.Element)
			{
				if (info.Element is LogicalEntity)
				{
                    ToolStripItem item = contextMenu.Items.Add(global::dataMagus.Forms.Handlers.HandlerResource.DeleteEntity);
					item.Click += new EventHandler(this.OnLogicalDelete);
				}
				else if (info.Element is LogicalRelationship)
				{
                    ToolStripItem item = contextMenu.Items.Add(global::dataMagus.Forms.Handlers.HandlerResource.DeleteRelationship);
					item.Click += new EventHandler(this.OnLogicalDelete);
				}
				else if (info.Element is LogicalAttribute)
				{
                    ToolStripItem item = contextMenu.Items.Add(global::dataMagus.Forms.Handlers.HandlerResource.DeleteAttribute);
					item.Click += new EventHandler(this.OnLogicalDelete);
				}
			}
		}
		
        /// <summary>
        /// Event handler. Will be used when adding new logical elements.
        /// </summary>
        /// <param name="sender">Sender object.</param>
        /// <param name="e">EventArgs</param>
		private void OnLogicalAdd(object sender, EventArgs e)
		{
			LogicalSchema logSchema = (LogicalSchema)this._control.Schema;
			NodeInfo info = this.GetSelectedNodeInfo();

            if (SchemaNavigationControlHandler.EntityGroup == info.Identifyer)
			{
				// update model
                LogicalEntity entity = new LogicalEntity(global::dataMagus.Forms.Handlers.HandlerResource.DefaultEntityName);
				logSchema.AddEntity(entity);
			}
            else if (SchemaNavigationControlHandler.AttributeGroup == info.Identifyer)
			{
				// get parent
                NodeInfo parentInfo = GetNodeInfo(this._tree.SelectedNode.Parent);
				LogicalElement parentElement = (LogicalElement)parentInfo.Element;
                LogicalAttribute attrib = new LogicalAttribute(parentElement, global::dataMagus.Forms.Handlers.HandlerResource.DefaultAttributeName);
				
				if (parentInfo.Element is LogicalEntity)
				{
					((LogicalEntity)parentInfo.Element).AddAttribute(attrib);
				}
				else if (parentInfo.Element is LogicalRelationship)
				{
                    if (parentInfo.Element is IAttributable)
                    {
                        ((IAttributable)parentInfo.Element).AddAttribute(attrib);
                    }
				}
   			}
            else if (SchemaNavigationControlHandler.RelationshipGroup == info.Identifyer)
			{
				LogicalRelationshipForm frm = new LogicalRelationshipForm();
				DialogResult result = frm.ShowDialog();
				if (DialogResult.OK == result)
				{
					logSchema.AddRelationship(frm.Relationship);
				}
			}
		}

        /// <summary>
        /// Event handler. Will be used when deleting logical elements.
        /// </summary>
        /// <param name="sender">Sender object.</param>
        /// <param name="e">EventArgs</param>
		private void OnLogicalDelete(object sender, EventArgs e)
		{
			LogicalSchema logSchema = (LogicalSchema)this._control.Schema;
			NodeInfo info = this.GetSelectedNodeInfo();

			if (info.Element is LogicalEntity)
			{
				logSchema.RemoveEntity((LogicalEntity)info.Element);
			}
			else if (info.Element is LogicalRelationship)
			{
				logSchema.RemoveRelationship((LogicalRelationship)info.Element);
			}
			else if (info.Element is LogicalAttribute)
			{
				// get parent
				NodeInfo parentInfo = this.GetNodeInfo(this._tree.SelectedNode.Parent.Parent);
				LogicalElement parentElement = (LogicalElement)parentInfo.Element;
				
				if (parentElement is LogicalEntity)
				{
					((LogicalEntity)parentElement).RemoveAttribute((LogicalAttribute)info.Element);
				}
				else if (parentElement is LogicalRelationship)
				{
					if (parentElement is IAttributable)
					{
						((IAttributable)parentElement).RemoveAttribute((LogicalAttribute)info.Element);
					}
				}
			}
		}
		#endregion
		
		#region Physical Relational Handler Methods
		/// <summary>
		/// Implementation for building the physical relational tree.
		/// </summary>
		/// <param name="schema">Physical relational schema to build the tree with.</param>
		private void BuildPhysicalRelationalSchemaTree(ISchema schema)
		{
			this.CleanUpSchemaTree();
			PhysicalRelationalSchema physSchema = (PhysicalRelationalSchema)schema;
			
			// create tables
            TreeNode tableGroupNd = this.CreateGroupNode(SchemaNavigationControlHandler.TableGroup, SchemaNavigationControlHandler.TableImageIndex);
            IEnumerator tablesEnumerator = physSchema.GetTables();
            while (tablesEnumerator.MoveNext())
            {
                PhysicalRelationalTable table = (PhysicalRelationalTable)tablesEnumerator.Current;

                TreeNode tableNd = this.CreateElementNode(table, SchemaNavigationControlHandler.DottedGroup,
					SchemaNavigationControlHandler.ColumnGroup, SchemaNavigationControlHandler.AttributeImageIndex);
				tableGroupNd.Nodes.Add(tableNd);
                IEnumerator columnsEnumerator = table.GetColumns();
                while (columnsEnumerator.MoveNext())
                {
                    PhysicalRelationalColumn column = (PhysicalRelationalColumn)columnsEnumerator.Current;
					TreeNode columnNd = this.CreateElementNode(column, SchemaNavigationControlHandler.DottedLine);
                    tableNd.Nodes[SchemaNavigationControlHandler.AttributeGroupNodeIndex].Nodes.Add(columnNd);
				}

                tableNd.Nodes.Add(this.CreateGroupNode(SchemaNavigationControlHandler.IndexGroup, SchemaNavigationControlHandler.IndexImageIndex));
                IEnumerator indexEnumerator = table.GetIndexes();
                while (indexEnumerator.MoveNext())
                {
                    PhysicalRelationalIndex index = (PhysicalRelationalIndex)indexEnumerator.Current;
                    TreeNode indexNd = this.CreateElementNode(index, SchemaNavigationControlHandler.DottedLine);
                    tableNd.Nodes[SchemaNavigationControlHandler.TableIndexGroupNodeIndex].Nodes.Add(indexNd);
                }
			}
			
			// create relationships
            TreeNode relGroupNd = this.CreateGroupNode(SchemaNavigationControlHandler.RelationshipGroup, SchemaNavigationControlHandler.RelationshipImageIndex);
            IEnumerator relEnumerator = physSchema.GetRelationships();
            while (relEnumerator.MoveNext())
            {
                PhysicalRelationalRelationship rel = (PhysicalRelationalRelationship)relEnumerator.Current;
				TreeNode relNd = this.CreateElementNode(rel, SchemaNavigationControlHandler.DottedLine);
                relGroupNd.Nodes.Add(relNd);
			}

            // add root nodes
            this._tree.Nodes.Add(tableGroupNd);
            this._tree.Nodes.Add(relGroupNd);

            // create datatypes
            this._tree.Nodes.Add(this.CreateDataTypes());
		}

		/// <summary>
		/// Implementation for building the physical relational context menu.
		/// </summary>
		/// <param name="contextMenu">Context menu to be used.</param>
		/// <param name="selectedNode">Selected node for building the context menu.</param>
		private void BuildPhysicalRelationalContextMenu(ContextMenuStrip contextMenu, TreeNode selectedNode)
		{
			contextMenu.Items.Clear();
            NodeInfo info = this.GetSelectedNodeInfo();

            if (SchemaNavigationControlHandler.DataTypeGroup == info.Identifyer)
            {
                ToolStripItem item = contextMenu.Items.Add(global::dataMagus.Forms.Handlers.HandlerResource.ManageDatatype);
                item.Click += new EventHandler(this.OnPhysicalRelationalManage);
            }
		}

        /// <summary>
        /// Event handler. Will be used when managing physical relational elements.
        /// </summary>
        /// <param name="sender">Sender object.</param>
        /// <param name="e">EventArgs</param>
        private void OnPhysicalRelationalManage(object sender, EventArgs e)
        {
            PhysicalRelationalSchema prSchema = (PhysicalRelationalSchema)this._control.Schema;
            NodeInfo info = this.GetSelectedNodeInfo();

            if (SchemaNavigationControlHandler.DataTypeGroup == info.Identifyer)
            {
                DataTypeForm frm = new DataTypeForm();
                frm.Schema = prSchema;
                DialogResult result = frm.ShowDialog();

                // refresh datatypes
                this._tree.Nodes[SchemaNavigationControlHandler.DataTypeGroupNodeIndex].Remove();
                this._tree.Nodes.Insert(SchemaNavigationControlHandler.DataTypeGroupNodeIndex, this.CreateDataTypes());
            }
        }
		#endregion
		
		#region Physical Objectoriented Handler Methods
		/// <summary>
		/// Implementation for building the physical objectoriented tree.
		/// </summary>
		/// <param name="schema">Physical objectoriented schema to build the tree with.</param>
		private void BuildPhysicalObjectorientedSchemaTree(ISchema schema)
		{
			this.CleanUpSchemaTree();
			PhysicalObjectorientedSchema physSchema = (PhysicalObjectorientedSchema)schema;
			
			// create classes
            TreeNode classGroupNd = this.CreateGroupNode(SchemaNavigationControlHandler.ClassGroup, SchemaNavigationControlHandler.ClassImageIndex);
            IEnumerator classEnumerator = physSchema.GetClasses();
            while (classEnumerator.MoveNext())
            {
                PhysicalObjectorientedClass classItem = (PhysicalObjectorientedClass)classEnumerator.Current;

                TreeNode classNd = this.CreateElementNode(classItem, SchemaNavigationControlHandler.DottedGroup, 
					SchemaNavigationControlHandler.PropertyGroup, SchemaNavigationControlHandler.AttributeImageIndex);
				classGroupNd.Nodes.Add(classNd);
                IEnumerator propEnumerator = classItem.GetProperties();
                while (propEnumerator.MoveNext())
                {
                    PhysicalObjectorientedProperty propItem = (PhysicalObjectorientedProperty)propEnumerator.Current;
					TreeNode propNd = this.CreateElementNode(propItem, SchemaNavigationControlHandler.DottedLine);
                    classNd.Nodes[SchemaNavigationControlHandler.AttributeGroupNodeIndex].Nodes.Add(propNd);
				}
			}
			
			// create associations
            TreeNode assocGroupNd = this.CreateGroupNode(SchemaNavigationControlHandler.AssociationGroup, SchemaNavigationControlHandler.RelationshipImageIndex);
            IEnumerator assocEnumerator = physSchema.GetAssociations();
            while (assocEnumerator.MoveNext())
            {
                PhysicalObjectorientedAssociation assoc = (PhysicalObjectorientedAssociation)assocEnumerator.Current;
				TreeNode assocNd = this.CreateElementNode(assoc, SchemaNavigationControlHandler.DottedLine);
				assocGroupNd.Nodes.Add(assocNd);
			}
			
			// add root nodes
			this._tree.Nodes.Add(classGroupNd);
			this._tree.Nodes.Add(assocGroupNd);

            // create datatypes
            this._tree.Nodes.Add(this.CreateDataTypes());
		}
		
		/// <summary>
		/// Implementation for building the physical objectoriented context menu.
		/// </summary>
		/// <param name="contextMenu">Context menu to be used.</param>
		/// <param name="selectedNode">Selected node for building the context menu.</param>
		private void BuildPhysicalObjectorientedContextMenu(ContextMenuStrip contextMenu, TreeNode selectedNode)
		{
			contextMenu.Items.Clear();
            NodeInfo info = this.GetSelectedNodeInfo();

            if (SchemaNavigationControlHandler.DataTypeGroup == info.Identifyer)
            {
                ToolStripItem item = contextMenu.Items.Add(global::dataMagus.Forms.Handlers.HandlerResource.ManageDatatype);
                item.Click += new EventHandler(this.OnPhysicalObjectorientedManage);
            }
		}

        /// <summary>
        /// Event handler. Will be used when managing physical relational elements.
        /// </summary>
        /// <param name="sender">Sender object.</param>
        /// <param name="e">EventArgs</param>
        private void OnPhysicalObjectorientedManage(object sender, EventArgs e)
        {
            PhysicalObjectorientedSchema ooSchema = (PhysicalObjectorientedSchema)this._control.Schema;
            NodeInfo info = this.GetSelectedNodeInfo();

            if (SchemaNavigationControlHandler.DataTypeGroup == info.Identifyer)
            {
                DataTypeForm frm = new DataTypeForm();
                frm.Schema = ooSchema;
                DialogResult result = frm.ShowDialog();

                // refresh datatypes
                this._tree.Nodes[SchemaNavigationControlHandler.DataTypeGroupNodeIndex].Remove();
                this._tree.Nodes.Insert(SchemaNavigationControlHandler.DataTypeGroupNodeIndex, this.CreateDataTypes());
            }
        }
		#endregion
		#endregion
	}
}
