#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.Logical;
#endregion

namespace dataMagus.Forms.Elements
{
	/// <summary>
	/// Extended data form for managing logical relationships.
	/// </summary>
	public partial class LogicalRelationshipForm : Form
	{
		#region Constants
		/// <summary>
		/// Identifier for is-a relationship.
		/// </summary>
		private const string IsAIdentifier = "is-a";
		
		/// <summary>
		/// Identifier for one-to-one relationship.
		/// </summary>
		private const string OneToOneIdentifier = "1 : 1";
		
		/// <summary>
		/// Identifier for one-to-many relationship.
		/// </summary>
		private const string OneToManyIdentifier = "1 : N";
		
		/// <summary>
		/// Identifier for many-to-many relationship.
		/// </summary>
		private const string ManyToManyIdentifier = "N : M";
		#endregion
		
		#region Members
		/// <summary>
		/// Logical relationship data the form is based on.
		/// </summary>
		private LogicalRelationship _relationship = null;
		
		/// <summary>
		/// Remembered automatical generated rolename A to know if the user
		/// changed it on its own.
		/// </summary>
		private bool _autoRolenameA = true;
		
		/// <summary>
		/// Remembered automatical generated rolename B to know if the user
		/// changed it on its own.
		/// </summary>
		private bool _autoRolenameB = true;
		
		/// <summary>
		/// Indicator if the data has been loaded or if it's a new one.
		/// </summary>
		private bool _isLoaded = false;
		#endregion
		
		#region Constructors / Destructors
		/// <summary>
        /// Default constructor.
        /// </summary>
		public LogicalRelationshipForm()
		{
			InitializeComponent();
		}
		#endregion
		
		#region Properties
		/// <summary>
		/// Logical relationship data the form is based on.
		/// </summary>
		public LogicalRelationship Relationship 
		{
			get { return this._relationship; }
			set { this._relationship = value; }
		}
		#endregion
		
