#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.ComponentModel;
using System.Collections.Generic;
using System.Text;
using dataMagus.Common.Logical;
using System.Collections;
#endregion

namespace dataMagus.Common.Physical.Relational
{
	/// <summary>
	/// This class is the abstraction of a physical relational relationship.
	/// </summary>
    [Serializable]
    public abstract class PhysicalRelationalRelationship : PhysicalElement
    {
        #region Constants
        /// <summary>
        /// Name of the Rolename1 property
        /// </summary>
        private const string Rolename1PropertyName = "Rolename1";

        /// <summary>
        /// Name of the Rolename2 property
        /// </summary>
        private const string Rolename2PropertyName = "Rolename2";
        #endregion

        #region Members
        /// <summary>
        /// First table.
        /// </summary>
        private PhysicalRelationalTable _table1;
        
        /// <summary>
        /// Second table.
        /// </summary>
        private PhysicalRelationalTable _table2;
        
        /// <summary>
        /// Rolename of the first class.
        /// </summary>
        private string _rolename1;
        
        /// <summary>
        /// Rolename of the second class.
        /// </summary>
        private string _rolename2;

        /// <summary>
        /// Holds the mapping between the primary keys and its foreign keys.
        /// </summary>
        private Dictionary<PhysicalRelationalColumn, PhysicalRelationalColumn> _columnMapping = new Dictionary<PhysicalRelationalColumn,PhysicalRelationalColumn>();
        #endregion

        #region Constructors / Destructors
        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="schema">Physical relational schema.</param>
        /// <param name="id">Unique id of the object.</param>
        /// <param name="origin">Origin element the relationship is based on.</param>
        /// <param name="name">Name of the relationship.</param>
        /// <param name="table1">First table.</param>
        /// <param name="table2">Second table.</param>
        /// <param name="rolename1">Rolename of the first table.</param>
        /// <param name="rolename2">Rolename of the second table.</param>
        public PhysicalRelationalRelationship(PhysicalRelationalSchema schema, string id, IElement origin, string name,
            PhysicalRelationalTable table1, PhysicalRelationalTable table2,
            string rolename1, string rolename2)
            : base(schema, id, origin, name)
        {
            this._table1 = table1;
            this._table2 = table2;
            this._rolename1 = rolename1;
            this._rolename2 = rolename2;

            // Restore physical changes if available
            object rolename1ToRestore = DataContainer.Instance.PhysicalData.GetRestoreDataItem(base._schema, this.Id, PhysicalRelationalRelationship.Rolename1PropertyName);
            if (null != rolename1ToRestore)
            {
                this._rolename1 = (string)rolename1ToRestore;
            }
            object rolename2ToRestore = DataContainer.Instance.PhysicalData.GetRestoreDataItem(base._schema, this.Id, PhysicalRelationalRelationship.Rolename2PropertyName);
            if (null != rolename2ToRestore)
            {
                this._rolename2 = (string)rolename2ToRestore;
            }
        }
        #endregion

        #region Properties
        /// <summary>
        /// First table.
        /// </summary>
        public PhysicalRelationalTable Table1
        {
            get { return this._table1; }
        }

        /// <summary>
        /// Second table.
        /// </summary>
        public PhysicalRelationalTable Table2
        {
            get { return this._table2; }
        }

        /// <summary>
		/// Indicates if the relationship is a self relation.
        /// </summary>
		public bool IsSelfRelationship
		{
			get { return this._table1 == this._table2; }
		}
        
        /// <summary>
        /// Rolename of the first class.
        /// </summary>
        public string Rolename1
        {
            get { return this._rolename1; }
            set 
            {
                if (this._rolename1 != value)
                {
                    this.EnsureUpdateOriginalRestoreDataItem(base._schema, this, PhysicalRelationalRelationship.Rolename1PropertyName, value);
                    this._rolename1 = value; 
                    this.FireElementChangedEvent(this, new ElementEventArgs(this, ElementChangedAction.ChangedProperty));
                }
            }
        }

        /// <summary>
        /// Rolename of the second class.
        /// </summary>
        public string Rolename2
        {
            get { return this._rolename2; }
            set 
            { 
                if (this._rolename2 != value)
                {
                    this.EnsureUpdateOriginalRestoreDataItem(base._schema, this, PhysicalRelationalRelationship.Rolename2PropertyName, value);
                    this._rolename2 = value;
                    this.FireElementChangedEvent(this, new ElementEventArgs(this, ElementChangedAction.ChangedProperty));
                }
            }
        }
        #endregion

        #region Methodes
        /// <summary>
        /// Add a new column mapping.
        /// </summary>
        /// <param name="column1">Primary key column.</param>
        /// <param name="column2">Foreign key column.</param>
        public void AddColumnMapping(PhysicalRelationalColumn column1, PhysicalRelationalColumn column2)
        {
            if (!this._columnMapping.ContainsKey(column1))
            {
                this._columnMapping.Add(column1, column2);
            }
        }

        /// <summary>
        /// Get all column mappings.
        /// </summary>
        /// <returns>Enumerator to parse.</returns>
        public IEnumerator GetColumnMappings()
        {
            return this._columnMapping.GetEnumerator();
        }

        /// <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)
        {
            base.WriteXml(writer);

            writer.WriteAttributeString("table1", this.Table1.Id);
            writer.WriteAttributeString("table2", this.Table2.Id);
            writer.WriteAttributeString("rolename1", this.Rolename1);
            writer.WriteAttributeString("rolename2", this.Rolename2);

            writer.WriteStartElement("ColumnMappings");
            IEnumerator mappingEnumerator = this._columnMapping.GetEnumerator();
            while (mappingEnumerator.MoveNext())
            {
                KeyValuePair<PhysicalRelationalColumn, PhysicalRelationalColumn> mapping = (KeyValuePair<PhysicalRelationalColumn, PhysicalRelationalColumn>)mappingEnumerator.Current;
                writer.WriteStartElement("Mapping");
                
                writer.WriteStartElement("Column1");
                mapping.Key.WriteXml(writer);
                writer.WriteEndElement();

                writer.WriteStartElement("Column2");
                mapping.Value.WriteXml(writer);
                writer.WriteEndElement();

                writer.WriteEndElement();
            }
            writer.WriteEndElement();
        }
        #endregion
    }
}
