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.Collections.Generic;
using System.Configuration.Assemblies;
using System.Threading;
using System.Xml.Serialization;
using System.Xml;

using CategoryTheory;
using MathGraph;

namespace DiagramUI
{
	/// <summary>
	/// The desktop of situation object
	/// </summary>
	public class PanelDesktop : Panel, IDesktop
	{

		/// <summary>
		/// Then "is not equal" string
		/// </summary>
		public static readonly string IsNotEqual = "is not equal";

		/// <summary>
		/// Transformer of objects
		/// </summary>
		public static readonly ObjectTransformer ObjectTransformer = 
			new ObjectTransformer(transformObjectLabel);

		/// <summary>
		/// Transformer of arrows
		/// </summary>
		public static readonly ObjectTransformer ArrowTransformer = 
			new ObjectTransformer(transformArrowLabel);

		/// <summary>
		/// The tools
		/// </summary>
		private ToolsDiagram tools;

		/// <summary>
		/// The buffer image
		/// </summary>
		private Image image;

		/// <summary>
		/// The clean brush
		/// </summary>
		private Brush brush;

		/// <summary>
		/// The collection of object pairs
		/// </summary>
		private ArrayList pairs = new ArrayList();

		/// <summary>
		/// The activated arrow
		/// </summary>
		private ICategoryArrow activeArrow;

		/// <summary>
		/// The activated object label
		/// </summary>
		private IObjectLabel activeObjectLabel;

		/// <summary>
		/// The pen to draw arrows
		/// </summary>
		private Pen linePen;

		/// <summary>
		/// The x coordinate of selection
		/// </summary>
		private int xSelect;

		/// <summary>
		/// The y coordinate of selection
		/// </summary>
		private int ySelect;

		/// <summary>
		/// The flag of selection performing
		/// </summary>
		private bool performsSelection;

		/// <summary>
		/// Constructor
		/// </summary>
		/// <param name="tools">The tools</param>
		public PanelDesktop(ToolsDiagram tools)
		{
			this.tools = tools;
			Paint += new PaintEventHandler(onPaint);
			brush = new SolidBrush(Color.White);
			MouseUp += new MouseEventHandler(onMouseUp);
			MouseMove += new MouseEventHandler(onMouseMoveArrow);
			MouseDown += new MouseEventHandler(onMouseDownSelect);
            InitializeComponent();
			linePen = new Pen(Color.Black);
			BackColor = Color.White;
		}

		#region IDesktop Members

		public ArrayList Components
		{
			get
			{
				ArrayList comp = new ArrayList();
				foreach (object o in Controls)
				{
					if (o is INamedComponent)
					{
						comp.Add(o);
					}
				}
				return comp;
			}
		}

		/// <summary>
		/// All components
		/// </summary>
		public ICollection AllComponents
		{
			get
			{
				return PureDesktopPeer.GetAllObjects(this);
			}
		}

		public ArrayList Objects
		{
			get
			{
				ArrayList comp = new ArrayList();
				foreach (object o in Controls)
				{
					if (o is IObjectLabel)
					{
						comp.Add(o);
					}
				}
				return comp;
			}
		}

		public ArrayList Arrows
		{
			get
			{
				ArrayList comp = new ArrayList();
				foreach (object o in Controls)
				{
					if (o is IArrowLabel)
					{
						comp.Add(o);
					}
				}
				return comp;
			}
		}

		public void Copy(ArrayList objects, ArrayList arrows, bool associated)
		{
            PureObjectLabel.Wrappers = true;
			ArrayList objs = new ArrayList();
			foreach (IObjectLabel l in objects)
			{
				ObjectLabel lab = tools.Factory.CreateObjectLabel(tools.FindButton(l));
				lab.X = l.X;
				lab.Y = l.Y;
				IObjectLabel la = lab;
				//la.Kind = l.Kind;
				//la.Type = l.Type;
				lab.ComponentName = l.Name;
				lab.Object = l.Object;
				lab.Initialize();
				AddNewObjectLabel(lab);
				objs.Add(lab);
				if (lab is ContainerObjectLabel)
				{
					ContainerObjectLabel cl = lab as ContainerObjectLabel;
					cl.Expand();
				}
				//tools.AddObjectNode(lab);
			}
			ArrayList arrs = new ArrayList();
            PureObjectLabel.Wrappers = false;
			foreach (IArrowLabel l in arrows)
			{
				//int s = objects.IndexOf(l.Source);
				//int t = objects.IndexOf(l.Target);
				IObjectLabel source = PureDesktopPeer.Find(objects, objs, l.Source, l.Desktop) as IObjectLabel;
				IObjectLabel target = PureDesktopPeer.Find(objects, objs, l.Target, l.Desktop) as IObjectLabel;
				ArrowLabel lab = tools.Factory.CreateArrowLabel(tools.FindButton(l), l.Arrow, source, target);
				lab.ComponentName = l.Name;
				lab.Arrow = l.Arrow;
				lab.Source = source;
				lab.Target = target;
				arrs.Add(lab);
				//AddArrowLabel(lab);
			}
			if (!associated)
			{
				return;
			}
			PureDesktopPeer.SetParents(this);
			ArrayList ob = Objects;
			//ArrayList ar = Arrows;
			PureObjectLabel.SetLabels(ob);
			PureArrowLabel.SetLabels(arrs);
			foreach (ObjectLabel l in ob)
			{
				tools.AddObjectNode(l);
			}
			foreach (ArrowLabel l in arrs)
			{
				AddArrowLabel(l);
			}
            PureObjectLabel.Wrappers = false;

		}

		public INamedComponent this[string name]
		{
			get
			{
				foreach (object o in Controls)
				{
					if (!(o is INamedComponent))
					{
						continue;
					}
					INamedComponent c = o as INamedComponent;
					if (name.Equals(c.Name))
					{
						return c;
					}
				}
				return null;
			}
		}


		public object GetObject(string name)
		{
			return PureDesktop.GetAssociatedObject(this, name);
		}



