#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.Drawing;
using System.Windows.Forms;
using dataMagus.Common;
using dataMagus.Common.PlugIn;
using dataMagus.Common.PlugIn.Notation;
using dataMagus.Common.Logical;
#endregion

namespace dataMagus.PlugIns.Notations.Chen
{
	/// <summary>
	/// This class is used to draw a Chen relationship.
	/// </summary>
	public class RelationshipDrawer
	{
        #region Constants
        /// <summary>
        /// Defines the name of a Is-a relationship.
        /// </summary>
        private const string FixedIsAName = "is-a";
        
        /// <summary>
        /// Defines the 1 cardinality.
        /// </summary>
        private const string Cardinality1 = "1";
        
        /// <summary>
        /// Defines the N cardinality.
        /// </summary>
        private const string CardinalityN = "N";
        
        /// <summary>
        /// Defines the M cardinality.
        /// </summary>
        private const string CardinalityM = "M";
        
        /// <summary>
        /// Defines the default gap for self relationships.
        /// </summary>
        private const int DefaultStartSelfGap = 30;
        #endregion

        #region Members
        /// <summary>
        /// Logical relationship the control has been created from.
        /// </summary>
        private LogicalRelationship _relationship = null;
        
        /// <summary>
        /// Control of the first element between the relationship.
        /// </summary>
		private ElementControl _element1 = null;
		
		/// <summary>
		/// Control of the second element between the relationship.
		/// </summary>
		private ElementControl _element2 = null;
		
		/// <summary>
		/// Indicator if the cardinalities should show up.
		/// </summary>
		private bool _showCardinalities = true;

		/// <summary>
		/// Conter holds the multiplier for more than one relationship between equal entites.
		/// </summary>
        private static Dictionary<long, int> _betweenRelationCounter = new Dictionary<long, int>();
        
        /// <summary>
        /// Holds the current gaps for self relationships.
        /// </summary>
        private static Dictionary<string, int> _currentSelfGap = new Dictionary<string, int>();
        
        /// <summary>
        /// Memorizes for each relationship if the attributes has to be expanded.
        /// </summary>
        private static Dictionary<string, bool> _expanderMemorizer = new Dictionary<string, bool>();
        
        /// <summary>
        /// Memorizes for each relationship the range of the expander symbol. This is used to react on
        /// mouse click events.
        /// </summary>
        private static Dictionary<string, Rectangle> _expanderRangeMemorizer = new Dictionary<string, Rectangle>();

        /// <summary>
        /// Offset to draw the attributes of the relationship calculated from the middle.
        /// </summary>
		private PointF _attributesOffset = new PointF(10F, 5F);
		
		/// <summary>
		/// Margin of the relationship lines around the entites.
		/// </summary>
		private int _marginEntity = 8;
		
		/// <summary>
		/// Top and bottom gap for multiple relationships drawing a line to the same entity.
		/// </summary>
		private int _multipleRelationsTopBottomGap = 60;
		
		/// <summary>
		/// Left and right gap for multiple relationships drawing a line to the same entity.
		/// </summary>
		private int _multipleRelationsLeftRightGap = 40;
		
		/// <summary>
		/// Distance between self relationships.
		/// </summary>
		private int _selfRelationDistance = 20;
		
		/// <summary>
		/// Padding on the top of the diamond sign.
		/// </summary>
		private float _paddingDiamondTop = 10;
		
		/// <summary>
		/// Padding on the bottom of the diamond sign.
		/// </summary>
		private float _paddingDiamondBottom = 10;
		
		/// <summary>
		/// Padding on the left of the diamond sign.
		/// </summary>
		private float _paddingDiamondLeft = 15;
		
		/// <summary>
		/// Padding on the right of the diamond sign.
		/// </summary>
		private float _paddingDiamondRight = 15;
		
		/// <summary>
		/// Padding on the top of the Is-a sign.
		/// </summary>
		private float _paddingIsASignTop = 5;
		
		/// <summary>
		/// Padding on the bottom of the Is-a sign.
		/// </summary>
		private float _paddingIsASignBottom = 5;
		
		/// <summary>
		/// Padding on the left of the Is-a sign.
		/// </summary>
		private float _paddingIsASignLeft = 15;
		
		/// <summary>
		/// Padding on the right of the Is-a sign.
		/// </summary>
		private float _paddingIsASignRight = 15;
		
		/// <summary>
		/// Delte of the hexagon for the Is-a sign.
		/// </summary>
		private int _isaHexagonDelta = 10;
		
		/// <summary>
		/// Padding on the top of the attribute.
		/// </summary>
        private float _paddingAttributeTop = 7F;
        
        /// <summary>
		/// Padding on the bottom of the attribute.
        /// </summary>
        private float _paddingAttributeBottom = 7F;
        
        /// <summary>
		/// Padding on the left of the attribute.
        /// </summary>
        private float _paddingAttributeLeft = 10F;
        
        /// <summary>
		/// Padding on the right of the attribute.
        /// </summary>
        private float _paddingAttributeRight = 3F;
        
        /// <summary>
		/// Margin on the top of the attribute.
        /// </summary>
        private float _marginAttributeTop = 0F;
        
        /// <summary>
		/// Margin on the bottom of the attribute.
        /// </summary>
        private float _marginAttributeBottom = 5F;
        