		#region Methodes
        /// <summary>
        /// Event handler.
        /// </summary>
        /// <param name="sender">Sender object.</param>
        /// <param name="e">EventArgs</param>
		private void OkButtonClick(object sender, EventArgs e)
		{
			try
			{
				if (!this.FormIsValidate())
				{
					return;
				}
				
				string name = this.nameTextBox.Text.Trim();
				string rolenameA = this.roleATextBox.Text.Trim();
				string rolenameB = this.roleBTextBox.Text.Trim();
				string type = this.typeCombo.SelectedItem.ToString();
				
				KeyValuePair<string, LogicalEntity> pairA = (KeyValuePair<string, LogicalEntity>)this.entityACombo.SelectedItem;
				KeyValuePair<string, LogicalEntity> pairB = (KeyValuePair<string, LogicalEntity>)this.entityBCombo.SelectedItem;
				LogicalEntity entityA = (LogicalEntity)pairA.Value;
				LogicalEntity entityB = (LogicalEntity)pairB.Value;
	
				//--- check to create a new one ---
				if (null == this._relationship)
				{
					if (LogicalRelationshipForm.IsAIdentifier == type)
					{
						// entity is swapped by is-a relationship 
						this._relationship = new IsALogicalRelationship(name, entityB, entityA, rolenameA, rolenameB);
					}
					if (LogicalRelationshipForm.OneToOneIdentifier == type)
					{
						this._relationship = new OneToOneLogicalRelationship(name, entityA, entityB, rolenameA, rolenameB,
							OneToOneLogicalRelationship.Cardinality.OnlyOne, OneToOneLogicalRelationship.Cardinality.OnlyOne);
					}
					else if (LogicalRelationshipForm.OneToManyIdentifier == type)
					{
						this._relationship = new OneToManyLogicalRelationship(name, entityA, entityB, rolenameA, rolenameB,
							OneToManyLogicalRelationship.CardinalityForOne.OnlyOne, OneToManyLogicalRelationship.CardinalityForMany.OneOrMore);
					}
					else if (LogicalRelationshipForm.ManyToManyIdentifier == type)
					{
						this._relationship = new ManyToManyLogicalRelationship(name, entityA, entityB, rolenameA, rolenameB,
							ManyToManyLogicalRelationship.Cardinality.OneOrMore, ManyToManyLogicalRelationship.Cardinality.OneOrMore);
					}
				}
				
				//--- set global properties ---
				this._relationship.Name = name;
				this._relationship.Entity1 = entityA;
				this._relationship.Entity2 = entityB;
				this._relationship.Rolename1 = rolenameA;
				this._relationship.Rolename2 = rolenameB;
				
				//--- set specific properties ---
				if (LogicalRelationshipForm.IsAIdentifier == type)
				{
					IsALogicalRelationship rel = (IsALogicalRelationship)this._relationship;
					// on is-a its swapped
					this._relationship.Entity1 = entityB;
					this._relationship.Entity2 = entityA;
					this._relationship.Rolename1 = rolenameB;
					this._relationship.Rolename2 = rolenameA;
				}
				if (LogicalRelationshipForm.OneToOneIdentifier == type)
				{
					OneToOneLogicalRelationship rel = (OneToOneLogicalRelationship)this._relationship;
					rel.Cardinality1 = (OneToOneLogicalRelationship.Cardinality)Enum.Parse(typeof(OneToOneLogicalRelationship.Cardinality),
						this.cardinalityACombo.SelectedItem.ToString());
					rel.Cardinality2 = (OneToOneLogicalRelationship.Cardinality)Enum.Parse(typeof(OneToOneLogicalRelationship.Cardinality),
						this.cardinalityBCombo.SelectedItem.ToString());
				}
				else if (LogicalRelationshipForm.OneToManyIdentifier == type)
				{
					OneToManyLogicalRelationship rel = (OneToManyLogicalRelationship)this._relationship;
					rel.Cardinality1 = (OneToManyLogicalRelationship.CardinalityForOne)Enum.Parse(typeof(OneToManyLogicalRelationship.CardinalityForOne),
						this.cardinalityACombo.SelectedItem.ToString());
					rel.Cardinality2 = (OneToManyLogicalRelationship.CardinalityForMany)Enum.Parse(typeof(OneToManyLogicalRelationship.CardinalityForMany),
						this.cardinalityBCombo.SelectedItem.ToString());
					rel.ExactValue = Convert.ToInt32(this.exactlyBUpDown.Value);
				}
				else if (LogicalRelationshipForm.ManyToManyIdentifier == type)
				{
					ManyToManyLogicalRelationship rel = (ManyToManyLogicalRelationship)this._relationship;
					rel.Cardinality1 = (ManyToManyLogicalRelationship.Cardinality)Enum.Parse(typeof(ManyToManyLogicalRelationship.Cardinality),
						this.cardinalityACombo.SelectedItem.ToString());
					rel.Cardinality2 = (ManyToManyLogicalRelationship.Cardinality)Enum.Parse(typeof(ManyToManyLogicalRelationship.Cardinality),
						this.cardinalityBCombo.SelectedItem.ToString());
					rel.ExactValue1 = Convert.ToInt32(this.exactlyAUpDown.Value);
					rel.ExactValue2 = Convert.ToInt32(this.exactlyBUpDown.Value);
				}
	
				this.DialogResult = DialogResult.OK;
				this.Close();
			}
			catch (Exception ex)
			{
				MessageForm.ShowDialog(ex);
			}
		}
		
        /// <summary>
        /// Event handler.
        /// </summary>
        /// <param name="sender">Sender object.</param>
        /// <param name="e">EventArgs</param>
		private void CancelButtonClick(object sender, EventArgs e)
		{
			try
			{
				this.DialogResult = DialogResult.Cancel;
				this.Close();
			}
			catch (Exception ex)
			{
				MessageForm.ShowDialog(ex);
			}
		}
		