		#endregion

		
		/// <summary>
		/// Saves selected objects
		/// </summary>
		/// <param name="stream">The stream to save</param>
		public void SaveSelected(Stream stream)
		{
			ArrayList[] selected = Selected;
			Save(selected[0], selected[1], stream);
		}


		/// <summary>
		/// Sets components in frame
		/// </summary>
		public void SetInFrame()
		{
			foreach (Control c in Controls)
			{
				if (c.Left < 0)
				{
					c.Left = 0;
				}
				if (c.Top < 0)
				{
					c.Top = 0;
				}
				if (c.Left > Width - c.Width)
				{
					c.Left = Width - c.Width;
				}
				if (c.Top > Height - c.Height)
				{
					c.Top = Height - c.Height;
				}
			}
		}

		/// <summary>
		/// Deletes all comments
		/// </summary>
		public void DeleteComments()
		{
			ArrayList l = new ArrayList();
			foreach (Control c in Controls)
			{
				if (!(c is NamedComponent))
				{
					l.Add(c);
				}
			}
			foreach (Control c in l)
			{
				Controls.Remove(c);
			}
		}


		/// <summary>
		/// Sets selection sign
		/// </summary>
		/// <param name="selection">The sign</param>
		public void SetSelection(bool selection)
		{
			foreach (object o in Controls)
			{
				if (!(o is NamedComponent))
				{
					continue;
				}
				NamedComponent c = o as NamedComponent;
				c.Selected = selection;
			}
		}

		/// <summary>
		/// Saves all items to stream
		/// </summary>
		/// <param name="stream">The stream to save</param>
		public void SaveAll(Stream stream)
		{
			ArrayList objects = new ArrayList();
			ArrayList arrows = new ArrayList();
			foreach (Control c in Controls)
			{
				if (c is ObjectLabel)
				{
					objects.Add(c);
				}
				if (c is ArrowLabel)
				{
					arrows.Add(c);
				}
			}
			Save(objects, arrows, stream);
		}

		/// <summary>
		/// All objects on desktop
		/// </summary>
		public ArrayList[] AllObjects
		{
			get
			{
				ArrayList objects = new ArrayList();
				ArrayList arrows = new ArrayList();
				foreach (Control c in Controls)
				{
					if (c is ObjectLabel)
					{
						objects.Add(c);
					}
					if (c is ArrowLabel)
					{
						arrows.Add(c);
					}
				}
				return new ArrayList[] {objects, arrows};
			}
		}

		
		/// <summary>
		/// Names of aliases
		/// </summary>
		public ArrayList AliasNames
		{
			get
			{
				ArrayList l = new ArrayList();
				foreach (Control c in Controls)
				{
					if (c is ObjectLabel)
					{
						ObjectLabel ol = c as ObjectLabel;
						ICategoryObject ob = ol.Object;
						if (!(ob is IAlias))
						{
							continue;
						}
						IAlias a = ob as IAlias;
						List<string> an = a.AliasNames;
						string name = ol.ComponentName;
						foreach (string s in an)
						{
							l.Add(name + "." + s);
						}
					}
					if (c is ArrowLabel)
					{
						ArrowLabel al = c as ArrowLabel;
						ICategoryArrow ar = al.Arrow;
						if (!(ar is IAlias))
						{
							continue;
						}
						IAlias a = ar as IAlias;
						List<string> an = a.AliasNames;
						string name = al.ComponentName;
						foreach (string s in an)
						{
							l.Add(name + "." + s);
						}
					}
				}
				return l;
			}
		}

		/// <summary>
		/// Transforms digraph
		/// </summary>
		/// <param name="graph">Digraph to transform</param>
		public void TransformDigraph(Digraph graph)
		{
			graph.Transform(ObjectTransformer, ArrowTransformer);
		}

		/// <summary>
		/// Gets diagram of definite types
		/// </summary>
		/// <param name="objectTypes">List of object types</param>
		/// <param name="arrowTypes">List of arrow types</param>
		/// <returns>Array of arrays of objects and arrows [0] - objects, [1] - arrows</returns>
		public ArrayList[] GetTypedDiagram(ArrayList objectTypes, ArrayList arrowTypes)
		{
			ArrayList[] list = new ArrayList[]{new ArrayList(), new ArrayList()};
			foreach (object o in Controls)
			{
				if (o is ObjectLabel)
				{
					ObjectLabel l = o as ObjectLabel;
					if (objectTypes.Contains(l.Kind))
					{
						list[0].Add(l);
					}
					continue;
				}
				if (o is ArrowLabel)
				{
					ArrowLabel l = o as ArrowLabel;
					if (arrowTypes.Contains(l.Kind))
					{
						list[1].Add(l);
					}
				}
			}
			return list;
		}



		/// <summary>
		/// Creates XML Element from selected objects
		/// </summary>
		/// <param name="doc">The document</param>
		/// <returns></returns>
		public XmlElement CreateSelectedXml(XmlDocument doc)
		{
			ArrayList[] selected = Selected;
			return CreateXml(selected[0], selected[1], doc);
		}

		/// <summary>
		/// Creates XML Element from all objects
		/// </summary>
		/// <param name="doc">The document</param>
		/// <returns></returns>
		public XmlElement CreateXml(XmlDocument doc)
		{
			ArrayList ob = new ArrayList();
			ArrayList ar = new ArrayList();
			foreach (Control c in Controls)
			{
				if (c is ObjectLabel)
				{
					ob.Add(c);
					continue;
				}
				if (c is ArrowLabel)
				{
					ar.Add(c);
				}
			}
			return CreateXml(ob, ar, doc);
		}


