using System;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Runtime.Serialization.Formatters;
using System.Windows.Forms;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Collections;
using System.Configuration.Assemblies;
using System.Threading;
using System.Xml.Serialization;
using System.Xml;

using CategoryTheory;
using MathGraph;

namespace DiagramUI
{
	/// <summary>
	/// Component that shows the object on desktop
	/// </summary>
	[Serializable()]
	public class ObjectLabel : NamedComponent, ISerializable, IXmlElementCreator, IObjectLabel
	{
		/// <summary>
		/// Width
		/// </summary>
		protected int width = 100;

		/// <summary>
		/// Height
		/// </summary>
		protected int height = 100;

		/// <summary>
		/// Height of caption indicator
		/// </summary>
		protected int captionHeight = 30;

		/// <summary>
		/// The "is moved" flag
		/// </summary>
		protected bool isMoved;

		/// <summary>
		/// Old mouse x position
		/// </summary>
		protected int mouseX;

		/// <summary>
		/// Old mouse y position
		/// </summary>
		protected int mouseY;

		/// <summary>
		/// The correspond object
		/// </summary>
		protected ICategoryObject theObject;

		/// <summary>
		/// Brush for caption foreground
		/// </summary>
		protected Brush captionBrush;

		/// <summary>
		/// The pen of border
		/// </summary>
		protected Pen borderPen;
		
		/// <summary>
		/// Brush for caption foreground for inactive object
		/// </summary>
		protected Brush captionInactiveBrush;

		/// <summary>
		/// The pen of border for inactive object
		/// </summary>
		protected Pen borderInactivePen;

		/// <summary>
		/// Auxiliary variable
		/// </summary>
		protected bool arrowSelected;


		/// <summary>
		/// The events initialized flag
		/// </summary>
		protected bool eventsInitialized = false;


		/// <summary>
		/// Constructor
		/// </summary>
		private ObjectLabel()
		{
		}

		/// <summary>
		/// Construcor from correspond button
		/// </summary>
		/// <param name="button">Correspond button</param>
		public ObjectLabel(PaletteButton button) : base(button)
		{
			Initialize();
		}

		
		/// <summary>
		/// Serialization constructor
		/// </summary>
		/// <param name="info">Serialization info</param>
		/// <param name="context">Streaming context</param>
		public ObjectLabel(SerializationInfo info, StreamingContext context) : base(info, context)
		{
			Left = (int)info.GetValue("Left", typeof(int));
			Top = (int)info.GetValue("Top", typeof(int));
			theObject = (ICategoryObject)info.GetValue("Object", typeof(object));
			if (true)
			{
				IAssociatedObject obj = theObject as IAssociatedObject;
				obj.Object = this;
			}
			
		}

		
		/// <summary>
		/// ISerializable interface implementation
		/// </summary>
		/// <param name="info">Serialization info</param>
		/// <param name="context">Streaming context</param>
		new public void GetObjectData(SerializationInfo info, StreamingContext context)
		{
			base.GetObjectData(info, context);
			info.AddValue("Left", Left);
			info.AddValue("Top", Top);
			info.AddValue("Object", theObject);
		}

		#region INamedComponent Members

		public override string Name
		{
			get
			{
				return ComponentName;
			}
		}

		string INamedComponent.Kind
		{
			get
			{
				return stringKind;
			}
		}

		public override string Type
		{
			get
			{
				return type;
			}
		}

		void DiagramUI.INamedComponent.Remove()
		{
		}

		public override int X
		{
			get
			{
				return Left;
			}
			set
			{
				Left = value;
			}
		}

		public override int Y
		{
			get
			{
				return Top;
			}
			set
			{
				Top = value;
			}
		}

		IDesktop INamedComponent.Desktop
		{
			get
			{
				return Parent as IDesktop;
			}
			set
			{
			}
		}

		#endregion


		/// <summary>
		/// Desktop
		/// </summary>
		public override PanelDesktop Desktop
		{
			get
			{
				return Parent as PanelDesktop;
			}
		}




		
		/// <summary>
		/// The associated object
		/// </summary>
		public ICategoryObject Object
		{
			get
			{
                if (!PureObjectLabel.Wrappers)
                {
                    if (theObject is CategoryObjectWrapper)
                    {
                        CategoryObjectWrapper wrapper = theObject as CategoryObjectWrapper;
                        ICategoryObject ob = wrapper.TheObject;
                        if (ob != null)
                        {
                            return ob;
                        }
                    }
                }
				return theObject;
			}
			set
			{
				theObject = value;
			}
		}
		