        /// <summary>
		/// Margin on the left of the attribute.
        /// </summary>
        private float _marginAttributeLeft = 0F;
        
        /// <summary>
		/// Margin on the right of the attribute.
        /// </summary>
        private float _marginAttributeRight = 0F;
		
        /// <summary>
        /// Thickness of the line.
        /// </summary>
        private float _lineThikness = 1.6F;
        
        /// <summary>
        /// Color of the line.
        /// </summary>
        private Color _lineColor = Color.DarkGray;
        
        /// <summary>
        /// Color of the arrow on Is-a relationships.
        /// </summary>
        private Color _arrowColor = Color.Black;
        
        /// <summary>
        /// Border color of the diamond sign.
        /// </summary>
        private Color _diamondBorderColor = Color.DarkGray;
        
        /// <summary>
        /// Background color of the diamon sign.
        /// </summary>
        private Color _diamondBackground = Color.LightGray;
        
        /// <summary>
        /// Border color of the Is-a sign.
        /// </summary>
        private Color _isaBorderColor = Color.DarkGray;
        
        /// <summary>
        /// Background color of the Is-a sign.
        /// </summary>
        private Color _isaBackground = Color.LightGray;
        
        /// <summary>
        /// Color of the text inside the diamond.
        /// </summary>
        private Color _diamondTextColor = Color.Black;
        
        /// <summary>
        /// Color of the text inside the Is-a sign.
        /// </summary>
        private Color _isaTextColor = Color.Black;
        
        /// <summary>
        /// Color of the text for the cardinalities.
        /// </summary>
        private Color _cardinalityTextColor = Color.Black;
        
        /// <summary>
        /// Border color of an attribute.
        /// </summary>
        private Color _attributeBorderColor = Color.DarkGray;
        
        /// <summary>
        /// Color of the attribute text.
        /// </summary>
        private Color _attributeNameColor = Color.Black;
        
        /// <summary>
        /// Background color of the attribute.
        /// </summary>
        private Color _attributeBackground = Color.White;
        
        /// <summary>
        /// Font of the text inside the diamond sign.
        /// </summary>
        private Font _diamondFont = new Font(FontFamily.GenericSansSerif, 8, FontStyle.Regular);
        
        /// <summary>
        /// Font of the text inside the Is-a sign.
        /// </summary>
        private Font _isaFont = new Font(FontFamily.GenericSansSerif, 8, FontStyle.Bold);
        
        /// <summary>
        /// Font of the text of the cardinalities.
        /// </summary>
        private Font _cardinalityFont = new Font(FontFamily.GenericSansSerif, 8, FontStyle.Regular);
        
        /// <summary>
        /// Font of the text for attributes.
        /// </summary>
        private Font _attributeNameFont = new Font(FontFamily.GenericSansSerif, 8, FontStyle.Regular);
        
        /// <summary>
        /// Font of the text for kex attributes.
        /// </summary>
        private Font _keyAttributeNameFont = new Font(FontFamily.GenericSansSerif, 8, FontStyle.Underline);
        #endregion

        #region Constructors / Destructors
        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="relationship">Logical relationship to draw.</param>
        /// <param name="element1">Control of the first element between the relationship.</param>
        /// <param name="element2">Control of the second element between the relationship.</param>
		public RelationshipDrawer(LogicalRelationship relationship, ElementControl element1, ElementControl element2)
		{
			this._relationship = relationship;
			this._element1 = element1;
			this._element2 = element2;

			// create expander entry
			if (!RelationshipDrawer._expanderMemorizer.ContainsKey(this._relationship.Id))
			{
				RelationshipDrawer._expanderMemorizer[this._relationship.Id] = false;
			}
		}
        #endregion

        #region Properties
		/// <summary>
		/// Indicator if the cardinalities should show up.
		/// </summary>
		public bool ShowCardinalities 
		{
			get { return _showCardinalities; }
			set { _showCardinalities = value; }
		}

		/// <summary>
		/// Indicates if the relationship is marked as expanded and has to draw the attributes.
		/// </summary>
		public bool IsExpanded
		{
			get 
			{
				return RelationshipDrawer._expanderMemorizer[this._relationship.Id];
			}
			set 
			{ 
				RelationshipDrawer._expanderMemorizer[this._relationship.Id] = value;
			}
		}
        
        /// <summary>
        /// Thickness of the line.
        /// </summary>
		public float LineThikness 
		{
			get { return _lineThikness; }
			set { _lineThikness = value; }
		}
        
        /// <summary>
        /// Color of the line.
        /// </summary>
		public Color LineColor 
		{
			get { return _lineColor; }
			set { _lineColor = value; }
		}
        
        /// <summary>
        /// Color of the arrow on Is-a relationships.
        /// </summary>
		public Color ArrowColor 
		{
			get { return _arrowColor; }
			set { _arrowColor = value; }
		}
		
        /// <summary>
        /// Border color of the diamond sign.
        /// </summary>
		public Color DiamondBorderColor 
		{
			get { return _diamondBorderColor; }
			set { _diamondBorderColor = value; }
		}
        
        /// <summary>
        /// Background color of the diamon sign.
        /// </summary>
		public Color DiamondBackground 
		{
			get { return _diamondBackground; }
			set { _diamondBackground = value; }
		}
        