		/// <summary>
		/// Removes selected objects
		/// </summary>
		public void RemoveSelected()
		{
			ArrayList list = new ArrayList();
			foreach (Control c in Controls)
			{
				if (!(c is ObjectLabel))
				{
					continue;
				}
				ObjectLabel label = c as ObjectLabel;
				if (label.Selected)
				{
					list.Add(c);
				}
			}
			foreach (ObjectLabel label in list)
			{
				label.Remove(true);
			}
			list.Clear();
			foreach (Control c in Controls)
			{
				if (!(c is ArrowLabel))
				{
					continue;
				}
				ArrowLabel label = c as ArrowLabel;
				if (label.Selected)
				{
					list.Add(c);
				}
			}
			foreach (ArrowLabel label in list)
			{
				label.Remove(true);
			}
			list = null;
			GC.Collect();
		}

		/// <summary>
		/// Removes all objects
		/// </summary>
		public void RemoveAll()
		{
			ArrayList arrs = new ArrayList();
			for (int i = 0; i < Controls.Count; i++)
			{
				if (Controls[i] is ArrowLabel)
				{
					arrs.Add(Controls[i]);
				}
			}
			object[] ob = arrs.ToArray();
			for (int i = 0; i < ob.Length; i++)
			{
				ArrowLabel l = ob[i] as ArrowLabel;
				l.Remove(true);
			}
			ArrayList objs = new ArrayList();
			for (int i = 0; i < Controls.Count; i++)
			{
				if (Controls[i] is ObjectLabel)
				{
					objs.Add(Controls[i]);
				}
			}
			object[] o = objs.ToArray();
			for (int i = 0; i < o.Length; i++)
			{
				ObjectLabel l = o[i] as ObjectLabel;
				l.Remove(true);
			}
            arrs = null;
            ob = null;
            objs = null;
            o = null;
            GC.Collect();
		}

		/// <summary>
		/// Creates image of arrow
		/// </summary>
		/// <param name="label">Label of arrow</param>
		/// <param name="homType">Type of arrow</param>
		/// <param name="objType">Type of object</param>
		/// <param name="x">X shift of image label</param>
		/// <param name="y">Y shift of image label</param>
		public void CreateImage(ArrowLabel label, int x, int y)
		{
			IImageArrow hom = label.Arrow as IImageArrow;
			ICategoryArrow to = hom.ToImage;
			ICategoryArrow from = hom.FromImage;
			ICategoryObject im = to.Target;
			ObjectLabel labImObj = tools.Factory.CreateObjectLabel(Tools.Factory.GetObjectButton(im));
			ObjectLabel labSource = label.Source as ObjectLabel;
			ObjectLabel labTarget = label.Target as ObjectLabel;
			labImObj.Object = im;
			im.Object = labImObj;
			labImObj.Top = labTarget.Top + y;
			labImObj.Left = labTarget.Left + x;
			labImObj.ComponentName = "Im (" + label.ComponentName + ")";
			AddNewObjectLabel(labImObj);
			tools.AddObjectNode(labImObj);
			ArrowLabel arrowSo = tools.Factory.CreateArrowLabel(Tools.Factory.GetArrowButton(to), to, labSource, labImObj);
			to.Object = arrowSo;
			arrowSo.ComponentName = "im (" + label.ComponentName + ")";
			AddNewArrowLabel(arrowSo);
			ArrowLabel arrowTa = tools.Factory.CreateArrowLabel(Tools.Factory.GetArrowButton(from), from, labImObj, labTarget);
			from.Object = arrowTa;
			arrowTa.ComponentName = "Im (" + label.ComponentName + ")";
			AddNewArrowLabel(arrowTa);
			Redraw();
		}



		/// <summary>
		/// Gets selected objects and arrows
		/// </summary>
		public ArrayList[] Selected
		{
			get
			{
				ArrayList objects = new ArrayList();
				ArrayList arrows = new ArrayList();
				foreach (Control control in Controls)
				{
					if (!(control is ArrowLabel))
					{
						continue;
					}
					ArrowLabel label = control as ArrowLabel;
					if (!label.Selected)
					{
						continue;
					}
					arrows.Add(label);
				}
				foreach (Control control in Controls)
				{
					if (!(control is ObjectLabel))
					{
						continue;
					}
					ObjectLabel label = control as ObjectLabel;
					if (label.Selected & !(label.ArrowSelected))
					{
						objects.Add(label);
					}
				}
				foreach (ObjectLabel label in objects)
				{
					label.ArrowSelected = false;
				}
				return new ArrayList[]{objects, arrows};
			}
		}

        /// <summary>
        /// All object labels
        /// </summary>
		public ArrayList[] AllLabels
		{
			get
			{
				ArrayList[] l = new ArrayList[]{new ArrayList(), new ArrayList()};
				foreach (object o in Controls)
				{
					if (o is ObjectLabel)
					{
						l[0].Add(o);
						continue;
					}
					if (o is ArrowLabel)
					{
						l[1].Add(o);
					}
				}
				return l;
			}
		}

        /// <summary>
        /// Adds arrow label with existing source and target object labels
        /// </summary>
        /// <param name="arrow">Associated arrow</param>
        /// <returns>The arrow label</returns>
        public ArrowLabel AddArrowWithExistingObjects(ICategoryArrow arrow)
        {
            ICategoryObject s = arrow.Source;
            ICategoryObject t = arrow.Target;
            IObjectLabel sl = s.Object as IObjectLabel;
            IObjectLabel tl = t.Object as IObjectLabel;
            ArrowLabel arrowLabel = tools.Factory.CreateArrowLabel(tools.Factory.GetArrowButton(arrow), arrow, sl, tl);
            arrowLabel.Source = sl;
            arrowLabel.Target = tl;
            arrowLabel.Arrow = arrow;
            arrow.Object = arrowLabel;
            AddNewArrowLabel(arrowLabel);
            return arrowLabel;

        }
		

		/// <summary>
		/// Saves objects and arrows to stream
		/// </summary>
		/// <param name="objects">List of objects</param>
		/// <param name="arrows">List of arrows</param>
		/// <param name="stream">The stream</param>
		static public void Save(ArrayList objects, ArrayList arrows, Stream stream)
		{
			PureDesktopPeer desktop = new PureDesktopPeer();
			desktop.Copy(objects, arrows, false);
			desktop.Save(stream);
			if (objects.Count == 0)
			{
				return;
			}
			IObjectLabel l = objects[0] as IObjectLabel;
			PureDesktopPeer.SetParents(l.Desktop);
		}