		/// <summary>
		/// Removes itself
		/// </summary>
		/// <param name="formRemove">The "should remove properties editor" flag</param>
		public virtual void Remove(bool formRemove)
		{
			Desktop.Remove(this);
			if (formRemove)
			{
				RemoveForm();
			}
			if (Object is IRemovableObject)
			{
				IRemovableObject obj = Object as IRemovableObject;
				obj.RemoveObject();
			}
			theObject = null;
			GC.Collect();
		}

		/// <summary>
		/// Updates all associated properties editors
		/// </summary>
		public override void UpdateForms()
		{
			UpdateForm();
			ArrayList pairs = Desktop.Pairs;
			for (int i = 0; i < pairs.Count; i++)
			{
				ObjectsPair pair = pairs[i] as ObjectsPair;
				if (!pair.Belongs(this))
				{
					continue;
				}
				pair.UpdateForms();
			}
		}

		/// <summary>
		/// The selection flag
		/// </summary>
		public override bool Selected
		{
			get
			{
				return selected;
			}
			set
			{
				if (selected != value)
				{
					selected = value;
					Refresh();
				}
			}
		}

		/// <summary>
		/// Auxiliary property
		/// </summary>
		public bool ArrowSelected
		{
			get
			{
				return arrowSelected;
			}
			set
			{
				arrowSelected = value;
			}
		}

		/// <summary>
		/// Initialization
		/// </summary>
		public virtual void Initialize()
		{
			if (eventsInitialized)
			{
				return;
			}
			eventsInitialized = true;
			Width = width;
			Height = height;
			Rectangle r = new Rectangle();
			r.Width = Width;
			r.Height = captionHeight;
			EditorRectangle = r;
			textBrush = new SolidBrush(Color.White);
			captionBrush = new SolidBrush(Color.Blue);
			borderPen = new Pen(Color.Black);
			captionInactiveBrush = new SolidBrush(Color.DarkGray);
			borderInactivePen = new Pen(Color.LightGray);
			initEventHandlers();
			captionEditor.Visible = false;
			arrowSelected = false;
		}

		/// <summary>
		/// Creates correspond xml
		/// </summary>
		/// <param name="doc">document to create element</param>
		/// <returns>The created element</returns>
		public XmlElement CreateXml(XmlDocument doc)
		{
			XmlElement element = doc.CreateElement("ObjectLabel");
			XmlAttribute attrName = doc.CreateAttribute("Name");
			attrName.Value = ComponentName;
			element.Attributes.Append(attrName);
			if (Object is IXmlElementCreator)
			{
				IXmlElementCreator c = Object as IXmlElementCreator;
				XmlElement child = c.CreateXml(doc);
				element.AppendChild(child);
			}
			return element;
		}

		/// <summary>
		/// Event handlers initialisation
		/// </summary>
		protected void initEventHandlers()
		{
			initNamedEventHandlers();
			MouseDown += new MouseEventHandler(onMouseDownMoveEventHandler);
			MouseUp += new MouseEventHandler(onMouseUpMoveEventHandler);
			MouseLeave += new EventHandler(onLeaveEventHandler);
			MouseMove += new MouseEventHandler(onMouseMoveEventHandler) + 
				new MouseEventHandler(onMouseMoveArrow);
			Paint += new PaintEventHandler(onPaint);
		}

		/// <summary>
		/// The On Paint event handler
		/// </summary>
		/// <param name="sender">The sender</param>
		/// <param name="e">The event arguments</param>
		protected void onPaint(object sender, PaintEventArgs e)
		{
			Graphics g = e.Graphics;
			Brush brush = (theObject == null) ? captionInactiveBrush : captionBrush;
			g.FillRectangle(brush, EditorRectangle);
			Pen pen = (theObject == null) ? borderInactivePen : borderPen;
			g.DrawRectangle(pen, 0, 0, Width - 1, Height - 1);
			g.DrawString(ComponentName, font, textBrush, 5F, 5F);

		}
		
		/// <summary>
		/// The on mouse down event handler
		/// </summary>
		/// <param name="sender">The sender</param>
		/// <param name="e">The event arguments</param>
		protected void onMouseDownMoveEventHandler(object sender, MouseEventArgs e)
		{
			if ((Control.ModifierKeys & Keys.Shift) == Keys.Shift)
			{
				return;
			}
			if (e.Button == MouseButtons.Right)
			{
				return;
			}
			if (EditorRectangle.Contains(e.X, e.Y))
			{
				return;
			}
			PaletteButton active = Desktop.Tools.Active; 
			if (active != null)
			{
				if (active.IsArrow & !(active.Kind == -1))
				{
					try
					{
						ICategoryArrow arrow = Desktop.Tools.Factory.CreateArrow(active);
						arrow.Source = Object;
						Desktop.ActiveArrow = arrow;
						Desktop.ActiveObjectLabel = this;
						return;
					}
					catch (Exception ex)
					{
						Desktop.Tools.Factory.ShowError(ex);
						return;
					}
				}
			}
			isMoved = true;
			mouseX = e.X;
			mouseY = e.Y;
		}

