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>
	/// The associated with Arrow control
	/// </summary>
	[Serializable()]
	public class ArrowLabel : NamedComponent, ISerializable, IArrowLabel
	{
		/// <summary>
		/// Width
		/// </summary>
		const int width = 40;

		/// <summary>
		/// Height
		/// </summary>
		const int height = 40;

		/// <summary>
		/// Height of caption
		/// </summary>
		const int captionHeight = 20;

		/// <summary>
		/// The base triangle for arrow drawing
		/// </summary>
		private static int[] triangle = new int[]{0, 0, -10, 4, -10, -4};

		/// <summary>
		/// The label of source object
		/// </summary>
		private IObjectLabel source;

		/// <summary>
		/// The label of target object
		/// </summary>
		private IObjectLabel target;

		/// <summary>
		/// The pair of correspond object label
		/// </summary>
		private ObjectsPair pair;

		/// <summary>
		/// Matrix for arrow triangle drawing
		/// </summary>
		private Matrix matrix;

		/// <summary>
		/// Pen for border drawing
		/// </summary>
		private Pen borderPen;

		/// <summary>
		/// The associated arrow
		/// </summary>
		private ICategoryArrow arrow;

		/// <summary>
		/// Pen for arrow line drawing
		/// </summary>
		private Pen linePen;

		/// <summary>
		/// The brush for arrow triangle drawing
		/// </summary>
		private Brush triangleBrush;

		/// <summary>
		/// The triangle vertices
		/// </summary>
		private PointF[] trianglePoints;

		/// <summary>
		/// The number of source object
		/// </summary>
		private object sourceNumber;

		/// <summary>
		/// The number of target object
		/// </summary>
		private object targetNumber;

		/// <summary>
		/// Brush for text foreground drawing
		/// </summary>
		new private Brush textBrush;

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

		/// <summary>
		/// Constructor
		/// </summary>
		/// <param name="button">Associated button</param>
		/// <param name="arrow">Associated arrow</param>
		/// <param name="source">Associated source</param>
		/// <param name="target">Associated target</param>
		public ArrowLabel(PaletteButton button, ICategoryArrow arrow, 
			IObjectLabel source, IObjectLabel target) : base(button)
		{
			this.arrow = arrow;
			this.source = source;
			this.target = target;
			Initialize();
		}
		/// <summary>
		/// Serialization constructor
		/// </summary>
		/// <param name="info">Serialization info</param>
		/// <param name="context">Streaming context</param>
		public ArrowLabel(SerializationInfo info, StreamingContext context) : base(info, context)
		{
			arrow = (ICategoryArrow)info.GetValue("Arrow", typeof(object));
			sourceNumber = (int)info.GetValue("SourceNumber", typeof(int));
			targetNumber = (int)info.GetValue("TargetNumber", typeof(int));
		}
		
		/// <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("Arrow", arrow);
			info.AddValue("SourceNumber", sourceNumber);
			info.AddValue("TargetNumber", targetNumber);
		}

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


		/// <summary>
		/// Removes itself
		/// </summary>
		/// <param name="removeForm">The "should remove properties editor" flag</param>
		public void Remove(bool removeForm)
		{
			Desktop.Tools.RemoveArrowNode(this);
			RemoveFromComponent();
			pair.Remove(this);
			pair.Refresh();
			if (removeForm)
			{
				RemoveForm();
			}
			if (Arrow is IRemovableObject)
			{
				IRemovableObject obj = Arrow as IRemovableObject;
				obj.RemoveObject();
			}
			arrow = null;
			GC.Collect();
		}

		/// <summary>
		/// Updates associated forms
		/// </summary>
		public override void UpdateForms()
		{
			UpdateForm();
		}

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

		/// <summary>
		/// The label associated with arrow source
		/// </summary>
		public virtual IObjectLabel Source
		{
			get
			{
				return source;
			}
			set
			{
				source = value;
			}
		}

		/// <summary>
		/// The label associated with arrow target
		/// </summary>
		public virtual IObjectLabel Target
		{
			get
			{
				return target;
			}
			set
			{
				target = value;
			}
		}

		/// <summary>
		/// The number of source object
		/// </summary>
		public object SourceNumber
		{
			get
			{
				return sourceNumber;
			}
			set
			{
				sourceNumber = value;
			}
		}

		
		/// <summary>
		/// The number of target object
		/// </summary>
		public object TargetNumber
		{
			get
			{
				return targetNumber;
			}
			set
			{
				targetNumber = value;
			}
		}

		/// <summary>
		/// The associated arrow
		/// </summary>
		public ICategoryArrow Arrow
		{
			get
			{
				return arrow;
			}
			set
			{
				arrow = value;
			}
		}


		/// <summary>
		/// Initialization
		/// </summary>
		public void Initialize()
		{
			Paint += new PaintEventHandler(onPaint);
			initNamedEventHandlers();
			captionEditor.Visible = false;
			imagePosition = new PointF(5, 20);
			Width = width;
			Height = height;
			Rectangle r = new Rectangle();
			r.Width = Width;
			r.Height = captionHeight;
			EditorRectangle = r;
			textBrush = new SolidBrush(Color.White);
			borderPen = new Pen(Color.Black);
			linePen = new Pen(Color.Black);
			matrix = new Matrix();
			trianglePoints = new PointF[3];
			textBrush = new SolidBrush(Color.Black);
			for (int i = 0; i < 3; i++)
			{
				trianglePoints[i] = new PointF(0, 0);
			}
			triangleBrush = new SolidBrush(Color.Black);
		
		}

		/// <summary>
		/// The pair of associated objects labels 
		/// </summary>
		public ObjectsPair Pair
		{
			set
			{
				if (!value.Belongs(source, target))
				{
					throw new Exception("");
				}
				pair = value;
			}
		}
		
		/// <summary>
		/// Draws itself
		/// </summary>
		/// <param name="g">Graphics to draw</param>
		public void Draw(Graphics g)
		{
			int x1 = 0;
			int y1 = 0;
			int x2 = 0;
			int y2 = 0;
			int x3 = 0;
			int y3 = 0;
			Panel p = null;
			if (Source is Panel)
			{
				p = Source as Panel;
			}
			else
			{
				p = ContainerObjectLabel.GetPanel(Source);
			}
			x1 = p.Left + p.Width / 2;
			y1 = p.Top + p.Height / 2;
			x2 = Left + Width / 2;
			y2 = Top + Height / 2;
			if (Target is Panel)
			{
				p = Target as Panel;
			}
			else
			{
				p = ContainerObjectLabel.GetPanel(Target);
			}
			x3 = p.Left + p.Width / 2;
			y3 = p.Top + p.Height / 2;
			g.DrawLine(linePen, x1, y1, x2, y2);
			g.DrawLine(linePen, x2, y2, x3, y3);
			for (int i = 0; i < 3; i++)
			{
				trianglePoints[i].X = triangle[2 * i];
				trianglePoints[i].Y = triangle[2 * i + 1];
			}
			matrix.Reset();
			float dx = x3 - x2;
			float dy = y3 - y2;
			double s = Math.Sqrt(dx * dx + dy * dy);
			if (s == 0)
			{
				return;
			}
			double angle = 180 * Math.Atan2(dy / s, dx / s) / Math.PI;
			matrix.Translate(x2 + dx / 2, y2 + dy / 2);
			matrix.RotateAt((float)angle, trianglePoints[0]);
			matrix.TransformPoints(trianglePoints);
			g.FillPolygon(triangleBrush, trianglePoints);
		}

		/// <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("ArrowLabel");
			XmlAttribute attrName = doc.CreateAttribute("Name");
			attrName.Value = ComponentName;
			element.Attributes.Append(attrName);
			if (Arrow is IXmlElementCreator)
			{
				IXmlElementCreator c = Arrow as IXmlElementCreator;
				XmlElement child = c.CreateXml(doc);
				element.AppendChild(child);
			}
			return element;
		}




		/// <summary>
		/// The on paint event handler
		/// </summary>
		/// <param name="sender">The sender</param>
		/// <param name="e">The event handler arguments</param>
		protected void onPaint(object sender, PaintEventArgs e)
		{
			Graphics g = e.Graphics;
			g.DrawRectangle(borderPen, 0, 0, Width - 1, Height - 1);
			g.DrawString(ComponentName, font, textBrush, 5F, 5F);
		}
		#region IArrowLabel Members

		IObjectLabel IArrowLabel.Source
		{
			get
			{
				return source;
			}
			set
			{
				source = value as ObjectLabel;
			}
		}

		IObjectLabel IArrowLabel.Target
		{
			get
			{
				return target;
			}
			set
			{
				target = value as ObjectLabel;
			}
		}

		#endregion

		#region INamedComponent Members

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

		public override int Kind
		{
			get
			{
				return kind;
			}
		}

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

		void INamedComponent.Remove()
		{
			// TODO:  Add ArrowLabel.DiagramUI.INamedComponent.Remove implementation
		}

		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
	}

}