		/// <summary>
		/// Creates functor image from objects and arrows
		/// </summary>
		/// <param name="functor">The functor</param>
		/// <param name="preffix">Preffix</param>
		/// <param name="suffix">Suffix</param>
		/// <param name="objects">Objects</param>
		/// <param name="arrows">Arrows</param>
		/// <param name="x">x - shift</param>
		/// <param name="y">x - shift</param>
		/// <param name="covariant"></param>
		public void CreateFunctor(IFunctor functor, string preffix, string suffix,
			ArrayList objects, ArrayList arrows, int x, int y, bool covariant)
		{
			foreach (ArrowLabel label in arrows)
			{
				label.SourceNumber = objects.IndexOf(label.Source);
				label.TargetNumber = objects.IndexOf(label.Target);
			}
			ArrayList imObjectLabels = new ArrayList();
			ICategoryObject[] imObjects = new ICategoryObject[objects.Count];
			int i = 0;
			foreach (ObjectLabel label in objects)
			{
				ICategoryObject ob = label.Object;
				ICategoryObject im = functor.CalculateObject(ob);
				ObjectLabel lab = tools.Factory.CreateObjectLabel(tools.Factory.GetObjectButton(im));
				lab.Left = label.Left + x;
				lab.Top = label.Top + y;
				lab.ComponentName = preffix + label.ComponentName + suffix;
				lab.Object = im;
				imObjectLabels.Add(lab);
				AddNewObjectLabel(lab);
				tools.AddObjectNode(lab);
				imObjects[i] = im;
				++i;
			}
			foreach (ArrowLabel arr in arrows)
			{
				ObjectLabel source = imObjectLabels[(int) arr.SourceNumber] as ObjectLabel;
				ObjectLabel target = imObjectLabels[(int) arr.TargetNumber] as ObjectLabel;
				ICategoryObject imSource = imObjects[(int) arr.SourceNumber] as ICategoryObject;
				ICategoryObject imTarget = imObjects[(int) arr.TargetNumber] as ICategoryObject;
				ICategoryArrow imArrow = functor.CalculateArrow(imSource, imTarget, arr.Arrow);
				ArrowLabel arrow = null;
				ObjectLabel so = null;
				ObjectLabel ta = null;
				if (covariant)
				{
					so = source;
					ta = target;
				}
				else
				{
					so = target;
					ta = source;
				}
				arrow = tools.Factory.CreateArrowLabel(tools.Factory.GetArrowButton(imArrow), imArrow, so, ta);
				arrow.ComponentName = preffix + arr.ComponentName + suffix;
				arrow.Source = so;
				arrow.Target = ta;
				AddNewArrowLabel(arrow);
			}
			foreach (ObjectLabel lab in objects)
			{
				object obj = lab.Object;
				if (obj is IPostSetArrow)
				{
					IPostSetArrow post = obj as IPostSetArrow;
					post.PostSetArrow();
				}
			}
		}


        public static void CreateNaturalTransformations(INaturalTransformation[] transformations,
            string preffix, string suffix,
            List<ObjectLabel> objects, List<ArrowLabel> arrows, int x, int y)
        {
        }


		/// <summary>
		/// Checks whether loop is commutative
		/// </summary>
		/// <param name="category">Category</param>
		/// <param name="loop">The loop to check</param>
		/// <returns>Non - commutative paths</returns>
		static public string NonCommutativeLoop(ICategory category, DigraphLoop loop)
		{
			ICategoryArrow[] arrows = new ICategoryArrow[2];
			for (int i = 0; i < 2; i++)
			{
				arrows[i] = PureDesktop.Composition(category, loop[i]);
			}
			if (arrows[0].Equals(arrows[1]))
			{
				return null;
			}
			return ToString(loop[0]) + " " + ResourceService.Resources.GetResourceString(IsNotEqual) + " " + ToString(loop[1]);
		}

		/// <summary>
		/// Gets desktop object label
		/// </summary>
		/// <param name="name">Object name</param>
		/// <returns>The label</returns>
		public ObjectLabel GetObjectLabel(string name)
		{
			foreach (Control c in Controls)
			{
				if (!(c is ObjectLabel))
				{
					continue;
				}
				ObjectLabel l = c as ObjectLabel;
				if (name.Equals(l.ComponentName))
				{
					return l;
				}
			}
			return null;
		}

		/// <summary>
		/// Root desktop
		/// </summary>
		public IDesktop Root
		{
			get
			{
				return this;
			}
		}

	
		/// <summary>
		/// Names of selected components
		/// </summary>
		public ArrayList[] SelectedNames
		{
			get
			{
				ArrayList[] s = Selected;
				ArrayList[] n = new ArrayList[]{new ArrayList(), new ArrayList()};
				foreach (ObjectLabel l in s[0])
				{
					n[0].Add(l.ComponentName);
				}
				foreach (ArrowLabel l in s[1])
				{
					n[1].Add(l.ComponentName);
				}
				return n;
			}
		}

		/// <summary>
		/// String representation of path
		/// </summary>
		/// <param name="path">The path</param>
		/// <returns> String representation of path</returns>
		static public string ToString(DigraphPath path)
		{
			ArrowLabel l = path[path.Count - 1].Object as ArrowLabel;
			string s = l.ComponentName;
			for (int i = path.Count - 2; i >= 0; i--)
			{
				l = path[i].Object as ArrowLabel;
				s += " * " + l.ComponentName;
			}
			return s;
		}


		/// <summary>
		/// String representation of non-commutative loop
		/// </summary>
		/// <param name="category">Category</param>
		/// <param name="objects">Objects</param>
		/// <param name="arrows">Arrows</param>
		/// <returns>String representation of non-commutative loop</returns>
		static public string NonCommutativeLoop(ICategory category, ArrayList objects, ArrayList arrows)
		{
			Digraph graph = PureDesktop.CreateDigraph(objects, arrows);
			ArrayList loops = graph.Loops;
			foreach (DigraphLoop loop in loops)
			{
				string s = NonCommutativeLoop(category, loop);
				if (s != null)
				{
					return s;
				}
			}
			return null;
		}