		/// <summary>
		/// The on mouse up event handler
		/// </summary>
		/// <param name="sender">The sender</param>
		/// <param name="e">The event arguments</param>
		protected void onMouseUpMoveEventHandler(object sender, MouseEventArgs e)
		{
			isMoved = false;
			ICategoryArrow arrow = Desktop.ActiveArrow;
			if (e.Button == MouseButtons.Right)
			{
				return;
			}
			try
			{
				if (arrow == null)
				{
					return;
				}
				int x = Left + e.X;
				int y = Top + e.Y;
				for (int i = 0; i < Desktop.Controls.Count; i++)
				{
					if (!(Desktop.Controls[i] is ChildObjectLabel) & !(Desktop.Controls[i] is ObjectLabel))
					{
						continue;
					}
					Control c = Desktop.Controls[i];
					bool hor = x < c.Left | x > c.Left + c.Width;
					bool vert = y < c.Top | y > c.Top + c.Height;
					if (hor | vert)
					{
						continue;
					}
					IObjectLabel label = null;
					if (Desktop.Controls[i] is IObjectLabel)
					{
						label = Desktop.Controls[i] as IObjectLabel;
					}
					else
					{
						ChildObjectLabel child = Desktop.Controls[i] as ChildObjectLabel;
						label = child.label;
					}

					arrow.Target = label.Object;
					ArrowLabel lab = Desktop.Tools.Factory.CreateArrowLabel(Desktop.Tools.Active, arrow, this, label);
					lab.Arrow.Object = lab;
					Desktop.AddArrowLabel(lab);
					break;
				}
			}
			catch (Exception ex)
			{
				if (arrow != null)
				{
					if (arrow is IRemovableObject)
					{
						IRemovableObject rem = arrow as IRemovableObject;
						rem.RemoveObject();
					}
				}
				Desktop.Tools.Factory.ShowError(ex);
			}
			Desktop.ActiveArrow = null;
			Desktop.Redraw();
		}

		/// <summary>
		/// The on mouse move event handler
		/// Draws correspond arrows
		/// </summary>
		/// <param name="sender">The sender</param>
		/// <param name="e">The event arguments</param>
		protected void onMouseMoveArrow(object sender, MouseEventArgs e)
		{
			if (e.Button == MouseButtons.Right)
			{
				return;
			}
			if (Desktop.ActiveArrow == null)
			{
				return;
			}
			Desktop.DrawArrow(this, e);
		}

		/// <summary>
		/// The on mouse move event handler
		/// Moves itself
		/// </summary>
		/// <param name="sender">The sender</param>
		/// <param name="e">The event arguments</param>
		protected void onMouseMoveEventHandler(object sender, MouseEventArgs e)
		{
			if (!isMoved)
			{
				return;
			}
			Left += e.X - mouseX;
			Top += e.Y - mouseY;
			//Desktop.IsMoved = true;
			if (Selected)
			{
				foreach (Control control in Desktop.Controls)
				{
					if (!(control is ObjectLabel))
					{
						continue;
					}
					ObjectLabel comp = control as ObjectLabel;
					if (comp.Selected & comp != this)
					{
						comp.Left += e.X - mouseX;
						comp.Top += e.Y - mouseY;
					}
				}
			}
			Desktop.Redraw();
		}

        protected override void imagedPaintEventHandler(object sender, PaintEventArgs e)
        {
            Image im = button.ButtonImage;
            if (Object is IPropertiesEditor)
            {
                IPropertiesEditor pe = Object as IPropertiesEditor;
                object o = pe.Editor;
                if (o is Array)
                {
                    Array arr = o as Array;
                    if (arr.Length > 1)
                    {
                        object image = arr.GetValue(1);
                        if (image is Image)
                        {
                            im = image as Image;
                        }
                        if (image is Icon)
                        {
                            Icon ic = image as Icon;
                            im = ic.ToBitmap();
                        }
                    }
                }
            }
            Graphics g = e.Graphics;
            if (selected)
            {
                BackColor = Color.LightGray;
            }
            else
            {
                BackColor = Color.White;
            }
            g.DrawImage(im, imagePosition);

        }
		
		/// <summary>
		/// The on mouse leave event handler
		/// </summary>
		/// <param name="sender">The sender</param>
		/// <param name="e">The event arguments</param>
		protected void onLeaveEventHandler(object sender,	EventArgs e)
		{
			isMoved = false;
		}
	}

}
