#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.IO;
using System.ComponentModel;
using System.Drawing;
using System.Xml.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
#endregion

namespace dataMagus.Common.Logical
{
	/// <summary>
	/// This class represents a abstract logical element.
	/// </summary>
	[Serializable]
    public abstract class LogicalElement : IElement, IXmlSerializable
	{
		#region Members
		/// <summary>
		/// Id of the element.
		/// </summary>
		private string _id;
		
		/// <summary>
		/// Name of the element.
		/// </summary>
		private string _name;
		
		/// <summary>
		/// Location of the element.
		/// </summary>
		private Point _location = Point.Empty;
		
		/// <summary>
		/// Indicates if the element is in InitMode.
		/// </summary>
		/// <remarks>InitMode will suppress all changing events.</remarks>
		protected bool _initMode = false;
		
		/// <summary>
		/// Indicates if the element has been cloned.
		/// </summary>
        /// <remarks>Beware: events on clones will be suppressed.</remarks>
		private bool _isClone = false;
		#endregion
		
		#region Constructors / Destructors
		/// <summary>
		/// Default constructor
		/// </summary>
		protected LogicalElement()
		{
		}

		/// <summary>
		/// Constructor
		/// </summary>
		/// <param name="name">Name of the element.</param>
		protected LogicalElement(string name)
		{
			this._id = Guid.NewGuid().ToString();
			this._name = name;
		}
		#endregion
		
		#region Properties
		/// <summary>
		/// Id of the element.
		/// </summary>
		[Browsable(false)]
		public string Id 
		{ 
			get { return this._id; }
		}
		
		/// <summary>
		/// Name of the element.
		/// </summary>
		public string Name
		{
			get { return this._name; }
			set 
			{ 
				if (this._name != value)
				{
                    this._name = this.EnsureName(value);
					this.FireElementChangedEvent(this, new ElementEventArgs(this, ElementChangedAction.ChangedProperty));
				}
			}
		}
		
		/// <summary>
		/// Indicates if the element is in InitMode.
		/// </summary>
		/// <remarks>InitMode will suppress all changing events.</remarks>
		[Browsable(false)]
		public bool InitMode
		{
			get { return this._initMode; }
		}

		/// <summary>
		/// Indicates if the element has been cloned.
		/// </summary>
        /// <remarks>Beware: events on clones will be suppressed.</remarks>
		[Browsable(false)]
		public bool IsClone 
		{ 
			get { return this._isClone; }
		}

		/// <summary>
		/// Location of the element.
		/// </summary>
		[Browsable(false)]
		public Point Location
		{
			get { return this._location; }
			set 
			{ 
				if (this._location != value)
				{
					this._location = value; 
					this.FireElementChangedEvent(this, new ElementEventArgs(this, ElementChangedAction.ChangedProperty));
				}
			}
		}
		#endregion
		
		#region Events
		/// <summary>
		/// Event when the element has been changed.
		/// </summary>
        // Very important: Mark this event as NonSerialized!! The compiler, will behind the scences,
        // create a hidden field for us to maintain to a collection of all your listeners. 
        // But our listeners are type of UserControl which can not be serialized. Therefore,
        // we have to explicitely mark as NonSerialized. Esoteric indeed!
		[field:NonSerialized]	// VIC = Very Important Code!
		public event ElementChangedHandler ElementChanged;
		#endregion
		
		#region Methodes
		/// <summary>
		/// String representation of the object.
		/// </summary>
		/// <returns>Name of the element.</returns>
		public override string ToString()
		{
			return this.Name;
		}

		/// <summary>
		/// Ensures that the object has a name.
		/// </summary>
		/// <remarks>Spaces will be replaced with nothing.</remarks>
		/// <param name="name">Name to check.</param>
		/// <returns>New name.</returns>
		private string EnsureName(string name)
        {
            if (null == name)
            {
                name = String.Empty;
            }
            return name.Replace(" ", "");
        }

		/// <summary>
		/// Begin initialization mode.
		/// </summary>
		/// <remarks>Prevents event handling.</remarks>
		public virtual void BeginInit()
        {
            this._initMode = true;
        }

		/// <summary>
		/// End initialization mode.
		/// </summary>
		/// <remarks>Event handling will continue.</remarks>
		/// <param name="forceEvent">Indicates if an Undefined event has to be fired to inform other listeners.</param>
        public virtual void EndInit(bool forceEvent)
        {
            this._initMode = false;
            if (forceEvent)
            {
                this.FireElementChangedEvent(this, new ElementEventArgs(this, ElementChangedAction.Undefined));
            }
        }

        /// <summary>
        /// Gets the parent object.
        /// </summary>
        /// <returns>Always null, method has to be overriden by extended implementation.</returns>
        public virtual IElement GetParent()
        {
        	return null;
        }
		
        /// <summary>
        /// Fires an element changed event.
        /// </summary>
        /// <param name="sender">Sender object.</param>
        /// <param name="args">ElementEventArgs.</param>
        public void FireElementChangedEvent(object sender, ElementEventArgs args)
        {
        	if (!this.InitMode && !this._isClone && null != this.ElementChanged)
        	{
        		this.ElementChanged(sender, args);
        	}
        }
		#endregion

        #region IXmlSerializable Members
        /// <summary>
        /// Gets the xml schema object.
        /// </summary>
        /// <returns>Always null.</returns>
        public System.Xml.Schema.XmlSchema GetSchema()
        {
            return null;
        }

        /// <summary>
        /// Reads back an instance of this class from a reader.
        /// </summary>
        /// <param name="reader">Reader object to read from.</param>
        public virtual void ReadXml(System.Xml.XmlReader reader)
        {
            this.BeginInit();

            this._id = reader.GetAttribute("id");
            this.Name = reader.GetAttribute("name");
            this._location.X = Convert.ToInt32(reader.GetAttribute("locationX"));
            this._location.Y = Convert.ToInt32(reader.GetAttribute("locationY"));

            this.EndInit(false);
        }

        /// <summary>
        /// Writes a the instace of this class to a writer.
        /// </summary>
        /// <param name="writer">Writer object to write into.</param>
        public virtual void WriteXml(System.Xml.XmlWriter writer)
        {
            writer.WriteAttributeString("id", this.Id);
            writer.WriteAttributeString("name", this.Name);
            writer.WriteAttributeString("locationX", this.Location.X.ToString());
            writer.WriteAttributeString("locationY", this.Location.Y.ToString());
        }
        #endregion
        
        #region ICloneable Members
        /// <summary>
        /// Clones the element.
        /// </summary>
        /// <remarks>Beware: events on clones will be suppressed.</remarks>
        /// <returns>Cloned object.</returns>
        public object Clone()
        { 
            BinaryFormatter binaryFormatter = new BinaryFormatter();
            MemoryStream stream = new MemoryStream();
            binaryFormatter.Serialize(stream, this);
            stream.Seek(0, SeekOrigin.Begin);
            LogicalElement cloneElement = (LogicalElement)binaryFormatter.Deserialize(stream);
            cloneElement._isClone = true;
            return cloneElement;
        }
        #endregion
    }
}