		/// <summary>
		/// Restores arrow
		/// </summary>
		/// <param name="category">Category</param>
		/// <param name="label">Label of arrow to restore</param>
		/// <param name="objects">Objects of diagram</param>
		/// <param name="arrows">Arrows of diagram</param>
		static public void RestoreArrow(ICategory category, ArrowLabel label, ArrayList objects, ArrayList arrows,
			out FindResults res)
		{
			object o = label.Source.Object;
			if (!(o is IDiagramRestoredObject))
			{
				throw new Exception("Arrow cannot be restored");
			}
			IDiagramRestoredObject source = o as IDiagramRestoredObject;
			ICategoryObject sourceObject = source as ICategoryObject;
			ICategoryObject target = label.Target.Object as ICategoryObject;
			Digraph graph = PureDesktop.CreateDigraph(objects, arrows);
			ArrayList tempLoops = graph.Loops;
			ArrayList restLoops = new ArrayList();
			ArrayList commLoops = new ArrayList();
			foreach (DigraphLoop loop in tempLoops)
			{
				if (loop.ContainsObject(label))
				{
					restLoops.Add(loop);
					continue;
				}
				commLoops.Add(loop);
			}
			foreach (DigraphLoop loop in commLoops)
			{
				string s = NonCommutativeLoop(category, loop);
				if (s != null)
				{
					throw new Exception(s);
				}
			}
			ICategoryArrow[,] arr = new ICategoryArrow[restLoops.Count, 3];
			for (int i = 0; i < arr.GetLength(0); i++)
			{
				DigraphLoop loop = restLoops[i] as DigraphLoop;
				int n = (loop[0].ContainsObject(label)) ? 0 : 1;
				DigraphPath arrowPath = loop[n];
				DigraphPath roundPath = loop[1 - n];
				ICategoryArrow round = PureDesktop.Composition(category, roundPath);
				arr[i, 0] = round;
				int j = 0;
				ICategoryArrow to = null;
				ObjectLabel lo = arrowPath.Source.Object as ObjectLabel;
				if (lo.Object == sourceObject)
				{
					to = sourceObject.Id;
					j++;
				}
				else
				{
					for (j = 0; j < arrowPath.Count; j++)
					{
						lo = arrowPath[j].Target.Object as ObjectLabel;
						if (lo.Object == source)
						{
							break;
						}
					}
					++j;
					ArrowLabel la = arrowPath[0].Object as ArrowLabel;
					to = la.Arrow;
					for (int k = 1; k < j; k++)
					{
						la = arrowPath[k].Object as ArrowLabel;
						ICategoryArrow ar = la.Arrow;
						to = ar.Compose(category, to);
					}
					++j;
				}
				arr[i, 1] = to;
				ICategoryArrow from = null;
				if (j == arrowPath.Count)
				{
					from = target.Id;
				}
				else
				{
					ArrowLabel la = arrowPath[j].Object as ArrowLabel;
					from = la.Arrow;
					++j;
					for (; j < arrowPath.Count; j++)
					{
						la = arrowPath[j].Object as ArrowLabel;
						from = la.Arrow.Compose(category, from);
					}
				}
				arr[i, 2] = from;
			}
			ICategoryArrow result = source.RestoreArrow(target, arr, out res);
			if (result != null)
			{
				label.Arrow = result;
			}
		}


		/// <summary>
		/// Performs all operations for adding object label
		/// </summary>
		/// <param name="lab">Object label to add</param>
		public void AddNewObjectLabel(ObjectLabel lab)
		{
			Controls.Add(lab);
			object obj = lab.Object;
			lab.Object.Object = lab;
			if (lab.Object is IFactoryObject)
			{
				IFactoryObject f = lab.Object as IFactoryObject;
				f.Facrory = Tools.Factory;
			}
			
			if (lab.Object is IPostSerialize)
			{
				IPostSerialize p = lab.Object as IPostSerialize;
				p.PostSerialize();
			}
			
		}


		/// <summary>
		/// Performs all operations for adding arrow label
		/// </summary>
		/// <param name="arrow">Arrow label to add</param>
		public void AddNewArrowLabel(ArrowLabel arrow)
		{
			AddArrowLabel(arrow);
			arrow.Arrow.Object = arrow;
			if (true)
			{
				IAssociatedObject ass = arrow.Arrow as IAssociatedObject;
				ass.Object = arrow;
			}
			if (arrow.Arrow is IFactoryObject)
			{
				IFactoryObject f = arrow.Arrow as IFactoryObject;
				f.Facrory = Tools.Factory;
			}
			if (arrow.Arrow is IPostSetArrow)
			{
				IPostSetArrow post = arrow.Arrow as IPostSetArrow;
				post.PostSetArrow();
			}
		}

		/// <summary>
		/// Gets path from arrows
		/// </summary>
		/// <param name="arrows">Arrows</param>
		/// <returns>The path</returns>
		public static ArrayList GetPath(ArrayList arrows)
		{
			Hashtable hash = new Hashtable();
			foreach (ArrowLabel l in arrows)
			{
				IObjectLabel s = l.Source;
				object[] os = null;
				if (hash.Contains(s))
				{
					os = hash[s] as object[];
					if (os[1] != null)
					{
						throw new Exception("Two sources");
					}
				}
				else
				{
					os = new object[3];
					os[0] = s;
					hash[s] = os;
				}
				os[1] = l;
				IObjectLabel t = l.Target;
				object[] ot = null;
				if (hash.Contains(t))
				{
					ot = hash[t] as object[];
					if (ot[2] != null)
					{
						throw new Exception("Two targets");
					}
				}
				else
				{
					ot = new object[3];
					ot[0] = t;
					hash[t] = ot;
				}
				ot[2] = l;
			}
			object[] b = null;
			foreach (object[] o in hash.Values)
			{
				if ((o[1] != null) & (o[2] == null))
				{
					b = o;
					break;
				}
			}
			if (b == null)
			{
				throw new Exception("Source is not defined");
			}
			ArrayList path = new ArrayList();
			while (true)
			{
				if (b[1] == null)
				{
					break;
				}
				ArrowLabel a = b[1] as ArrowLabel;
				path.Add(a);
				b = hash[a.Target] as object[];
			}
			if (path.Count != arrows.Count)
			{
				throw new Exception("Two paths");
			}
			return path;
		}