		/// <summary>
		/// Clean the data on the form for primitive state.
		/// </summary>
		private void CleanData()
		{
			// clean
			this._isLoaded = false;
			this.typeCombo.Enabled = true;
			this.typeCombo.DataSource = null;
			this.entityACombo.DataSource = null;
			this.entityBCombo.DataSource = null;
			
			this.nameTextBox.Clear();
			this.roleATextBox.Clear();
			this.roleBTextBox.Clear();
			this.typeCombo.Items.Clear();
			this.entityACombo.Items.Clear();
			this.entityBCombo.Items.Clear();
			
			// fill types
			this.typeCombo.Items.Add(LogicalRelationshipForm.OneToOneIdentifier);
			this.typeCombo.Items.Add(LogicalRelationshipForm.OneToManyIdentifier);
			this.typeCombo.Items.Add(LogicalRelationshipForm.ManyToManyIdentifier);
			this.typeCombo.Items.Add(LogicalRelationshipForm.IsAIdentifier);
			this.typeCombo.SelectedIndex = 0;
			
			// fill entites
			SortedList<string, LogicalEntity> entityAList = new SortedList<string, LogicalEntity>();
			SortedList<string, LogicalEntity> entityBList = new SortedList<string, LogicalEntity>();
			IEnumerator entitiesEnum = DataContainer.Instance.LogicalSchema.GetEntities();
			while (entitiesEnum.MoveNext())
			{
				LogicalEntity entity = (LogicalEntity)entitiesEnum.Current;
				string uniqueListName = entity.Name;
				int counter = 0;
				while (entityAList.ContainsKey(uniqueListName))
				{
					uniqueListName = string.Format("{0}({1})", entity.Name, ++counter);
				}
				entityAList.Add(uniqueListName, entity);
				entityBList.Add(uniqueListName, entity);
			}
			
			this.entityACombo.ValueMember = "Value";
			this.entityACombo.DisplayMember = "Key";
			this.entityACombo.DataSource = new BindingSource(entityAList, null);
			if (entityAList.Count > 0)
			{
				this.entityACombo.SelectedIndex = 0;
			}

			this.entityBCombo.ValueMember = "Value";
			this.entityBCombo.DisplayMember = "Key";
			this.entityBCombo.DataSource = new BindingSource(entityBList, null);
			if (entityBList.Count > 0)
			{
				this.entityBCombo.SelectedIndex = 0;
			}
		}
		
		/// <summary>
		/// Load the data into the form.
		/// </summary>
		private void LoadData()
		{
			if (null != this._relationship)
			{
				this._isLoaded = true;
				this.typeCombo.Enabled = false;
				
				this.entityACombo.SelectedValue = this._relationship.Entity1;
				this.entityBCombo.SelectedValue = this._relationship.Entity2;
				
				this.nameTextBox.Text = this._relationship.Name;
				this.roleATextBox.Text = this._relationship.Rolename1;
				this.roleBTextBox.Text = this._relationship.Rolename2;
				
				// define the autorolename switch
				this._autoRolenameA = (this.roleATextBox.Text == this.CreateAutoRolename(this.entityACombo));
				this._autoRolenameB = (this.roleBTextBox.Text == this.CreateAutoRolename(this.entityBCombo));
				
				if (this._relationship is IsALogicalRelationship)
				{
					IsALogicalRelationship castedRel = (IsALogicalRelationship)this._relationship;
					this.typeCombo.SelectedItem = LogicalRelationshipForm.IsAIdentifier;
					// on is-a its swapped
					this.entityACombo.SelectedValue = this._relationship.Entity2;
					this.entityBCombo.SelectedValue = this._relationship.Entity1;
					this.roleATextBox.Text = this._relationship.Rolename2;
					this.roleBTextBox.Text = this._relationship.Rolename1;
				}
				else if (this._relationship is OneToOneLogicalRelationship)
				{
					OneToOneLogicalRelationship castedRel = (OneToOneLogicalRelationship)this._relationship;
					this.typeCombo.SelectedItem = LogicalRelationshipForm.OneToOneIdentifier;
					this.cardinalityACombo.SelectedItem = castedRel.Cardinality1.ToString();
					this.cardinalityBCombo.SelectedItem = castedRel.Cardinality2.ToString();
				}
				else if (this._relationship is OneToManyLogicalRelationship)
				{
					OneToManyLogicalRelationship castedRel = (OneToManyLogicalRelationship)this._relationship;
					this.typeCombo.SelectedItem = LogicalRelationshipForm.OneToManyIdentifier;
					this.cardinalityACombo.SelectedItem = castedRel.Cardinality1.ToString();
					this.cardinalityBCombo.SelectedItem = castedRel.Cardinality2.ToString();
					this.exactlyBUpDown.Value = castedRel.ExactValue;
				}
				else if (this._relationship is ManyToManyLogicalRelationship)
				{
					ManyToManyLogicalRelationship castedRel = (ManyToManyLogicalRelationship)this._relationship;
					this.typeCombo.SelectedItem = LogicalRelationshipForm.ManyToManyIdentifier;
					this.cardinalityACombo.SelectedItem = castedRel.Cardinality1.ToString();
					this.cardinalityBCombo.SelectedItem = castedRel.Cardinality2.ToString();
					this.exactlyAUpDown.Value = castedRel.ExactValue1;
					this.exactlyBUpDown.Value = castedRel.ExactValue2;
				}
			}
			else
			{
				this._isLoaded = false;
			}
		}
		