        /// <summary>
        /// Border color of the Is-a sign.
        /// </summary>
		public Color IsaBorderColor
		{
			get { return _isaBorderColor; }
			set { _isaBorderColor = value; }
		}
        
        /// <summary>
        /// Background color of the Is-a sign.
        /// </summary>
		public Color IsaBackground 
		{
			get { return _isaBackground; }
			set { _isaBackground = value; }
		}
        
        /// <summary>
        /// Font of the text inside the diamond sign.
        /// </summary>
		public Font DiamondFont 
		{
			get { return _diamondFont; }
			set { _diamondFont = value; }
		}
        
        /// <summary>
        /// Font of the text inside the Is-a sign.
        /// </summary>
		public Font IsaFont
		{
			get { return _isaFont; }
			set { _isaFont = value; }
		}
		
        /// <summary>
        /// Color of the text inside the diamond.
        /// </summary>
		public Color DiamondTextColor 
		{
			get { return _diamondTextColor; }
			set { _diamondTextColor = value; }
		}
        
        /// <summary>
        /// Color of the text inside the Is-a sign.
        /// </summary>
		public Color IsaTextColor 
		{
			get { return _isaTextColor; }
			set { _isaTextColor = value; }
		}
		
        /// <summary>
        /// Font of the text of the cardinalities.
        /// </summary>
		public Font CardinalityFont
		{
			get { return _cardinalityFont; }
			set { _cardinalityFont = value; }
		}
        
        /// <summary>
        /// Color of the text for the cardinalities.
        /// </summary>
		public Color CardinalityTextColor
		{
			get { return _cardinalityTextColor; }
			set { _cardinalityTextColor = value; }
		}
		
        /// <summary>
        /// Color of the attribute text.
        /// </summary>
		public Color AttributeNameColor
		{
			get { return _attributeNameColor; }
			set { _attributeNameColor = value; }
		}
		
        /// <summary>
        /// Background color of the attribute.
        /// </summary>
		public Color AttributeBackground 
		{
			get { return _attributeBackground; }
			set { _attributeBackground = value; }
		}
        
        /// <summary>
        /// Font of the text for attributes.
        /// </summary>
		public Font AttributeNameFont 
		{
			get { return _attributeNameFont; }
			set { _attributeNameFont = value; }
		}

        /// <summary>
        /// Font of the text for kex attributes.
        /// </summary>
		public Font KeyAttributeNameFont
        {
            get { return _keyAttributeNameFont; }
            set { _keyAttributeNameFont = value; }
        }
		
        /// <summary>
        /// Border color of an attribute.
        /// </summary>
		public Color AttributeBorderColor 
		{
			get { return _attributeBorderColor; }
			set { _attributeBorderColor = value; }
		}
		#endregion

        #region Methodes
        /// <summary>
        /// Tells that the drawing of the relationships will begin.
        /// </summary>
        public static void BeginDrawingRelationships()
        {
        	RelationshipDrawer._betweenRelationCounter.Clear();
        	RelationshipDrawer._currentSelfGap.Clear();
        }

        /// <summary>
        /// Tells that a MouseDown event has happend on the schema control.
        /// </summary>
        /// <param name="e">MouseEventArgs</param>
        /// <remarks>This method will search for expander contols and expand/collapse it.</remarks>
        public static void OnMouseDown(MouseEventArgs e)
        {
        	if (MouseButtons.Left == e.Button)
        	{
	        	// search for a range to switch the expander
	        	IEnumerator rangeEnum = RelationshipDrawer._expanderRangeMemorizer.GetEnumerator();
	        	while (rangeEnum.MoveNext())
	        	{
	        		KeyValuePair<string, Rectangle> pair = (KeyValuePair<string, Rectangle>)rangeEnum.Current;
	        		if (pair.Value.Contains(e.Location))
	        		{
	        			// search for existing expander
	        			if (RelationshipDrawer._expanderMemorizer.ContainsKey(pair.Key))
	        			{
	        				// swap expander data
	        				bool currentValue = RelationshipDrawer._expanderMemorizer[pair.Key];
	        				RelationshipDrawer._expanderMemorizer[pair.Key] = !currentValue;
	        				break;
	        			}
	        		}
	        	}
        	}
        }
        
        /// <summary>
        /// Draws the relationship.
        /// </summary>
        /// <param name="context">Graphics context.</param>
        public void DrawRelationship(Graphics context)
        {
            if (this._element1 == this._element2)
            {
            	this.DrawSelfRelationship(context);
            }
            else
            {
            	this.DrawDoubleRelationship(context);
            }
        }
        