		/// <summary>
		/// Gets associated with edge arrow
		/// </summary>
		/// <param name="edge">The edge</param>
		/// <returns>Associated arrow</returns>
		public static ICategoryArrow GetEdgeArrow(DigraphEdge edge)
		{
			ArrowLabel l = edge.Object as ArrowLabel;
			return l.Arrow;
		}

		/// <summary>
		/// Creates composition from selection
		/// </summary>
		/// <param name="token"></param>
		public void CreateCompositionFromSelection(ICategory cat, string token)
		{
			ArrayList path = GetPath(Selected[1]);
			ICategoryArrow f = null;
			IObjectLabel source = null;
			IObjectLabel target = null;
			string name = "";
			foreach (ArrowLabel label in path)
			{
				ICategoryArrow g = label.Arrow;
				if (f == null)
				{
					f = g;
					name = label.ComponentName;
					source = label.Source;
				}
				else
				{
					f = g.Compose(cat, f);
					name = label.ComponentName + token + name; 
				}
				target = label.Target;
			}
			ArrowLabel arrow = tools.Factory.CreateArrowLabel(tools.Factory.GetArrowButton(f), f, source as ObjectLabel, target as ObjectLabel);
			arrow.ComponentName = name;
			AddArrowLabel(arrow);
			Redraw();
		}


		/// <summary>
		/// Creates xml from objects and arriws
		/// </summary>
		/// <param name="objects">List of objects</param>
		/// <param name="arrows">List of arrows</param>
		/// <param name="doc">XML Document</param>
		/// <returns></returns>
		static public XmlElement CreateXml(ArrayList objects, ArrayList arrows, XmlDocument doc)
		{
			foreach (ArrowLabel label in arrows)
			{
				label.SourceNumber = objects.IndexOf(label.Source);
				label.TargetNumber = objects.IndexOf(label.Target);
			}
			XmlElement root = doc.CreateElement("Situation");
			XmlAttribute now = doc.CreateAttribute("DateTime");
			now.Value = DateTime.Now.ToString();
			root.Attributes.Append(now);
			XmlElement objectRoot = doc.CreateElement("Objects");
			foreach (ObjectLabel label in objects)
			{
				XmlElement element = label.CreateXml(doc);
				int id = objects.IndexOf(label);
				XmlAttribute attrId = doc.CreateAttribute("ObjectId");
				attrId.Value = id + "";
				element.Attributes.Append(attrId);
				objectRoot.AppendChild(element);
			}

			root.AppendChild(objectRoot);
			XmlElement arrowRoot = doc.CreateElement("Arrows");
			foreach (ArrowLabel label in arrows)
			{
				XmlElement element = label.CreateXml(doc);
				int id = arrows.IndexOf(label);
				XmlAttribute attrId = doc.CreateAttribute("ArrowId");
				attrId.Value = id + "";
				element.Attributes.Append(attrId);
				XmlAttribute sourceId = doc.CreateAttribute("SourceId");
				sourceId.Value = label.SourceNumber + "";
				element.Attributes.Append(sourceId);
				XmlAttribute sourceName = doc.CreateAttribute("SourceName");
				sourceName.Value = label.Source.Name;
				element.Attributes.Append(sourceName);
				XmlAttribute targetId = doc.CreateAttribute("TargetId");
				targetId.Value = label.TargetNumber + "";
				element.Attributes.Append(targetId);
				XmlAttribute targetName = doc.CreateAttribute("TargetName");
				targetName.Value = label.Target.Name;
				element.Attributes.Append(targetName);
				arrowRoot.AppendChild(element);
			}
			root.AppendChild(arrowRoot);
			return root;
		}


		/// <summary>
		/// Loads arrows and objects from stream
		/// </summary>
		/// <param name="stream">The stream</param>
		/// <param name="x">The x shift to set controls</param>
		/// <param name="y">The y shift to set controls</param>
		public bool Load(Stream stream, int x, int y, SerializationBinder[] binders)
		{
            try
            {
                BinaryFormatter b = new BinaryFormatter();
                load(stream, b);
                return true;

            }
            catch (Exception ex)
            {
                ex = ex;
            }
            BinaryFormatter bformatter = new BinaryFormatter();
			if (binders != null)
			{
				foreach (SerializationBinder binder in binders)
				{
					bformatter.Binder = binder;
					try
					{
						load(stream, bformatter);
						return true;
					}
					catch (Exception)
					{
					}
				}
			}
			return false;
		}

		private void load(Stream stream, BinaryFormatter bformatter)
		{
			stream.Position = 0;
			object[] objs = (object[])bformatter.Deserialize(stream);
			object[] arrs = (object[])bformatter.Deserialize(stream);
			ObjectLabel[] objects = new ObjectLabel[objs.Length];
			ArrowLabel[] arrows = new ArrowLabel[arrs.Length];
			int i = 0;
			foreach (ObjectLabel obj in objs)
			{
				obj.ComponentButton = tools.FindButton(obj);
				obj.Left += 0;
				obj.Top += 0;
				objects[i] = obj;
				obj.Initialize();
				AddNewObjectLabel(obj);
				tools.AddObjectNode(obj);
				++i;
			}
			foreach (ArrowLabel arrow in arrs)
			{
				arrow.ComponentButton = tools.FindButton(arrow);
				arrow.Initialize();
				ObjectLabel source = objects[(int) arrow.SourceNumber];
				ObjectLabel target = objects[(int) arrow.TargetNumber];
				arrow.Arrow.Source = source.Object;
				arrow.Arrow.Target = target.Object;
				arrow.Source = source;
				arrow.Target = target;
				AddNewArrowLabel(arrow);
			}
			foreach (ObjectLabel lab in objects)
			{
				object obj = lab.Object;
				if (obj is IPostSetArrow)
				{
					IPostSetArrow post = obj as IPostSetArrow;
					post.PostSetArrow();
				}
			}
			foreach (ArrowLabel lab in arrs)
			{
				object obj = lab.Arrow;
				if (obj is IPostSerialize)
				{
					IPostSerialize post = obj as IPostSerialize;
					post.PostSerialize();
				}
			}

		}