        /// <summary>
        /// Event handler.
        /// </summary>
        /// <param name="sender">Sender object.</param>
        /// <param name="e">EventArgs</param>
		private void LogicalRelationshipFormLoad(object sender, EventArgs e)
		{
			try
			{
				this.CleanData();
				this.LoadData();
				this.nameTextBox.Focus();
			}
			catch (Exception ex)
			{
				MessageForm.ShowDialog(ex);
			}
		}
		
        /// <summary>
        /// Event handler.
        /// </summary>
        /// <param name="sender">Sender object.</param>
        /// <param name="e">EventArgs</param>
		private void TypeComboSelectedIndexChanged(object sender, EventArgs e)
		{
			try
			{
				if (-1 == this.typeCombo.SelectedIndex)
				{
					return;
				}
				
				this.cardinalityAGroup.Visible = true;
				this.cardinalityBGroup.Visible = true;
				this.cardinalityACombo.SelectedIndex = -1;
				this.cardinalityBCombo.SelectedIndex = -1;
				this.cardinalityACombo.DataSource = null;
				this.cardinalityBCombo.DataSource = null;
				this.cardinalityACombo.Items.Clear();
				this.cardinalityBCombo.Items.Clear();
				
				List<object> cardinalityAList = new List<object>();
				List<object> cardinalityBList = new List<object>();
				string selected = this.typeCombo.SelectedItem.ToString();
				if (LogicalRelationshipForm.IsAIdentifier == selected)
				{
					this.cardinalityAGroup.Visible = false;
					this.cardinalityBGroup.Visible = false;
				}
				if (LogicalRelationshipForm.OneToOneIdentifier == selected)
				{
					string[] names = Enum.GetNames(typeof(OneToOneLogicalRelationship.Cardinality));
					cardinalityAList.InsertRange(0, names);
					cardinalityBList.InsertRange(0, names);
				}
				else if (LogicalRelationshipForm.OneToManyIdentifier == selected)
				{
					string[] namesA = Enum.GetNames(typeof(OneToManyLogicalRelationship.CardinalityForOne));
					string[] namesB = Enum.GetNames(typeof(OneToManyLogicalRelationship.CardinalityForMany));
					cardinalityAList.InsertRange(0, namesA);
					cardinalityBList.InsertRange(0, namesB);
				}
				else if (LogicalRelationshipForm.ManyToManyIdentifier == selected)
				{
					string[] names = Enum.GetNames(typeof(ManyToManyLogicalRelationship.Cardinality));
					cardinalityAList.InsertRange(0, names);
					cardinalityBList.InsertRange(0, names);
				}
	
				this.cardinalityACombo.DataSource = cardinalityAList;
				this.cardinalityBCombo.DataSource = cardinalityBList;
			}
			catch (Exception ex)
			{
				MessageForm.ShowDialog(ex);
			}
		}
		
