#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.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Data;
using System.Text;
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.ModifiedChen
{
	/// <summary>
	/// This class is used to represent an entity in Modified Chen notation.
	/// </summary>
	public partial class EntityControl : ElementControl
	{
		#region Members
		/// <summary>
		/// Logical entity the control has been created from.
		/// </summary>
		private LogicalEntity _entity = null;
		
		/// <summary>
		/// Identifier if the control is expandend and the attributes will be shown.
		/// </summary>
        private bool _isExpanded = true;
        
        /// <summary>
        /// Maximum size of the automatically calculated title
        /// </summary>
        private SizeF _autoTitleFont = SizeF.Empty;
        
        /// <summary>
        /// Image of the title.
        /// </summary>
        private Image _titleImage = null;
        
        /// <summary>
        /// Gap between the image and the title
        /// </summary>
        private float _titleImageGap = 10F;
        
        /// <summary>
        /// Padding on the top of the title.
        /// </summary>
        private float _paddingTitleTop = 10F;
        
        /// <summary>
        /// Padding on the bottom of the title.
        /// </summary>
        private float _paddingTitleBottom = 10F;
        
        /// <summary>
        /// Padding on the left of the title.
        /// </summary>
        private float _paddingTitleLeft = 10F;
        
        /// <summary>
        /// Padding on the right of the title.
        /// </summary>
        private float _paddingTitleRight = 10F;
        
        /// <summary>
        /// Maximum size of the automatically calculated attribute
        /// </summary>
        private SizeF _autoAttributeFont = SizeF.Empty;
        
        /// <summary>
        /// Padding on the top of the attribute.
        /// </summary>
        private float _paddingAttributeTop = 10F;
        
        /// <summary>
        /// Padding on the bottom of the attribute.
        /// </summary>
        private float _paddingAttributeBottom = 10F;
        
        /// <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 = 5F;
        
        /// <summary>
        /// Margin on the top of the attribute.
        /// </summary>
        private float _marginAttributeTop = 7F;
        
        /// <summary>
        /// Margin on the bottom of the attribute.
        /// </summary>
        private float _marginAttributeBottom = 2F;
        
        /// <summary>
        /// Margin on the left of the attribute.
        /// </summary>
        private float _marginAttributeLeft = 10F;
        
        /// <summary>
        /// Margin on the right of the attribute.
        /// </summary>
        private float _marginAttributeRight = 5F;
        
        /// <summary>
        /// Background color of the entity.
        /// </summary>
        private Color _entityBackground = Color.Gainsboro;
        
        /// <summary>
        /// Background color of the attribute.
        /// </summary>
        private Color _attributeBackground = Color.Gainsboro;
        
        /// <summary>
        /// Holds the backup of the entity background, when the control changes the color for beeing selected.
        /// </summary>
        private Color _entityBackgroundBackup = Color.Gainsboro;
        
        /// <summary>
        /// Holds the backup of the attribute background, when the control changes the color for beeing selected.
        /// </summary>
        private Color _attributeBackgroundBackup = Color.Gainsboro;
        
        /// <summary>
        /// Color of the border.
        /// </summary>
        private Color _borderColor = Color.DarkGray;
        
        /// <summary>
        /// Color of the entity title.
        /// </summary>
        private Color _entityNameColor = Color.Black;
        
        /// <summary>
        /// Color of the attribute.
        /// </summary>
        private Color _attributeNameColor = Color.Black;
        
        /// <summary>
        /// Font of the entity title.
        /// </summary>
        private Font _entityNameFont = new Font(FontFamily.GenericSansSerif, 10, FontStyle.Bold);
        
        /// <summary>
        /// Font of the attribute.
        /// </summary>
        private Font _attributeNameFont = new Font(FontFamily.GenericSansSerif, 8, FontStyle.Regular);
        
        /// <summary>
        /// Font of the key attribute.
        /// </summary>
        private Font _keyAttributeNameFont = new Font(FontFamily.GenericSansSerif, 8, FontStyle.Underline);
        #endregion
		
		#region Constructors / Destructors
		/// <summary>
		/// Default Constructor
		/// </summary>
		/// <remarks>Not used at runtime.</remarks>
		public EntityControl()
		{
            InitializeComponent();
        	this._titleImage = elementImages.Images[0];
		}
		
		/// <summary>
		/// Runtime constructor
		/// </summary>
		/// <param name="entity">Logical entity the control is based on.</param>
        public EntityControl(LogicalEntity entity) 
        	: base(entity)
        {
            InitializeComponent();
        	this._entity = entity;
        	this._titleImage = elementImages.Images[0];
        }
		#endregion
		
		#region Properties
		/// <summary>
		/// Indicates if the control is marked as selected.
		/// </summary>
		public override bool IsSelected
		{
			set 
			{
				bool dataChanged = value != this.IsSelected;
				if (dataChanged)
				{
					if (value)
					{
						// store old background
						this._entityBackgroundBackup = this._entityBackground;
						this._attributeBackgroundBackup = this._attributeBackground;
						// set selected color
						this._entityBackground = this.SelectedBackColor;
						this._attributeBackground = this.SelectedBackColor;
					}
					else 
					{
						// restore
						this._entityBackground = this._entityBackgroundBackup;
						this._attributeBackground = this._attributeBackgroundBackup;
					}
				}
				
				// call base
				base.IsSelected = value;
			}
		}
		
		/// <summary>
		/// Identifier if the control is expandend and the attributes will be shown.
		/// </summary>
		public bool IsExpanded 
		{
			get { return _isExpanded; }
			set { _isExpanded = value; }
		}
        
        /// <summary>
        /// Color of the border.
        /// </summary>
        public Color BorderColor
        {
            get { return _borderColor; }
            set { _borderColor = value; }
        }
        
        /// <summary>
        /// Color of the entity title.
        /// </summary>
        public Color EntityNameColor
        {
            get { return _entityNameColor; }
            set {_entityNameColor = value; }
        }
        
        /// <summary>
        /// Color of the attribute.
        /// </summary>
        public Color AttributeNameColor
        {
            get { return _attributeNameColor; }
            set { _attributeNameColor = value; }
        }
        
        /// <summary>
        /// Background color of the entity.
        /// </summary>
		public Color EntityBackground 
		{
			get { return _entityBackground; }
			set { _entityBackground = value; }
		}
        
        /// <summary>
        /// Background color of the attribute.
        /// </summary>
		public Color AttributeBackground 
		{
			get { return _attributeBackground; }
			set { _attributeBackground = value; }
		}
        
        /// <summary>
        /// Font of the entity title.
        /// </summary>
        public Font EntityNameFont
        {
            get { return _entityNameFont; }
            set { _entityNameFont = value; }
        }
        
        /// <summary>
        /// Font of the attribute.
        /// </summary>
        public Font AttributeNameFont
        {
            get { return _attributeNameFont; }
            set { _attributeNameFont = value; }
        }
        
        /// <summary>
        /// Font of the key attribute.
        /// </summary>
        public Font KeyAttributeNameFont
        {
            get { return _keyAttributeNameFont; }
            set { _keyAttributeNameFont = value; }
        }
		#endregion
		
		#region Methodes
		/// <summary>
		/// Occures when the control will be painted.
		/// </summary>
		/// <param name="e">PaintEventArgs</param>
        protected override void OnPaint(PaintEventArgs e)
        {
            // before base call, calc font-size
            // else the CalcControlSize() would have _autoFont.Width = 0
            this._autoTitleFont = this.CalcTitleFontSize(e.Graphics);
            this._autoAttributeFont = this.CalcAttributeFontSize(e.Graphics);

            // call base
            base.OnPaint(e);
            
            // draw entity
            DrawEntity(e.Graphics, new Point(0,0));
        }
        
        /// <summary>
        /// Draws the title of the entity.
        /// </summary>
        /// <param name="context">Graphics context.</param>
        /// <param name="offset">Drawing offset.</param>
        /// <param name="text">Title text.</param>
        /// <param name="textFont">Font of the title.</param>
        /// <param name="textBrush">Brush of the title.</param>
        /// <param name="bgBrush">Brush of the background.</param>
        /// <param name="borderPen">Pen for the border.</param>
        private void DrawTitle(Graphics context, Point offset, string text, Font textFont, Brush textBrush, Brush bgBrush, Pen borderPen)
        {
            SizeF titleSize = this.CalcTitleSize(context);
            PointF imgPos = new PointF(this._paddingTitleLeft, this._paddingTitleTop);
            PointF titlePos = new PointF(this._paddingTitleLeft + this._titleImage.Width + this._titleImageGap, this._paddingTitleTop);
            PointF rectPos = new PointF(0, 0);

            // add offset
            imgPos.X += offset.X;
            imgPos.Y += offset.Y;
            titlePos.X += offset.X;
            titlePos.Y += offset.Y;
            rectPos.X += offset.X;
            rectPos.Y += offset.Y;
            
            // draw border (-1 pixel because of right and bottom line)
            context.FillRectangle(bgBrush, offset.X, offset.Y, this.Width, titleSize.Height);
            context.DrawRectangle(borderPen, offset.X, offset.Y, this.Width-1, titleSize.Height-1);

            context.DrawImage(this._titleImage, imgPos);
            context.DrawString(text, textFont, textBrush, titlePos);
        }
        
        /// <summary>
        /// Draws a single attribute.
        /// </summary>
        /// <param name="context">Graphics context.</param>
        /// <param name="offset">Drawing offset.</param>
        /// <param name="text">Attribute text.</param>
        /// <param name="textFont">Font of the attribute.</param>
        /// <param name="textBrush">Brush of the attribute.</param>
        /// <param name="backgroundBrush">Brush for the background.</param>
        /// <param name="ellipsePos">Position of the ellipse to draw.</param>
        private void DrawAttributeItem(Graphics context, Point offset, string text, Font textFont, Brush textBrush, Brush backgroundBrush, PointF ellipsePos)
        {
        	// add offset
        	PointF offsetLessEllipsePos = ellipsePos;
            ellipsePos.X += offset.X;
            ellipsePos.Y += offset.Y;
        		
        	// draw ellipse
        	Pen borderPen = new Pen(this._borderColor);
        	SizeF ellipseSize = this.CalcMaxAttributeSize(context, false, true);
        	// scale to title with
        	ellipseSize.Width = this.CalcTitleSize(context).Width - (this._marginAttributeLeft + this._marginAttributeRight);
        	context.FillEllipse(backgroundBrush, 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, textBrush, textPos);
            
            // draw connector lines
            SizeF titleSize = this.CalcTitleSize(context);
            PointF verticalStart = new PointF(offset.X, offset.Y + (titleSize.Height / 2));
            PointF verticalEnd = new PointF(offset.X, offset.Y + (titleSize.Height / 2) + offsetLessEllipsePos.Y - (ellipseSize.Height / 2));
            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 control into a graphics context.
        /// </summary>
        /// <param name="context">Graphics context.</param>
		public override void DrawControl(Graphics context)
		{
			this.DrawEntity(context, this.Location);
		}
        
        /// <summary>
        /// Draws the whole entity.
        /// </summary>
        /// <param name="context">Graphics context.</param>
        /// <param name="offset">Drawing offset.</param>
        private void DrawEntity(Graphics context, Point offset)
        {
        	// create brushes and pens
            Brush entityNameBrush = new SolidBrush(this.EntityNameColor);
            Brush attributeNameBrush = new SolidBrush(this.AttributeNameColor);
            Brush entityBgBrush = new SolidBrush(this.EntityBackground);
            Brush entityAttributeBgBrush = new SolidBrush(this.AttributeBackground);
            Pen borderPen = new Pen(this.BorderColor);

            // draw title rectangle
            SizeF titleSize = this.CalcTitleSize(context);
            DrawTitle(context, offset, this._entity.Name, this.EntityNameFont, entityNameBrush, entityBgBrush, borderPen);

            // draw attributes only when control is expanded
            if (this.IsExpanded)
            {
	            // calculate start position
	            SizeF attribSize = this.CalcMaxAttributeSize(context, false, true);
	            PointF attribPos = new PointF(0, titleSize.Height);
	            attribPos.X += this._marginAttributeLeft;
	
	            // draw key attributes
	            IEnumerator keyEnum = this._entity.GetKeyAttributes();
	            while (keyEnum.MoveNext())
	            {
	            	LogicalAttribute key = (LogicalAttribute)keyEnum.Current;
	            	attribPos.Y += this._marginAttributeTop;
	                DrawAttributeItem(context, offset, key.Name, this.KeyAttributeNameFont, attributeNameBrush, entityAttributeBgBrush, attribPos);
	                // calculate next position
	                attribPos.Y += (attribSize.Height + this._marginAttributeBottom);
	            }

	            // draw regular attributes
	            IEnumerator attribEnum = this._entity.GetAttributes();
	            while (attribEnum.MoveNext())
	            {
	            	LogicalAttribute attrib = (LogicalAttribute)attribEnum.Current;
	            	if (!attrib.IsPartOfKey)
	            	{
	            		attribPos.Y += this._marginAttributeTop;
		                DrawAttributeItem(context, offset, attrib.Name, this.AttributeNameFont, attributeNameBrush, entityAttributeBgBrush, attribPos);
		                // calculate next position
						attribPos.Y += (attribSize.Height + this._marginAttributeBottom);
	            	}
	            }
            }
        }
		
        /// <summary>
        /// Event handler: Sets the IsExpanded flag and refreshs.
        /// </summary>
        /// <param name="sender">Sender object.</param>
        /// <param name="e">EventArgs</param>
		private void ExpanderPictureClick(object sender, EventArgs e)
		{
        	// change picture
	       	if (this.IsExpanded)
	       	{
	       		this.expanderPicture.Image = global::dataMagus.PlugIns.Notations.ModifiedChen.OtherResources.TreePlus;
	       	}
        	else 
        	{
	       		this.expanderPicture.Image = global::dataMagus.PlugIns.Notations.ModifiedChen.OtherResources.TreeMinus;
        	}

        	// switch flag
        	this.IsExpanded = !this.IsExpanded;
        	
        	// refresh
        	this.RefreshSizeAndSchema();
		}
		
		/// <summary>
		/// Calculates the maximum font size of the title.
		/// </summary>
		/// <param name="context">Graphics context.</param>
		/// <returns>Maximum size or SizeF.Empty if there is no context.</returns>
		private SizeF CalcTitleFontSize(Graphics context)
		{
			SizeF size = SizeF.Empty;
			if (null != context)
			{
				// title
				size += context.MeasureString(this._entity.Name, this.EntityNameFont);
			}
			return size;
		}
		
		/// <summary>
		/// Calculates the maximum font size of all attributes.
		/// </summary>
		/// <param name="context">Graphics context.</param>
		/// <returns>Maximum size or SizeF.Empty if there is no context.</returns>
		private SizeF CalcAttributeFontSize(Graphics context)
		{
			SizeF size = SizeF.Empty;
			if (null != context)
			{
				// elements
	            IEnumerator itemEnum = this._entity.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>
		/// Calculates the size of the whole title including padding.
		/// </summary>
		/// <param name="context">Graphics context.</param>
		/// <returns>Size or SizeF.Empty if there is no context.</returns>
		public SizeF CalcTitleSize(Graphics context)
		{
			return CalcTitleSize(context, true);
		}
		
		/// <summary>
		/// Calculates the size of the whole title.
		/// </summary>
		/// <param name="context">Graphics context.</param>
		/// <param name="includePadding">Indicates if the padding has to be considered.</param>
		/// <returns>Size or SizeF.Empty if there is no context.</returns>
		public SizeF CalcTitleSize(Graphics context, bool includePadding)
		{
			SizeF size = SizeF.Empty;
			if (null != context)
			{
				size.Width += context.MeasureString(this._entity.Name, this._entityNameFont).Width;
			}
			size.Height += this._entityNameFont.Height;
			size.Width += this._titleImageGap;
			size.Width += this._titleImage.Width;
			if (null != this.expanderPicture.Image)
			{
				size.Width += this.expanderPicture.Image.Width;
			}
			if (includePadding)
			{
				size.Height += (this._paddingTitleTop + this._paddingTitleBottom);
				size.Width += (this._paddingTitleLeft + this._paddingTitleRight);
			}
			return size;
		}

		/// <summary>
		/// Calculates the maximum size of all attributes including margin and padding.
		/// </summary>
		/// <param name="context">Graphics context.</param>
		/// <returns>Size or SizeF.Empty if there is no context.</returns>
		public SizeF CalcMaxAttributeSize(Graphics context)
		{
			return CalcMaxAttributeSize(context, true, true);
		}
		
		/// <summary>
		/// Calculates the maximum size of all attributes including margin and padding.
		/// </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>Size or SizeF.Empty if there is no context.</returns>
		public SizeF CalcMaxAttributeSize(Graphics context, bool includeMargin, bool includePadding)
		{
			SizeF size = SizeF.Empty;
			IEnumerator attribEnum = this._entity.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 whole size of the control.
		/// </summary>
		/// <returns>Size of the control.</returns>
		public override Size CalcControlSize()
		{
			float autoHeight = 0;
			float autoWidth = 0;
			float autoTitleWidth = 0;
			float autoAttribWidth = 0;
			
			SizeF titleSize = this.CalcTitleSize(null);
			SizeF attribSize = SizeF.Empty;
			
			autoHeight = titleSize.Height;
			autoTitleWidth = titleSize.Width + this._autoTitleFont.Width;

			if (this.IsExpanded && this._entity.CountAttributes > 0)
            {
				int attribCnt = this._entity.CountAttributes;
				attribSize = this.CalcMaxAttributeSize(null);
				attribSize.Height *= attribCnt;
				
				autoHeight += (int)attribSize.Height;
				autoAttribWidth = attribSize.Width + this._autoAttributeFont.Width;
            }
			
			autoWidth = Math.Max(autoTitleWidth, autoAttribWidth);
			return new Size((int)autoWidth, (int)autoHeight);
		}
		
		/// <summary>
		/// Event handler: Loads global data.
		/// </summary>
		/// <param name="sender">Sender object.</param>
		/// <param name="e">EventArgs</param>
		void EntityControlLoad(object sender, EventArgs e)
		{
			this.IsExpanded = true;
       		this.expanderPicture.Image = global::dataMagus.PlugIns.Notations.ModifiedChen.OtherResources.TreeMinus;
		}
		#endregion
	}
}