		/// <summary>
		/// The tools of this diagram
		/// </summary>
		public ToolsDiagram Tools
		{
			get
			{
				return tools;
			}
		}

		/// <summary>
		/// Active arrow
		/// </summary>
		public ICategoryArrow ActiveArrow
		{
			get
			{
				return activeArrow;
			}
			set
			{
				activeArrow = value;
			}
		}

		/// <summary>
		/// Pairs of objects
		/// </summary>
		public ArrayList Pairs
		{
			get
			{
				return pairs;
			}
		}
		
		/// <summary>
		/// Active label of object
		/// </summary>
		public IObjectLabel ActiveObjectLabel
		{
			get
			{
				return activeObjectLabel;
			}
			set
			{
				activeObjectLabel= value;
			}
		}

		/// <summary>
		/// Resizes background image
		/// </summary>
		public void ResizeImage()
		{
			image = new Bitmap(Width, Height);
			clearImage();
		}
		
		/// <summary>
		/// Adds object label
		/// </summary>
		/// <param name="x">x - coordinate to set label</param>
		/// <param name="y">y - coordinate to set label</param>
		public void AddObjectLabel(int x, int y)
		{
			ObjectLabel label = tools.Factory.CreateObjectLabel(tools.Active);
			label.Left = x;
			label.Top = y;
            
			ICategoryObject ob = tools.Factory.CreateObject(tools.Active);

			if (ob != null)
			{
                label.Object = ob;
                ob.Object = label;
				label.Object.Object = label;
			}
            if (label is ContainerObjectLabel & !(ob is ObjectContainer))
            {
                label = tools.Factory.CreateLabel(ob);
                ob.Object = label;
                label.Object = ob;
                label.Object.Object = label;
                label.Left = x;
                label.Top = y;
            }
            Controls.Add(label);
			if (label is ContainerObjectLabel)
			{
				ContainerObjectLabel cont = label as ContainerObjectLabel;
				ObjectContainer con = cont.Object as ObjectContainer;
				con.PostLoad();
				con.SetParents(this);
				con.PostSetArrow();
				cont.Expand();
			}
			tools.AddObjectNode(label);
		}
		

		/// <summary>
		/// Adds arrow label to this component
		/// </summary>
		/// <param name="label">The label to add</param>
		public void AddArrowLabel(ArrowLabel label)
		{
			ObjectsPair pair = null;
			object[] ps = pairs.ToArray();
			for (int i = 0; i < ps.Length; i++)
			{
				ObjectsPair p = (ObjectsPair)ps[i];
				if (p.Belongs(label))
				{
					pair = p;
					break;
				}
			}
			if (pair == null)
			{
				pair = new ObjectsPair(label.Source, label.Target);
				pair.Desktop = this;
				pairs.Add(pair);
			}
			pair.Add(label);
			Controls.Add(label);
			tools.AddArrowNode(label);
		}

		/// <summary>
		/// Removes object label
		/// </summary>
		/// <param name="label">The label to remove</param>
		public void Remove(ObjectLabel label)
		{
			label.RemoveFromComponent();
			for (int i = pairs.Count - 1; i >= 0; i--)
			{
				ObjectsPair p = pairs[i] as ObjectsPair;
				if (p.Belongs(label))
				{
					p.Remove();
				}
			}
			tools.RemoveObjectNode(label);
            GC.Collect();
		}

		/// <summary>
		/// Checks all objects
		/// </summary>
		public void CheckCorrectness()
		{
			foreach (Control c in Controls)
			{
				object o = null;
				if (c is ObjectLabel)
				{
					ObjectLabel l = c as ObjectLabel;
					o = l.Object;
				}
				else if (c is ArrowLabel)
				{
					ArrowLabel l = c as ArrowLabel;
					o = l.Arrow;
				}
				else
				{
					continue;
				}
				if (!(o is ICheckCorrectness))
				{
					continue;
				}
				ICheckCorrectness check = o as ICheckCorrectness;
				check.CheckCorrectness();
			}
		}

		/// <summary>
		/// Removes the pair of objects
		/// </summary>
		/// <param name="p">The pair to remove</param>
		public void Remove(ObjectsPair p)
		{
			pairs.Remove(p);
		}

		/// <summary>
		/// Gets resources string
		/// </summary>
		/// <param name="s">Source string</param>
		/// <param name="resources">Resources</param>
		/// <returns>Resources string</returns>
		public static string GetResourceString(String s, Dictionary<string, string> resources)
		{
			if (resources != null)
			{
				string rs = resources[s];
				if (rs != null)
				{
					return rs;
				}
			}
			return s;
		}


		/// <summary>
		/// Checks order
		/// </summary>
		public void CheckOrder()
		{
			tools.Factory.CheckOrder(this);
		}

