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

namespace dataMagus.Common.Logical
{
	/// <summary>
	/// This class extends a logical relationship for Is-a implementation.
	/// </summary>
	[Serializable]
	public class IsALogicalRelationship : LogicalRelationship
	{
		#region Constructors / Destructors
		/// <summary>
		/// Constructor
		/// </summary>
		/// <param name="name">Name of the relationship.</param>
		/// <param name="entity1">Logical first entity.</param>
		/// <param name="entity2">Logical second entity.</param>
		/// <param name="rolename1">Rolename of the first entity.</param>
		/// <param name="rolename2">Rolename of the second entity.</param>
		public IsALogicalRelationship(string name, LogicalEntity entity1, LogicalEntity entity2,
			string rolename1, string rolename2)
			: base(name, entity1, entity2, rolename1, rolename2)
		{
            // If entity2 has any key attributes, we need to remove them because this is not allowed
            // with a is-a relationship
            if (null != entity2 && 0 < entity2.CountKeyAttributes)
            {
                List<LogicalAttribute> keysToDelete = new List<LogicalAttribute>();
                IEnumerator keyEnumerator = entity2.GetKeyAttributes();
                while (keyEnumerator.MoveNext())
                {
                    LogicalAttribute key = (LogicalAttribute)keyEnumerator.Current;
                    keysToDelete.Add(key);
                }
                foreach (LogicalAttribute keyAttribute in keysToDelete)
                {
                    entity2.RemoveKeyAttribute(keyAttribute);
                }
            }
		}

		/// <summary>
		/// Default constructor
		/// </summary>
		/// <remarks>Only used for the serialization.</remarks>
        private IsALogicalRelationship()
        { 
        }
		#endregion
		
		#region Methodes
		/// <summary>
		/// Creates an is-a relationship object from a reader.
		/// </summary>
		/// <remarks>Reader must stand directly before the object.</remarks>
		/// <param name="reader">Reader containing the object.</param>
		/// <returns>The created instance.</returns>
        public static IsALogicalRelationship CreateInstance(System.Xml.XmlReader reader)
        {
            IsALogicalRelationship instance = new IsALogicalRelationship();
            instance.ReadXml(reader);
            return instance;
        }
        
        /// <summary>
        /// Look for the root entity of a is-a-relationship chain.
        /// </summary>
        /// <param name="entity">Entity to start searching.</param>
        /// <returns>Root entity or null if nothing has been found.</returns>
        public static LogicalEntity GetRootIsAEntity(LogicalEntity entity)
        {
            IEnumerator relEnumerator = DataContainer.Instance.LogicalSchema.GetRelationships();
            while (relEnumerator.MoveNext())
            { 
                LogicalRelationship rel = (LogicalRelationship)relEnumerator.Current;
                if (rel is IsALogicalRelationship)
                {
                    if (rel.Entity2 == entity)
                    {
                        return IsALogicalRelationship.GetRootIsAEntity(rel.Entity1);
                    }
                    else if (rel.Entity1 == entity)
                    {
                        return entity;
                    }
                }
            }
            return null;
        }
        
        /// <summary>
        /// Checks if an entity would have multiple inheritance.
        /// </summary>
        /// <param name="potentialChild">Potential child entity.</param>
        /// <param name="potentialParent">Potential new parent entity.</param>
        /// <param name="ignoreParent">Indicator if the parent entity should be ignored.</param>
        /// <returns>true if there will be a multiple inheritance, otherwise false.</returns>
        public static bool HasMultipleInheritance(LogicalEntity potentialChild, LogicalEntity potentialParent, bool ignoreParent)
        {
    		IEnumerator relEnum = potentialChild.GetRelationships();
	    	while (relEnum.MoveNext())
	    	{
	    		LogicalRelationship rel = (LogicalRelationship)relEnum.Current;
	    		// check for is-a relationship with the child as child and the new parent
	    		// is not a potential existing one.
	    		if (rel is IsALogicalRelationship 
	    		    && rel.Entity2 == potentialChild)
	    		{
	    			if (ignoreParent && rel.Entity1 == potentialParent)
	    			{
	    				// found parent is-a relationship -> ignore
	    				continue;
	    			}
    				return true;
	    		}
	    	}
        	return false;
        }
        
        /// <summary>
        /// Checks if between two entities would be a potential circulare reference.
        /// </summary>
        /// <param name="potentialChild">Potential child entity.</param>
        /// <param name="potentialParent">Potential new parent entity.</param>
        /// <returns>true if there will be circulare references, otherwise false.</returns>
        public static bool HasCircularReferences(LogicalEntity potentialChild, LogicalEntity potentialParent)
        {
        	// handle special case if there are currently no relationships
        	// and both entities are the same
        	if (potentialChild == potentialParent)
        	{
        		return true;
        	}
        	
        	LogicalEntity currentEntity = potentialParent;
        	bool hasMoreToProcess = true;
        	while (hasMoreToProcess)
        	{
        		hasMoreToProcess = false;
        		// find next isa realationship
        		IEnumerator relEnum = currentEntity.GetRelationships();
		    	while (relEnum.MoveNext())
		    	{
		    		LogicalRelationship rel = (LogicalRelationship)relEnum.Current;
		    		// check for is-a relationship
		    		if (rel is IsALogicalRelationship && rel.Entity2 == currentEntity)
		    		{
		    			hasMoreToProcess = true;
		    			currentEntity = rel.Entity1;
		    			break;
		    		}
		    	}
		    	
        		if (currentEntity == potentialChild)
        		{
        			return true;
        		}
        	}
        	return false;
        }
        
        /// <summary>
        /// Reads back an instance of this class from a reader.
        /// </summary>
        /// <remarks>Reader must stand on a IsArelationship-Element.</remarks>
        /// <param name="reader">Reader object to read from.</param>
        public override void ReadXml(System.Xml.XmlReader reader)
        {
            base.BeginInit();
            if (reader.IsStartElement("IsARelationship"))
            {
                base.ReadXml(reader);
            }
            base.EndInit(false);
        }

        /// <summary>
        /// Writes a the instace of this class to a writer.
        /// </summary>
        /// <param name="writer">Writer object to write into.</param>
        public override void WriteXml(System.Xml.XmlWriter writer)
        {
            writer.WriteStartElement("IsARelationship");
            base.WriteXml(writer);
            writer.WriteEndElement();
        }
		#endregion
	}
}