        /// <summary>
        /// Draws the relationship between two different entites.
        /// </summary>
        /// <param name="context">Graphics context.</param>
        private void DrawDoubleRelationship(Graphics context)
        {
        	Pen linePen = new Pen(this.LineColor, this.LineThikness);
        	Pen arrowBorderPen = new Pen(this.ArrowColor);
        	Brush arrowBrush = new SolidBrush(this.ArrowColor);

        	Point symbolA = Point.Empty;
            Point symbolB = Point.Empty;
            Point straightA = Point.Empty;
            Point straightB = Point.Empty;
            Point middle = Point.Empty;
            NotationHelper.CalcPoints(this._element1, this._element2, this._marginEntity,
				out symbolA, out symbolB, out straightA, out straightB, out middle);
            
            // correct middle if needed
            int middleMultiplier = NotationHelper.CalcCorrectionMultiplier(this._relationship.Entity1, this._relationship.Entity1.CountRelationshipLinksBetween,
				this._relationship.Entity2, RelationshipDrawer._betweenRelationCounter);
            NotationHelper.SideLocation locA = NotationHelper.EvalPointsLocation(symbolA, straightA);
            if (NotationHelper.SideLocation.Top == locA
               || NotationHelper.SideLocation.Bottom == locA)
            {
            	// needs more gap then left, right
            	middle.X += (this._multipleRelationsTopBottomGap * middleMultiplier);
            }
            else if (NotationHelper.SideLocation.Left == locA
               || NotationHelper.SideLocation.Right == locA)
            {
            	// needs less gap then top bottom
            	middle.Y += (this._multipleRelationsLeftRightGap * middleMultiplier);
            }
            
            // draw straight lines
            context.DrawLine(linePen, symbolA, straightA);
            context.DrawLine(linePen, symbolB, straightB);
            
            // draw sign
            Point signTop = Point.Empty;
            Point signBottom = Point.Empty;
            Point signLeft = Point.Empty;
            Point signRight = Point.Empty;
            if (this._relationship is IsALogicalRelationship)
            {
            	SizeF isaSignSize = this.CalcIsASignSize(context, false);
            	this.DrawIsASign(middle, (int)isaSignSize.Width, (int)isaSignSize.Height, this._isaHexagonDelta, RelationshipDrawer.FixedIsAName, context,
	        		out signTop, out signBottom, out signLeft, out signRight);
            }
            else
            {
            	SizeF diamondSize = this.CalcDiamondSize(context);
            	this.DrawDiamondSign(middle, (int)diamondSize.Width, (int)diamondSize.Height, this._relationship.Name, context,
	        		out signTop, out signBottom, out signLeft, out signRight);
            }
            
            // draw lines to sign
            Point linkPointA = NotationHelper.EvalBestConnectionPoint(this._element1, signTop, signBottom, signLeft, signRight);
            if (Point.Empty != linkPointA)
            {
            	context.DrawLine(linePen, symbolA, linkPointA);
            }
            Point linkPointB = NotationHelper.EvalBestConnectionPoint(this._element2, signTop, signBottom, signLeft, signRight);
            if (Point.Empty != linkPointB)
            {
         	   context.DrawLine(linePen, symbolB, linkPointB);
            }
            
            // draw arrow if needed
            if (this._relationship is IsALogicalRelationship)
            {
            	Point[] arrow = NotationHelper.CreateTrianglePolygon(straightA, symbolA, this._marginEntity);
	            context.FillPolygon(arrowBrush, arrow);
	            context.DrawPolygon(arrowBorderPen, arrow);
            }
            
            // draw relationships
            this.DrawAttributes(context, middle, signBottom);
            
            // draw cardinality
            this.DrawDoubleCardinality(symbolA, symbolB, linkPointA, linkPointB, context);
            
            // draw expander
            this.DrawExpander(context, signTop, signRight);
        }
        
        /// <summary>
        /// Draws the self relationship.
        /// </summary>
        /// <param name="context">Graphics context.</param>
        private void DrawSelfRelationship(Graphics context)
        {
        	Pen linePen = new Pen(this.LineColor, this.LineThikness);

            // calculate the current selfgap, so the next selfrelation knows where to position
            int selfGap = RelationshipDrawer.DefaultStartSelfGap;
            if (RelationshipDrawer._currentSelfGap.ContainsKey(this._element1.Element.Id))
            {
            	selfGap = RelationshipDrawer._currentSelfGap[this._element1.Element.Id];
            }
            RelationshipDrawer._currentSelfGap[this._element1.Element.Id] = selfGap + (int)this.CalcMaxSize(context).Width;
            
        	Point symbolA = Point.Empty;
            Point symbolB = Point.Empty;
            Point straightA = Point.Empty;
            Point straightB = Point.Empty;
        	Point selfSymbolA = Point.Empty;
            Point selfSymbolB = Point.Empty;
            Point edge = Point.Empty;
            NotationHelper.CalcSelfPoints(this._element1, this._marginEntity, selfGap, this._selfRelationDistance,
				out symbolA, out symbolB, out straightA, out straightB, out selfSymbolA, out selfSymbolB, out edge);
            
            // draw lines
            context.DrawLine(linePen, straightA, symbolA);
            context.DrawLine(linePen, symbolA, selfSymbolA);
            context.DrawLine(linePen, straightB, symbolB);
            context.DrawLine(linePen, symbolB, selfSymbolB);
            
            // draw diamond
        	SizeF labelSize = context.MeasureString(this._relationship.Name, this.DiamondFont);
            Point signTop = Point.Empty;
            Point signBottom = Point.Empty;
            Point signLeft = Point.Empty;
            Point signRight = Point.Empty;
            
            SizeF diamondSize = this.CalcDiamondSize(context);
            this.DrawDiamondSign(edge, (int)diamondSize.Width, (int)diamondSize.Height, this._relationship.Name, context,
        		out signTop, out signBottom, out signLeft, out signRight);
            
            // draw lines to sign
            context.DrawLine(linePen, selfSymbolA, signLeft);
            context.DrawLine(linePen, selfSymbolB, signBottom);

            // draw relationships
            this.DrawAttributes(context, edge, signBottom);
            
            // draw cardinality
            this.DrawSelfCardinality(selfSymbolA, selfSymbolB, context);
            
            // draw expander
            this.DrawExpander(context, signTop, signRight);
        }
        
