context: using System;
  using System.Collections;
  using System.ComponentModel;
  using System.Drawing;
  using System.Drawing.Drawing2D;
  using System.Windows.Forms;
  using System.Runtime.Serialization;
  
  namespace PetriNetSimulator2
  {
  	[TypeConverter(typeof(SubsystemConverter))]
  	[Serializable]
  	public class Subsystem : ConnectableControl, ISerializable, ICloneable, IComparable, IConnector, IMetafileModel
  	{
  		// Properties
  
  		#region public string NameID
  		[CommonProperties]
  		[Category("Identification")]
  		[Description("Name for this subsystem.")]
  		public string NameID
  		{
  			get
  			{
  				return this.sName;
  			}
  			set
  			{
  				if (this.Parent != null)
  				{
  					this.sName = value;
  
  					if (this.NameOrIndexChanged != null)
  						this.NameOrIndexChanged(this, new EventArgs());
  
  					this.Refresh();
  				}
  			}
  		}
  		#endregion
  
  		#region public new Point Location
  		[CommonProperties]
  		[Category("Layout")]
  		[Description("Location of label within editor.")]
  		public new Point Location
  		{
  			get
  			{
  				return base.Location;
  			}
  			set
  			{
  				base.Location = value;
  
  				if (this.Parent != null)
  					this.Parent.Refresh();
  			}
  		}
  		#endregion
  
  		#region public new Size Size
  		[CommonProperties]
  		[Category("Layout")]
  		[Description("Size of label in the editor.")]
  		public new Size Size
  		{
  			get
  			{
  				return base.Size;
  			}
  			set
  			{
  				base.Size = value;
  
  				if (this.Parent != null)
  					this.Parent.Refresh();
  			}
  		}
  		#endregion
  
  		#region public ArrayList Objects
  		public ArrayList Objects
  		{
  			get
  			{
  				if (this.seSubsystemEditor != null)
  					return this.seSubsystemEditor.Editor.Objects;
  				else
  					return null;
  			}
  		}
  		#endregion
  
  		#region public ArrayList Connections
  		public ArrayList Connections
  		{
  			get
  			{
  				if (this.seSubsystemEditor != null)
  					return this.seSubsystemEditor.Editor.Connections;
  				else
  					return null;
  			}
  		}
  		#endregion
  
  		#region public SubsystemEditor SubsystemEditor
  		public SubsystemEditor SubsystemEditor
  		{
  			get
  			{
  				return this.seSubsystemEditor;
  			}
  		}
  		#endregion
  
  		// Events
  		internal event EventHandler NameOrIndexChanged;
  
  		// Fields
  		private Color cBorderColor = Color.Black;
  		private string sName = "";
  		private ArrayList alObjects = new ArrayList();
  		private ArrayList alConnections = new ArrayList();
  		private Hashtable htInputGraphicsPaths = new Hashtable();
  		private Hashtable htInputInputPoints = new Hashtable();
  		private Hashtable htOutputGraphicsPaths = new Hashtable();
  		private Hashtable htOutputOutputPoints = new Hashtable();
  		private bool bSuppresDeserializationCall = false;
  		
  		private SubsystemEditor seSubsystemEditor = null;
  
  		private System.ComponentModel.IContainer components = null;
  
  		#region public Subsystem() : base(new Size(72, 72))
  		public Subsystem() : base(new Size(72, 72))
  		{
  			// This call is required by the Windows Form Designer.
  			InitializeComponent();
  
  			this.SetStyle(ControlStyles.UserPaint |	ControlStyles.AllPaintingInWmPaint | ControlStyles.DoubleBuffer, true);
  			this.UpdateStyles();
  
  			this.AllowResizing = true;
  			this.Paint += new PaintEventHandler(Subsystem_Paint);
  			this.MouseUp += new MouseEventHandler(Subsystem_MouseUp);
  			this.Resize += new EventHandler(Subsystem_Resize);
  			this.DoubleClick += new EventHandler(Subsystem_DoubleClick);
  			this.ParentChanged += new EventHandler(Subsystem_ParentChanged);
  		}
  		#endregion
  
  		// Constructor for Deserialization
  		#region public Subsystem(SerializationInfo info, StreamingContext context) : base(info, context)
  		public Subsystem(SerializationInfo info, StreamingContext context) : base(info, context)
  		{
  			// This call is required by the Windows.Forms Form Designer.
  			InitializeComponent();
  
  			this.SetStyle(ControlStyles.UserPaint |	ControlStyles.AllPaintingInWmPaint | ControlStyles.DoubleBuffer, true);
  			this.UpdateStyles();
  
  			this.AllowResizing = true;
  			this.Paint += new PaintEventHandler(Subsystem_Paint);
  			this.MouseUp += new MouseEventHandler(Subsystem_MouseUp);
  			this.Resize += new EventHandler(Subsystem_Resize);
  			this.DoubleClick += new EventHandler(Subsystem_DoubleClick);
  			this.ParentChanged += new EventHandler(Subsystem_ParentChanged);
  
  			this.Location = (Point)info.GetValue("location", typeof(Point));
  			this.seSubsystemEditor = (SubsystemEditor)info.GetValue("editor", typeof(SubsystemEditor));
  
  			// To set region, call resize
  //			this.Transition_Resize(this, EventArgs.Empty);
  		}
  		#endregion
  
  		/// <summary>
  		/// Clean up any resources being used.
  		/// </summary>
  		protected override void Dispose( bool disposing )
  		{
  			if( disposing )
  			{
  				if (components != null) 
  				{
  					components.Dispose();
  				}
  			}
  			base.Dispose( disposing );
  		}
  
  		#region Designer generated code
  		/// <summary>
  		/// Required method for Designer support - do not modify
  		/// the contents of this method with the code editor.
  		/// </summary>
  		private void InitializeComponent()
  		{
  			components = new System.ComponentModel.Container();
  		}
  		#endregion
  
  
  		#region public new void GetObjectData(SerializationInfo info, StreamingContext context)
  		public new void GetObjectData(SerializationInfo info, StreamingContext context)
  		{
  			base.GetObjectData(info, context);
  
  			info.AddValue("location", this.Location);
  			info.AddValue("editor", this.seSubsystemEditor);
  		}
  		#endregion
  
  
  		#region private void Subsystem_Paint(object sender, PaintEventArgs e)
          private void Subsystem_Paint(object sender, PaintEventArgs e)
  		{
  			// Draws Subsystem object
  			Graphics g = e.Graphics;
  
  			if (PetriNetDocument.AntiAlias == true)
  				g.SmoothingMode = SmoothingMode.AntiAlias;
  
  			PetriNetEditor pne = (PetriNetEditor)this.Parent;
  
  			Rectangle rect = this.ClientRectangle;
  
  			LinearGradientBrush lgb = new LinearGradientBrush(rect, Color.White, this.cBackgroundColor, LinearGradientMode.ForwardDiagonal);
              
  			g.FillRectangle(lgb, rect);
  
  			Pen pBlack = new Pen(this.cBorderColor, pne.Zoom * 5);
  			g.DrawRectangle(pBlack, 0, 0, this.Width - 1, this.Height - 1);
  
  			g.FillRegion(Brushes.Red, this.ConnectableInputRegion);
  			g.FillRegion(Brushes.Red, this.ConnectableOutputRegion);
  
  			Brush bBlack = new SolidBrush(Color.Black);
  			StringFormat sf = new StringFormat();
  			sf.Alignment = StringAlignment.Center;
  			sf.LineAlignment = StringAlignment.Center;
  			
  			Font f = new Font(this.Parent.Font.FontFamily, pne.Zoom * 7f, FontStyle.Bold);
  			
  			string sText = (this.sName != "") ? this.sName + "\n\n" + "S" + this.sIndex : "S" + this.sIndex.ToString();
  			g.DrawString(sText, f, bBlack, new Rectangle(new Point(0, 0), new Size(this.Width - (int)(pne.Zoom * 2), this.Height)), sf);
  
  			foreach(object o in this.htInputGraphicsPaths.Keys)
  			{
  				GraphicsPath gp = (GraphicsPath)this.htInputGraphicsPaths[o];
  				g.DrawPath(Pens.Black, gp);
  
  				string sIndex = ((Input)o).Index.ToString();
  				Point p = (Point)this.htInputInputPoints[o];
  				
  				g.DrawString(sIndex, f, bBlack, p, sf);
  			}
  
  			foreach(object o in this.htOutputGraphicsPaths.Keys)
  			{
  				GraphicsPath gp = (GraphicsPath)this.htOutputGraphicsPaths[o];
  				g.DrawPath(Pens.Black, gp);
  
  				string sIndex = ((Output)o).Index.ToString();
  				Point p = (Point)this.htOutputOutputPoints[o];
  				
  				g.DrawString(sIndex, f, bBlack, p, sf);
  			}
  
  		}
  		#endregion
  
  		#region private void Subsystem_MouseUp(object sender, System.Windows.Forms.MouseEventArgs e)
  		private void Subsystem_MouseUp(object sender, System.Windows.Forms.MouseEventArgs e)
  		{
  			// Refresh Location property
  			this.OnPropertiesChanged(this, EventArgs.Empty);
  		}
  		#endregion
  
  		#region private void Subsystem_Resize(object sender, EventArgs e)
  		private void Subsystem_Resize(object sender, EventArgs e)
  		{
  			this.AdjustConnectableRegions();	
  		}
  		#endregion
  
  		#region private void Subsystem_DoubleClick(object sender, EventArgs e)
  		private void Subsystem_DoubleClick(object sender, EventArgs e)
  		{
  			this.seSubsystemEditor.Text = "Subsystem Editor : " + this.ToString();
  			this.seSubsystemEditor.ShowDialog();
  		}
  		#endregion
  
  		#region private void Subsystem_ParentChanged(object sender, EventArgs e)
  		private void Subsystem_ParentChanged(object sender, EventArgs e)
  		{
  			if (this.Parent is PetriNetEditor)
  			{
  				if (this.seSubsystemEditor == null)
  				{
  					this.seSubsystemEditor = new SubsystemEditor(((PetriNetEditor)this.Parent).Document, this);
  				}
  				else
  				{
  					if (this.bSuppresDeserializationCall != true)
  						this.seSubsystemEditor.RestoreAfterDeserialization(((PetriNetEditor)this.Parent).Document, this);
  				}
  			}
  		}
  		#endregion
  
  
  		#region public void AddObjectReference(object o)
  		public void AddObjectReference(object o)
  		{
  			if (o is SelectableAndMovableControl)
  			{
  				this.Objects.Add(o);
  				this.seSubsystemEditor.Editor.Controls.Add((Control)o);
  			}
  			else if (o is Connection)
  				this.Connections.Add(o);
  		}
  		#endregion
  
  		#region public void AdjustConnectableRegions()
  		public void AdjustConnectableRegions()
  		{
  			if (this.Parent is PetriNetEditor)
  			{
  				PetriNetEditor pne = (PetriNetEditor)this.Parent;
  
  				this.alInputPorts.Clear();
  				this.alOutputPorts.Clear();
  
  				ArrayList alInputPoints = new ArrayList();
  				ArrayList alOutputPoints = new ArrayList();
  				ArrayList alInputs = new ArrayList();
  				ArrayList alOutputs = new ArrayList();
  
  				int iX = int.MaxValue;
  				int iY = int.MaxValue;
  				int iWidth = 0;
  				int iHeight = 0;
  
  				int iHalfArrowWidth = (int)(7 * pne.Zoom);
  
  				foreach(object o in this.Objects)
  				{
  					SelectableAndMovableControl smac = (SelectableAndMovableControl)o;
  
  					if (o is Input)
  					{
  						alInputs.Add(o);
  						alInputPoints.Add(smac.Location);
  					}
  					if (o is Output)
  					{
  						alOutputs.Add(o);
  						alOutputPoints.Add(smac.Location);
  					}
  
  					if (smac.Location.X < iX)
  						iX = smac.Location.X;
  					if (smac.Location.Y < iY)
  						iY = smac.Location.Y;
  					if (smac.Location.X + smac.Width > iWidth)
  						iWidth = smac.Location.X + smac.Width;
  					if (smac.Location.Y + smac.Height > iHeight)
  						iHeight = smac.Location.Y + smac.Height;
  				}
  
  				Rectangle rect = new Rectangle(iX, iY, iWidth - iX, iHeight - iY);
  
  				// Transform rect coordinates in client coordinates
  				float iXScaler = (float)(this.Size.Width) / (float)rect.Width;
  				float iYScaler = (float)(this.Size.Height) / (float)rect.Height;
  
  				// Smaller rect
  				float iXScaler2 = (float)(this.Width - 20 * pne.Zoom) / (float)this.Width;
  				float iYScaler2 = (float)(this.Height - 20 * pne.Zoom) / (float)this.Height;
  
  				GraphicsPath gpInputs = new GraphicsPath();
  				GraphicsPath gpOutputs = new GraphicsPath();
  
  				this.htInputGraphicsPaths.Clear();
  				this.htInputInputPoints.Clear();
  				this.htOutputGraphicsPaths.Clear();
  				this.htOutputOutputPoints.Clear();
  
  				for(int i = 0; i < alInputPoints.Count; i++)
  				{
  					object o = alInputPoints[i];
  
  					Point p = (Point)o;
  					p.X -= iX;
  					p.Y -= iY;
  
  					Point pFixed = this.GetFixedPoint(p, rect);
  					LineDirection ldEnd = this.GetDirection(p, this.GetCenterPoint(p, rect), rect);
  
  					p = pFixed;
  
  					p.X = (int)(p.X * iXScaler);
  					p.Y = (int)(p.Y * iYScaler);
  
  					//Scale to smaller rect
  					p.X = (int)((p.X)* iXScaler2) + (int)(10 * pne.Zoom);
  					p.Y = (int)((p.Y)* iYScaler2) + (int)(10 * pne.Zoom);
  
  
  					GraphicsPath gp = new GraphicsPath();
  					if (ldEnd == LineDirection.Left)
  					{
  						gp.AddPolygon(new Point[]{new Point(0, p.Y - iHalfArrowWidth), new Point(iHalfArrowWidth, p.Y), new Point(0, p.Y + iHalfArrowWidth)});
  						p.X += 3;
  					}
  					if (ldEnd == LineDirection.Up)
  					{
  						gp.AddPolygon(new Point[]{new Point(p.X - iHalfArrowWidth, 0), new Point(p.X , iHalfArrowWidth), new Point(p.X + iHalfArrowWidth, 0)});
  						p.Y += 3;
  					}
  					if (ldEnd == LineDirection.Down)
  					{
  						gp.AddPolygon(new Point[]{new Point(p.X - iHalfArrowWidth, this.Height - 1), new Point(p.X, this.Height - 1 - iHalfArrowWidth), new Point(p.X + iHalfArrowWidth, this.Height - 1)});
  						p.Y -= 3;
  					}
  					if (ldEnd == LineDirection.Right)
  					{
  						gp.AddPolygon(new Point[]{new Point(this.Width - 1, p.Y - iHalfArrowWidth), new Point(this.Width - 1 - iHalfArrowWidth, p.Y), new Point(this.Width - 1, p.Y + iHalfArrowWidth)});
  						p.X -= 3;
  					}
  
  					this.htInputInputPoints.Add(alInputs[alInputPoints.IndexOf(o)], p);
  
  					gpInputs.AddPath(gp, false);
  					this.htInputGraphicsPaths.Add(alInputs[alInputPoints.IndexOf(o)], gp);
  
  					Port port = new Port(((Input)alInputs[i]).Index, new Region(gp));
  					this.alInputPorts.Add(port);
  				}
  
  				for(int i = 0; i < alOutputPoints.Count; i++)
  				{
  					object o = alOutputPoints[i];
  
  					Point p = (Point)o;
  					p.X -= iX;
  					p.Y -= iY;
  
  					Point pFixed = this.GetFixedPoint(p, rect);
  					LineDirection ldEnd = this.GetDirection(p, this.GetCenterPoint(p, rect), rect);
  
  					p = pFixed;
  
  					p.X = (int)(p.X * iXScaler);
  					p.Y = (int)(p.Y * iYScaler);
  
  					//Scale to smaller rect
  					p.X = (int)((p.X)* iXScaler2) + (int)(10 * pne.Zoom);
  					p.Y = (int)((p.Y)* iYScaler2) + (int)(10 * pne.Zoom);
  
  
  					GraphicsPath gp = new GraphicsPath();
  					if (ldEnd == LineDirection.Left)
  					{
  						gp.AddPolygon(new Point[]{new Point(iHalfArrowWidth, p.Y - iHalfArrowWidth), new Point(0, p.Y), new Point(iHalfArrowWidth, p.Y + iHalfArrowWidth)});
  						p.X += 3;
  					}
  					if (ldEnd == LineDirection.Up)
  					{
  						gp.AddPolygon(new Point[]{new Point(p.X - iHalfArrowWidth, iHalfArrowWidth), new Point(p.X , 0), new Point(p.X + iHalfArrowWidth, iHalfArrowWidth)});
  						p.Y += 3;
  					}
  					if (ldEnd == LineDirection.Down)
  					{
  						gp.AddPolygon(new Point[]{new Point(p.X - iHalfArrowWidth, this.Height - 1 - iHalfArrowWidth), new Point(p.X, this.Height - 1), new Point(p.X + iHalfArrowWidth, this.Height - 1 - iHalfArrowWidth)});
  						p.Y -= 3;
  					}
  					if (ldEnd == LineDirection.Right)
  					{
  						gp.AddPolygon(new Point[]{new Point(this.Width - 1 - iHalfArrowWidth, p.Y - iHalfArrowWidth), new Point(this.Width - 1, p.Y), new Point(this.Width - 1 - iHalfArrowWidth, p.Y + iHalfArrowWidth)});
  						p.X -= 3;
  					}
  
  					this.htOutputOutputPoints.Add(alOutputs[alOutputPoints.IndexOf(o)], p);
  
  					gpOutputs.AddPath(gp, false);
  					this.htOutputGraphicsPaths.Add(alOutputs[alOutputPoints.IndexOf(o)], gp);
  
  					Port port = new Port(((Output)alOutputs[i]).Index, new Region(gp));
  					this.alOutputPorts.Add(port);
  				}
  
  				this.Refresh();
  			}
  		}
  		#endregion
  
  		#region public int CompareTo(object o)
  		public int CompareTo(object o)
  		{
  			if (o is Subsystem)
  			{
  				Subsystem s = (Subsystem)o;
  				return this.iIndex.CompareTo(s.Index);
  			}
  
  			throw new ArgumentException("Invalid argument");
  		}
  		#endregion
  
  		#region public object Clone()
  		public object Clone()
  		{
  			Subsystem s = new Subsystem();
  			s.Location = this.Location;
  
  			s.Size = this.Size;
  
  			// Change default size
  			if (this.Parent != null && this.Parent is EditorSurface)
  			{
  				EditorSurface es = (EditorSurface)this.Parent;
  				s.DefaultSize = new Size((int)(this.Width / es.Zoom), (int)(this.Height / es.Zoom));
  			}
  			else if (this.Parent == null)
  			{
  				s.DefaultSize = this.DefaultSize;
  			}
  
  			if (this.seSubsystemEditor != null)
  				s.seSubsystemEditor = (SubsystemEditor)this.seSubsystemEditor.Clone(s);
  
  			return s;
  		}
  		#endregion
  
  		#region public override string ToString()
  		public override string ToString()
  		{
  			if (this.NameID != null && this.NameID != "")
  				return "S" + this.sIndex + " - " + this.NameID + " (Subsystem)";
  			else
  				return "S" + this.sIndex + " (Subsystem)";
  		}
  		#endregion
  
  		#region public override bool CanConnectTo(object o)
  		public override bool CanConnectTo(object o)
  		{
  			if (o is Transition || o is Output)
  				return true;
  			else 
  				return false;
  		}
  		#endregion
  
  		#region *IConnector
  
  		#region public Point GetBeginCenterPoint(Point ptBegin, Size sz)
  		public Point GetBeginCenterPoint(Point ptBegin, Size sz)
  		{
  			if ((((float)sz.Height/(float)sz.Width * (float)ptBegin.X) < (float)ptBegin.Y) && ((-(float)sz.Height/(float)sz.Width * (float)ptBegin.X + (float)sz.Height) < (float)ptBegin.Y))
  				return new Point(ptBegin.X, (int)(sz.Height/2));
  			else if ((((float)sz.Height/(float)sz.Width * (float)ptBegin.X) >= (float)ptBegin.Y) && ((-(float)sz.Height/(float)sz.Width * (float)ptBegin.X + (float)sz.Height) <= (float)ptBegin.Y))
  				return new Point((int)(sz.Width/2), ptBegin.Y);
  			else if ((((float)sz.Height/(float)sz.Width * (float)ptBegin.X) > (float)ptBegin.Y) && ((-(float)sz.Height/(float)sz.Width * (float)ptBegin.X + (float)sz.Height) > (float)ptBegin.Y))
  				return new Point(ptBegin.X, (int)(sz.Height/2));
  			else 
  				return new Point((int)(sz.Width/2), ptBegin.Y);
  		}
  		#endregion
  
  		#region public Point GetEndCenterPoint(Point ptEnd, Size sz)
  		public Point GetEndCenterPoint(Point ptEnd, Size sz)
  		{
  			if ((((float)sz.Height/(float)sz.Width * (float)ptEnd.X) < (float)ptEnd.Y) && ((-(float)sz.Height/(float)sz.Width * (float)ptEnd.X + (float)sz.Height) < (float)ptEnd.Y))
  				return new Point(ptEnd.X, (int)(sz.Height/2));
  			else if ((((float)sz.Height/(float)sz.Width * (float)ptEnd.X) >= (float)ptEnd.Y) && ((-(float)sz.Height/(float)sz.Width * (float)ptEnd.X + (float)sz.Height) <= (float)ptEnd.Y))
  				return new Point((int)(sz.Width/2), ptEnd.Y);
  			else if ((((float)sz.Height/(float)sz.Width * (float)ptEnd.X) > (float)ptEnd.Y) && ((-(float)sz.Height/(float)sz.Width * (float)ptEnd.X + (float)sz.Height) > (float)ptEnd.Y))
  				return new Point(ptEnd.X, (int)(sz.Height/2));
  			else 
  				return new Point((int)(sz.Width/2), ptEnd.Y);
  		}
  		#endregion
  
  		#region public Point GetEndFixedPoint(Point ptEnd, Size sz)
  		public Point GetEndFixedPoint(Point ptEnd, Size sz)
  		{
  			if ((((float)sz.Height/(float)sz.Width * (float)ptEnd.X) < (float)ptEnd.Y) && ((-(float)sz.Height/(float)sz.Width * (float)ptEnd.X + (float)sz.Height) < (float)ptEnd.Y))
  
gt: 				return new Point(ptEnd.X, sz.Height);