        /// <summary>
        /// Event handler.
        /// </summary>
        /// <param name="sender">Sender object.</param>
        /// <param name="e">EventArgs</param>
		private void CardinalityAComboSelectedIndexChanged(object sender, EventArgs e)
		{
			try
			{
				exactlyAUpDown.Visible = false;
				if (-1 != this.cardinalityACombo.SelectedIndex)
				{
					exactlyAUpDown.Visible = "Exactly" == this.cardinalityACombo.SelectedItem.ToString();
				}
			}
			catch (Exception ex)
			{
				MessageForm.ShowDialog(ex);
			}
		}
		
        /// <summary>
        /// Event handler.
        /// </summary>
        /// <param name="sender">Sender object.</param>
        /// <param name="e">EventArgs</param>
		private void CardinalityBComboSelectedIndexChanged(object sender, EventArgs e)
		{
			try
			{
				exactlyBUpDown.Visible = false;
				if (-1 != this.cardinalityBCombo.SelectedIndex)
				{
					exactlyBUpDown.Visible = "Exactly" == this.cardinalityBCombo.SelectedItem.ToString();
				}
			}
			catch (Exception ex)
			{
				MessageForm.ShowDialog(ex);
			}
		}
		
		/// <summary>
		/// Validate the data on the form and set the specified error provider for the
		/// invalide control.
		/// </summary>
		/// <returns>
		/// true indicates that the form is valid, false indicates that the form
		/// contains invalid data which sould not be used until corrected.
		/// </returns>
		private bool FormIsValidate()
		{
			bool isValid = true;
			this.errorProvider.Clear();
			
			if (-1 == this.typeCombo.SelectedIndex)
			{
				this.errorProvider.SetError(this.typeCombo, "Please choose the type.");
				isValid = false;
			}

			if (-1 == this.entityACombo.SelectedIndex)
			{
				this.errorProvider.SetError(this.entityACombo, "Please choose the entity.");
				isValid = false;
			}

			if (-1 == this.entityBCombo.SelectedIndex)
			{
				this.errorProvider.SetError(this.entityBCombo, "Please choose the entity.");
				isValid = false;
			}

			if (0 == this.nameTextBox.Text.Trim().Length)
			{
				this.errorProvider.SetError(this.nameTextBox, "Please fill in the name.");
				isValid = false;
			}
			
			// check is-a relation on the same entitiy
			if (LogicalRelationshipForm.IsAIdentifier == this.typeCombo.SelectedItem.ToString())
			{
				KeyValuePair<string, LogicalEntity> pairA = (KeyValuePair<string, LogicalEntity>)this.entityACombo.SelectedItem;
				KeyValuePair<string, LogicalEntity> pairB = (KeyValuePair<string, LogicalEntity>)this.entityBCombo.SelectedItem;
				bool isCircular = IsALogicalRelationship.HasCircularReferences(pairA.Value, pairB.Value);
				if (isCircular)
				{
					this.errorProvider.SetError(this.typeCombo, "Please choose another type or entity. This combination would lead to a circular relationship.");
					isValid = false;
				}
				
				bool hasMultiInheritance = IsALogicalRelationship.HasMultipleInheritance(pairA.Value, pairB.Value, this._isLoaded);
				if (hasMultiInheritance)
				{
					this.errorProvider.SetError(this.entityACombo, "Please choose another entity. Multiple inheritance is not allowed.");
					isValid = false;
				}
			}
			
			// check cardinality except vor is-a
			if (LogicalRelationshipForm.IsAIdentifier != this.typeCombo.SelectedItem.ToString())
			{
				if (-1 == this.cardinalityACombo.SelectedIndex
				   || -1 == this.cardinalityBCombo.SelectedIndex)
				{
					this.errorProvider.SetError(this.cardinalityACombo, "Please choose the cardinality.");
					this.errorProvider.SetError(this.cardinalityBCombo, "Please choose the cardinality.");
					isValid = false;
				}
			}
			
			return isValid;
		}
		