        /// <summary>
        /// Draws all attributes of the relationship.
        /// </summary>
        /// <param name="context">Graphics context.</param>
        /// <param name="middle">Point of the middle of the diamond sign.</param>
        /// <param name="lineConnection">Point where to connect the lines for the attributes.</param>
        private void DrawAttributes(Graphics context, Point middle, Point lineConnection)
        {
            // draw attributes only when control is expanded and relationship can have some
            if (this.IsExpanded && this._relationship is IAttributable)
            {
            	IAttributable attribRel = (IAttributable)this._relationship;
            	
	            // calculate start position
	            SizeF attribSize = this.CalcMaxAttributeSize(context, false, true);
	            PointF attribPos = new PointF(middle.X + this._attributesOffset.X, middle.Y + this._attributesOffset.Y);
	            attribPos.X += this._marginAttributeLeft;
	            
	            // draw key attributes
	            IEnumerator attribEnum = attribRel.GetAttributes();
	            while (attribEnum.MoveNext())
	            {
	            	LogicalAttribute attrib = (LogicalAttribute)attribEnum.Current;
	            	if (attrib.IsPartOfKey)
	            	{
	            		attribPos.Y += this._marginAttributeTop;
		                DrawAttributeItem(context, attrib.Name, this.KeyAttributeNameFont, attribPos, lineConnection);
		                // calculate next position
						attribPos.Y += (attribSize.Height + this._marginAttributeBottom);
	            	}
	            }
	            
	            // draw regular attributes
	            attribEnum.Reset();
	            while (attribEnum.MoveNext())
	            {
	            	LogicalAttribute attrib = (LogicalAttribute)attribEnum.Current;
	            	if (!attrib.IsPartOfKey)
	            	{
	            		attribPos.Y += this._marginAttributeTop;
		                DrawAttributeItem(context, attrib.Name, this.AttributeNameFont, attribPos, lineConnection);
		                // calculate next position
						attribPos.Y += (attribSize.Height + this._marginAttributeBottom);
	            	}
	            }
            }
        }
        
        /// <summary>
        /// Draws on signle attribute.
        /// </summary>
        /// <param name="context">Graphics context.</param>
        /// <param name="text">Text of the attribute.</param>
        /// <param name="textFont">Font of the attribute.</param>
        /// <param name="ellipsePos">Position of the ellipse.</param>
        /// <param name="lineConnection">Point where to connect the line for that attribute.</param>
        private void DrawAttributeItem(Graphics context, string text, Font textFont, PointF ellipsePos, PointF lineConnection)
        {
        	// draw ellipse
        	Pen borderPen = new Pen(this._attributeBorderColor);
        	Brush nameBrush = new SolidBrush(this.AttributeNameColor);
	    	Brush bgBrush = new SolidBrush(this.AttributeBackground);

    	    SizeF ellipseSize = this.CalcMaxAttributeSize(context, false, true);
        	context.FillEllipse(bgBrush, ellipsePos.X, ellipsePos.Y, ellipseSize.Width, ellipseSize.Height);
            context.DrawEllipse(borderPen, ellipsePos.X, ellipsePos.Y, ellipseSize.Width, ellipseSize.Height);
            
            // draw text
        	PointF textPos = ellipsePos;
        	textPos.X += this._paddingAttributeLeft;
        	textPos.Y += this._paddingAttributeTop;
            context.DrawString(text, textFont, nameBrush, textPos);
            
            // draw connector lines
            float connectionHeight = ellipsePos.Y - lineConnection.Y;
            float lineHeight = 0;
            if ( connectionHeight > 0)
            {
            	lineHeight += (connectionHeight + (ellipseSize.Height / 2));
            }
            
            PointF verticalStart = lineConnection;
            PointF verticalEnd = new PointF(lineConnection.X, lineConnection.Y + lineHeight);
            PointF horizontalStart = verticalEnd;
            PointF horizontalEnd = new PointF(ellipsePos.X, ellipsePos.Y + (ellipseSize.Height / 2));
            context.DrawLine(borderPen, verticalStart, verticalEnd);
            context.DrawLine(borderPen, horizontalStart, horizontalEnd);
        }
        
        /// <summary>
        /// Draw the cardinalities for self relationships.
        /// </summary>
        /// <param name="symbolA">Symbol point to draw the cardinality A.</param>
        /// <param name="symbolB">Symbol point to draw the cardinality B.</param>
        /// <param name="context">Graphics context.</param>
        private void DrawSelfCardinality(Point symbolA, Point symbolB, Graphics context)
        {
        	if (!this.ShowCardinalities)
        	{
        		return;
        	}
        	
        	Point locationA = symbolA;
            Point locationB = symbolB;
            
            // adjust them a bit, not to corss the line used for the linking
            locationA.Y -= 10;
            locationB.X -= 10;
            
            NotationHelper.SideLocation sideLocA = NotationHelper.SideLocation.Top;
            NotationHelper.SideLocation sideLocB = NotationHelper.SideLocation.Right;
            
            this.DrawCardinality(locationA, locationB, sideLocA, sideLocB, context);
        }
        