		/// <summary>
		/// Removes object label
		/// </summary>
		/// <param name="label">The label to remove</param>
		internal void remove(ChildObjectLabel label)
		{
			Controls.Remove(label);
			for (int i = pairs.Count - 1; i >= 0; i--)
			{
				ObjectsPair p = pairs[i] as ObjectsPair;
				if (p.belongs(label))
				{
					p.Remove();
				}
			}
            GC.Collect();
		}



		
		/// <summary>
		/// Selects objects by mouse
		/// </summary>
		/// <param name="x">x- coordinate of mouse</param>
		/// <param name="y">y- coordinate of mouse</param>
		private void selectMouse(int x, int y)
		{
			int[,] corners = new int[4, 2];
			performsSelection = false;
			foreach (Control control in Controls)
			{
				if (!(control is NamedComponent))
				{
					continue;
				}
				NamedComponent comp = control as NamedComponent;
				corners[0, 0] = comp.Left;
				corners[0, 1] = comp.Top;
				corners[1, 0] = comp.Left + comp.Width;
				corners[1, 1] = comp.Top;
				corners[2, 0] = comp.Left + comp.Width;
				corners[2, 1] = comp.Top + comp.Height;
				corners[3, 0] = comp.Left;
				corners[3, 1] = comp.Top + comp.Height;
				bool sel = false;
				for (int i = 0; i < 4; i++)
				{
					double xc = corners[i, 0];
					double yc = corners[i, 1];
					if (xSelect < xc & x > xc & ySelect < yc & y > yc)
					{
						sel = true;
						break;
					}
				}
				if (sel)
				{
					comp.Selected = true;
				}
			}
		}

		/// <summary>
		/// The "on mouse up" event handler
		/// </summary>
		/// <param name="sender">The sender</param>
		/// <param name="e">The event arguments</param>
		private void onMouseUp(object sender, MouseEventArgs e)
		{
			if (performsSelection)
			{
				selectMouse(e.X, e.Y);
				return;
			}
			if (tools.Active == null)
			{
				return;
			}
			if (tools.Active.IsArrow)
			{
				if (activeArrow != null)
				{
					activeArrow = null;
				}
				return;
			}
			AddObjectLabel(e.X, e.Y);
		}
		
		/// <summary>
		/// The "on mouse down" event handler
		/// </summary>
		/// <param name="sender">The sender</param>
		/// <param name="e">The event arguments</param>
		private void onMouseDownSelect(object sender, MouseEventArgs e)
		{
			if (Tools.Active == null)
			{
				return;
			}
			if (!Tools.Active.IsArrow | Tools.Active.Kind != -1)
			{
				return;
			}
			performsSelection = true;
			xSelect = e.X;
			ySelect = e.Y;
		}

		/// <summary>
		/// The "on mouse move" event handler
		/// </summary>
		/// <param name="sender">The sender</param>
		/// <param name="e">The event arguments</param>
		private void onMouseMoveArrow(object sender, MouseEventArgs e)
		{
			if (!performsSelection)
			{
				return;
			}
			Graphics g = redrawImage();
			g.DrawRectangle(linePen, xSelect, ySelect, e.X - xSelect, e.Y - ySelect);
			Refresh();
		}

		
		/// <summary>
		/// Draws arrow from object label
		/// </summary>
		/// <param name="l">The object label</param>
		/// <param name="e">The event arguments</param>
		public void DrawArrow(Control l, MouseEventArgs e)
		{
			lock(this)
			{
				Redraw();
				Graphics g = Graphics.FromImage(image);
				float x = l.Left + l.Width / 2;
				float y = l.Top + l.Height / 2;
				g.DrawLine(linePen, x, y, e.X + l.Left, e.Y + l.Top);
				g.Dispose();
				Refresh();
			}
		}

		/// <summary>
		/// The "on paint" event handler
		/// </summary>
		/// <param name="sender">The sender</param>
		/// <param name="e">The event arguments</param>
		private void onPaint(object sender, PaintEventArgs e)
		{
			Graphics g = e.Graphics;
			g.DrawImage(image, 0, 0);
		}


		/// <summary>
		/// Redraws itself
		/// </summary>
		public void Redraw()
		{
			redrawImage();
			Refresh();
		}

		/// <summary>
		/// Refreshes itself
		/// </summary>
		public void RefreshObjects()
		{
			MemoryStream stream = new MemoryStream();
			SaveAll(stream);
			RemoveAll();
			PureDesktopPeer d = new PureDesktopPeer();
			d.Load(stream, null);
			Copy(d.Objects, d.Arrows, true);
			Redraw();

		}
		
		/// <summary>
		/// Redraws bakground image
		/// </summary>
		/// <returns></returns>
		protected Graphics redrawImage()
		{
			clearImage();
			lock(this)
			{
				Graphics g = Graphics.FromImage(image);
				object[] p = pairs.ToArray();
				for (int i = 0; i < p.Length; i++)
				{
					ObjectsPair pair = (ObjectsPair)p[i];
					pair.SetArrows(g);
				}
				return g;
			}
			
		}

		/// <summary>
		/// Transforms object label
		/// </summary>
		/// <param name="o">Transformed object</param>
		/// <returns>Transformation result</returns>
		private static object transformObjectLabel(object o)
		{
			IObjectLabel l = o as IObjectLabel;
			return l.Object;
		}

		/// <summary>
		/// Transforms arrow label
		/// </summary>
		/// <param name="o">Transformed object</param>
		/// <returns>Transformation result</returns>
		private static object transformArrowLabel(object o)
		{
			IArrowLabel l = o as IArrowLabel;
			return l.Arrow;
		}

		/// <summary>
		/// Clears background image
		/// </summary>
		private void clearImage()
		{
			lock(this)
			{
				Graphics g = Graphics.FromImage(image);
				g.FillRectangle(brush, 0, 0, image.Width, image.Height);
				g.Dispose();
			}
		}

        private void InitializeComponent()
        {
            this.SuspendLayout();
            // 
            // PanelDesktop
            // 
            this.AutoSizeChanged += new System.EventHandler(this.PanelDesktop_SizeChanged);
            this.Resize += new System.EventHandler(this.PanelDesktop_SizeChanged);
            this.ResumeLayout(false);

        }

        private void PanelDesktop_SizeChanged(object sender, EventArgs e)
        {
            ResizeImage();
            redrawImage();
            Refresh();
        }
	}
}