		/// <summary>
		/// Create automatical rolenames based on the entity combobox
		/// </summary>
		/// <param name="combo">Combobox containging KeyValuePair with entities.</param>
		/// <returns>The created rolename.</returns>
		private string CreateAutoRolename(ComboBox combo)
		{
			string roleName = string.Empty;
			string name = this.nameTextBox.Text.Trim();
			if (0 != name.Length && -1 != combo.SelectedIndex)
			{
				KeyValuePair<string, LogicalEntity> pair = (KeyValuePair<string, LogicalEntity>)combo.SelectedItem;
				roleName = string.Format("{0}{1}", Helper.ToTitleCase(pair.Value.Name), Helper.ToTitleCase(name));
			}
			return roleName;
		}
		
		/// <summary>
		/// Fill automatical rolenames if needed.
		/// </summary>
		private void FillAutoRolenames()
		{
			// enable auto name again if needed
			if (0 == this.roleATextBox.Text.Trim().Length)
			{
				this._autoRolenameA = true;
			}
			if (0 == this.roleBTextBox.Text.Trim().Length)
			{
				this._autoRolenameB = true;
			}

			// create auto name
			if (this._autoRolenameA)
			{
				this.roleATextBox.Text = this.CreateAutoRolename(this.entityACombo);
			}
			
			if (this._autoRolenameB)
			{
				this.roleBTextBox.Text = this.CreateAutoRolename(this.entityBCombo);
			}
		}
		
        /// <summary>
        /// Event handler.
        /// </summary>
        /// <param name="sender">Sender object.</param>
        /// <param name="e">EventArgs</param>
		private void NameTextBoxTextChanged(object sender, EventArgs e)
		{
			try
			{
				this.FillAutoRolenames();
			}
			catch (Exception ex)
			{
				MessageForm.ShowDialog(ex);
			}
		}
		
        /// <summary>
        /// Event handler.
        /// </summary>
        /// <param name="sender">Sender object.</param>
        /// <param name="e">EventArgs</param>
		private void EntityAComboSelectedIndexChanged(object sender, EventArgs e)
		{
			try
			{
				this.FillAutoRolenames();
			}
			catch (Exception ex)
			{
				MessageForm.ShowDialog(ex);
			}
		}
		
        /// <summary>
        /// Event handler.
        /// </summary>
        /// <param name="sender">Sender object.</param>
        /// <param name="e">EventArgs</param>
		private void EntityBComboSelectedIndexChanged(object sender, EventArgs e)
		{
			try
			{
				this.FillAutoRolenames();
			}
			catch (Exception ex)
			{
				MessageForm.ShowDialog(ex);
			}
		}
		
        /// <summary>
        /// Event handler.
        /// </summary>
        /// <param name="sender">Sender object.</param>
        /// <param name="e">EventArgs</param>
		private void RoleATextBoxLeave(object sender, EventArgs e)
		{
			try
			{
				if (0 == this.roleATextBox.Text.Trim().Length)
				{
					this._autoRolenameA = true;
					this.FillAutoRolenames();
				}
				else
				{
					this._autoRolenameA = (this.roleATextBox.Text == this.CreateAutoRolename(this.entityACombo));
				}
			}
			catch (Exception ex)
			{
				MessageForm.ShowDialog(ex);
			}
		}
		
        /// <summary>
        /// Event handler.
        /// </summary>
        /// <param name="sender">Sender object.</param>
        /// <param name="e">EventArgs</param>
		private void RoleBTextBoxLeave(object sender, EventArgs e)
		{
			try
			{
				if (0 == this.roleBTextBox.Text.Trim().Length)
				{
					this._autoRolenameB = true;
					this.FillAutoRolenames();
				}
				else
				{
					this._autoRolenameB = (this.roleBTextBox.Text == this.CreateAutoRolename(this.entityBCombo));
				}
			}
			catch (Exception ex)
			{
				MessageForm.ShowDialog(ex);
			}
		}
		#endregion
	}
}