        /// <summary>
        /// Draw the cardinalities for relationships having different entities.
        /// </summary>
        /// <param name="symbolA">Symbol point of the first entity.</param>
        /// <param name="symbolB">Symbol point of the second entity.</param>
        /// <param name="diamondEdgeA">Connection point of diamon edge for the first entity.</param>
        /// <param name="diamondEdgeB">Connection point of diamon edge for the second entity.</param>
        /// <param name="context">Graphics context.</param>
        private void DrawDoubleCardinality(Point symbolA, Point symbolB, Point diamondEdgeA, Point diamondEdgeB, Graphics context)
        {
        	if (!this.ShowCardinalities)
        	{
        		return;
        	}

        	if (diamondEdgeA == Point.Empty || diamondEdgeB == Point.Empty)
        	{
        		// don't draw, points too near
        		return;
        	}
        	
            NotationHelper.SideLocation sideLocA = NotationHelper.EvalPointsLocation(symbolA, diamondEdgeA);
            NotationHelper.SideLocation sideLocB = NotationHelper.EvalPointsLocation(symbolB, diamondEdgeB);
            
        	Point locationA = NotationHelper.EvalMiddle(symbolA, diamondEdgeA);
            Point locationB = NotationHelper.EvalMiddle(symbolB, diamondEdgeB);;
            
            this.DrawCardinality(locationA, locationB, sideLocA, sideLocB, context);
        }
        
        /// <summary>
        /// Draw the cardinalities on the given points.
        /// </summary>
        /// <param name="locationA">Point for the first entity.</param>
        /// <param name="locationB">Point for the second entity.</param>
        /// <param name="sideLocA">Current location of the first entity.</param>
        /// <param name="sideLocB">Current location of the second entity.</param>
        /// <param name="context">Graphics context.</param>
        private void DrawCardinality(Point locationA, Point locationB, 
			NotationHelper.SideLocation sideLocA, NotationHelper.SideLocation sideLocB,
			Graphics context)
        {
        	if (this._relationship is IsALogicalRelationship)
            {
        		// is-a has no cardinality
            	return;
            }
        	
        	Brush textBrush = new SolidBrush(this.CardinalityTextColor);
        	string cardinalityTextA = string.Empty;
        	string cardinalityTextB = string.Empty;
            if (this._relationship is OneToOneLogicalRelationship)
            {
            	cardinalityTextA = RelationshipDrawer.Cardinality1;
            	cardinalityTextB = RelationshipDrawer.Cardinality1;
            }
            else if (this._relationship is OneToManyLogicalRelationship)
            {
            	cardinalityTextA = RelationshipDrawer.Cardinality1;
            	cardinalityTextB = RelationshipDrawer.CardinalityN;
            }
            else if (this._relationship is ManyToManyLogicalRelationship)
            {
            	cardinalityTextA = RelationshipDrawer.CardinalityN;
            	cardinalityTextB = RelationshipDrawer.CardinalityM;
            }
            
            // correct A
            if (NotationHelper.SideLocation.Top == sideLocA
               || NotationHelper.SideLocation.Bottom == sideLocA)
            {
            	locationA.Y -= (int)context.MeasureString(cardinalityTextA, this.CardinalityFont).Width / 2;
            }
            else if (NotationHelper.SideLocation.Left == sideLocA
				|| NotationHelper.SideLocation.Right == sideLocA)
            {
            	locationA.X -= (int)context.MeasureString(cardinalityTextA, this.CardinalityFont).Width / 2;
            }
            
            // correct B
            if (NotationHelper.SideLocation.Top == sideLocB
               || NotationHelper.SideLocation.Bottom == sideLocB)
            {
            	locationB.Y -= (int)context.MeasureString(cardinalityTextB, this.CardinalityFont).Width / 2;
            }
            else if (NotationHelper.SideLocation.Left == sideLocB
				|| NotationHelper.SideLocation.Right == sideLocB)
            {
            	locationB.X -= (int)context.MeasureString(cardinalityTextB, this.CardinalityFont).Width / 2;
            }
        	
            context.DrawString(cardinalityTextA, this.CardinalityFont, textBrush, locationA);
            context.DrawString(cardinalityTextB, this.CardinalityFont, textBrush, locationB);
        }
        
        /// <summary>
        /// Draw the expander sign.
        /// </summary>
        /// <remarks>Sign will be placed between the top and right edge point of the diamond sign.</remarks>
        /// <param name="context">Graphics context.</param>
        /// <param name="signTop">Top point of the diamond sign.</param>
        /// <param name="signRight">Right point of the diamond sign.</param>
        private void DrawExpander(Graphics context, Point signTop, Point signRight)
        {
        	if (this._relationship is IsALogicalRelationship)
            {
        		// is-a has no attributes
            	return;
            }
        	
        	// calc expander pos and remember range for events
        	Point expanderPos = new Point(signTop.X + ((signRight.X - signTop.X) / 2),
        	                              signTop.Y + ((signRight.Y - signTop.Y) / 2));
        	RelationshipDrawer._expanderRangeMemorizer[this._relationship.Id] = new Rectangle(expanderPos, 
				global::dataMagus.PlugIns.Notations.Chen.OtherResources.TreeMinus.Size);
        	
			// draw expander
			if (this.IsExpanded)
			{
	        	context.DrawImage(global::dataMagus.PlugIns.Notations.Chen.OtherResources.TreeMinus, expanderPos);
			}
			else
			{
	        	context.DrawImage(global::dataMagus.PlugIns.Notations.Chen.OtherResources.TreePlus, expanderPos);
			}
        }
        
        /// <summary>
        /// Draw the diamond sign.
        /// </summary>
        /// <param name="middle">Middle point of the sign.</param>
        /// <param name="width">With of the sign.</param>
        /// <param name="height">Height of the sign.</param>
        /// <param name="text">Text inside the sign.</param>
        /// <param name="context">Graphics context.</param>
        /// <param name="topEdge">Returns the top edge of the sign.</param>
        /// <param name="bottomEdge">Returns the bottom edge of the sign.</param>
        /// <param name="leftEdge">Returns the left edge of the sign.</param>
        /// <param name="rightEdge">Returns the right edge of the sign.</param>
        private void DrawDiamondSign(Point middle, int width, int height, string text, Graphics context,
			out Point topEdge, out Point bottomEdge, out Point leftEdge, out Point rightEdge)
        {
        	Brush textBrush = new SolidBrush(this.DiamondTextColor);
        	Brush diamondBrush = new SolidBrush(this.DiamondBackground);
        	Pen diamondPen = new Pen(this.DiamondBorderColor);
        	
	        Point[] diamond = new Point[4];
	        diamond[0] = new Point(middle.X - width/2, middle.Y);
	        diamond[1] = new Point(middle.X, middle.Y - height/2);
	        diamond[2] = new Point(middle.X + width/2, middle.Y);
	        diamond[3] = new Point(middle.X, middle.Y + height/2);
	        context.FillPolygon(diamondBrush, diamond);
	        context.DrawPolygon(diamondPen, diamond);

        	SizeF textSize = context.MeasureString(text, this.DiamondFont);
        	Point textLocation = new Point(middle.X - (int)textSize.Width/2, middle.Y - (int)textSize.Height/2);
            context.DrawString(text, this.DiamondFont, textBrush, textLocation);
            
            topEdge = new Point(middle.X, middle.Y - height/2);
            bottomEdge = new Point(middle.X, middle.Y + height/2);
            leftEdge = new Point(middle.X - width/2, middle.Y);
            rightEdge = new Point(middle.X + width/2, middle.Y);
        }
        
        /// <summary>
        /// Draw the Is-a sign.
        /// </summary>
        /// <param name="middle">Middle point of the sign.</param>
        /// <param name="width">With of the sign.</param>
        /// <param name="height">Height of the sign.</param>
        /// <param name="delta">Hexagon delta (width).</param>
        /// <param name="text">Text inside the sign.</param>
        /// <param name="context">Graphics context.</param>
        /// <param name="topEdge">Returns the top edge of the sign.</param>
        /// <param name="bottomEdge">Returns the bottom edge of the sign.</param>
        /// <param name="leftEdge">Returns the left edge of the sign.</param>
        /// <param name="rightEdge">Returns the right edge of the sign.</param>
        private void DrawIsASign(Point middle, int width, int height, int delta, string text, Graphics context,
			out Point topEdge, out Point bottomEdge, out Point leftEdge, out Point rightEdge)
        {
        	Brush textBrush = new SolidBrush(this.IsaTextColor);
        	Brush isaBrush = new SolidBrush(this.IsaBackground);
        	Pen isaPen = new Pen(this.IsaBorderColor);

        	int isaLineLen = (width - (2*delta));
            Point[] isaSymbol = new Point[6];
            isaSymbol[0] = new Point(middle.X - width/2, middle.Y);
            isaSymbol[1] = new Point(isaSymbol[0].X + delta, isaSymbol[0].Y - height/2);
            isaSymbol[2] = new Point(isaSymbol[1].X + isaLineLen, isaSymbol[1].Y);
            isaSymbol[3] = new Point(isaSymbol[2].X + delta, isaSymbol[2].Y + height/2);
            isaSymbol[4] = new Point(isaSymbol[2].X, isaSymbol[3].Y + height/2);
            isaSymbol[5] = new Point(isaSymbol[4].X - isaLineLen, isaSymbol[4].Y);
            context.FillPolygon(isaBrush, isaSymbol);
            context.DrawPolygon(isaPen, isaSymbol);
            
        	SizeF textSize = context.MeasureString(text, this.IsaFont);
        	Point textLocation = new Point(middle.X - (int)textSize.Width/2, middle.Y - (int)textSize.Height/2);
            context.DrawString(text, this.IsaFont, textBrush, textLocation);
            
            topEdge = new Point(middle.X, middle.Y - height/2);
            bottomEdge = new Point(middle.X, middle.Y + height/2);
            leftEdge = new Point(middle.X - width/2, middle.Y);
            rightEdge = new Point(middle.X + width/2, middle.Y);
        }

        /// <summary>
        /// Calculate the size of the diamond sign.
        /// </summary>
        /// <param name="context">Graphics context.</param>
		/// <returns>Size of the sign.</returns>
		private SizeF CalcDiamondSize(Graphics context)
		{
			SizeF size = SizeF.Empty;
        	size += context.MeasureString(this._relationship.Name, this.DiamondFont);
        	size.Width += (this._paddingDiamondLeft + this._paddingDiamondRight);
        	size.Height += (this._paddingDiamondTop + this._paddingDiamondBottom);
			return size;
		}
		
		/// <summary>
		/// Calculate the size of the Is-a sign.
		/// </summary>
		/// <param name="context">Graphics context.</param>
		/// <param name="includeHexagonDelta">Indicates if the hexagon delta has to be considered.</param>
		/// <returns>Size of the sign.</returns>
		private SizeF CalcIsASignSize(Graphics context, bool includeHexagonDelta)
		{
			SizeF size = SizeF.Empty;
        	size += context.MeasureString(RelationshipDrawer.FixedIsAName, this.IsaFont);
        	size.Width += (this._paddingIsASignLeft + this._paddingIsASignRight);
        	size.Height += (this._paddingIsASignTop + this._paddingIsASignBottom);
        	if (includeHexagonDelta)
        	{
	            size.Width += (2 * this._isaHexagonDelta);
        	}
			return size;
		}
		
		/// <summary>
		/// Calculates the maximum font size of the text inside the sign and all its attributes.
		/// </summary>
		/// <remarks>Size can differ depending on the type of the relationship.</remarks>
		/// <param name="context">Graphics context.</param>
		/// <returns>Maximum font size.</returns>
		private SizeF CalcFontSize(Graphics context)
		{
			SizeF size = SizeF.Empty;
			if (this._relationship is IsALogicalRelationship)
			{
	            // max isa title
				SizeF titleSize = context.MeasureString(this._relationship.Name, this.IsaFont);
	            size.Width = Math.Max(size.Width, titleSize.Width);
	            size.Height = Math.Max(size.Height, titleSize.Height);
			}
			else
			{
				// max diamond title
				SizeF titleSize = context.MeasureString(this._relationship.Name, this.DiamondFont);
	            size.Width = Math.Max(size.Width, titleSize.Width);
	            size.Height = Math.Max(size.Height, titleSize.Height);
			}

			// calc max attributes
			if (this._relationship is IAttributable)
			{
				IEnumerator itemEnum = ((IAttributable)this._relationship).GetAttributes();
	            while (itemEnum.MoveNext())
	            {
	            	IElement element = (IElement)itemEnum.Current;
					SizeF elementSize = context.MeasureString(element.Name, this.AttributeNameFont);
		            size.Width = Math.Max(size.Width, elementSize.Width);
		            size.Height = Math.Max(size.Height, elementSize.Height);
	            }
			}
			return size;
		}
		
		/// <summary>
		/// Calculate the maximum 
		/// </summary>
		/// <param name="context">Graphics context.</param>
		/// <returns></returns>
		public SizeF CalcMaxAttributeSize(Graphics context)
		{
			return CalcMaxAttributeSize(context, true, true);
		}
		
		/// <summary>
		/// Calculates the maximum size of all attributes.
		/// </summary>
		/// <param name="context">Graphics context.</param>
		/// <param name="includeMargin">Indicates if the margin has to be considered.</param>
		/// <param name="includePadding">Indicates if the padding has to be considered.</param>
		/// <returns>Maximum size.</returns>
		public SizeF CalcMaxAttributeSize(Graphics context, bool includeMargin, bool includePadding)
		{
			SizeF size = SizeF.Empty;
			if (this._relationship is IAttributable)
			{
				IEnumerator attribEnum = ((IAttributable)this._relationship).GetAttributes();
				while (attribEnum.MoveNext())
				{
					IElement element = (IElement)attribEnum.Current;
					SizeF textSize = SizeF.Empty;
					if (null != context)
					{
						textSize.Width = context.MeasureString(element.Name, this._attributeNameFont).Width;
					}
					textSize.Height = this._attributeNameFont.Height;
					
					size.Height = Math.Max(size.Height, textSize.Height);
					size.Width = Math.Max(size.Width, textSize.Width);
				}
				if (includePadding)
				{
					size.Height += (this._paddingAttributeTop + this._paddingAttributeBottom);
					size.Width += (this._paddingAttributeLeft + this._paddingAttributeRight);
				}
	
				if (includeMargin)
				{
					size.Height += (this._marginAttributeTop + this._marginAttributeBottom);
					size.Width += (this._marginAttributeLeft + this._marginAttributeRight);
				}
			}
			return size;
		}
		
		/// <summary>
		/// Calculates the maximum size of the control.
		/// </summary>
		/// <param name="context">Graphics context.</param>
		/// <returns>Maximum size.</returns>
		private SizeF CalcMaxSize(Graphics context)
		{
			SizeF size = SizeF.Empty;
			size += this.CalcFontSize(context);
			if (this._relationship is IsALogicalRelationship)
			{
				SizeF isaSignSize = this.CalcIsASignSize(context, true);
				size.Width = Math.Max(isaSignSize.Width, size.Width);
				size.Height = Math.Max(isaSignSize.Height, size.Height);
			}
			else
			{
				SizeF diamondSize = this.CalcDiamondSize(context);
				size.Width = Math.Max(diamondSize.Width, size.Width);
				size.Height = Math.Max(diamondSize.Height, size.Height);
			}
			return size;
		}
        #endregion
	}
}
