using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;

namespace FormLayout
{
	public partial class FormLayoutCtl : UserControl
	{
		private EnvDTE.WindowEvents windowsEvents;

		EnvDTE._DTE _dte;//A control was resized in the active document. Resize
		// the control on the virtual screen by invalidating the display.
		private void onControlSized(object sender, System.EventArgs e)
		{
			panel1.Invalidate();
		}

		//A control was moved in the active document. Resize
		// the control on the virtual screen by invalidating the display.
		private void onControlMoved(object sender, System.EventArgs e)
		{
			panel1.Invalidate();
		}

		//The tool window was resized. Resize
		// the control on the virtual screen by invalidating the display.
		private void onResize(object sender, System.EventArgs e)
		{
			panel1.Invalidate();
		}

		//A control was added, connect to it's events and then draw the control.
		private void onControlsAdded(object sender, System.Windows.Forms.ControlEventArgs e)
		{
			e.Control.Move += new System.EventHandler(this.onControlMoved);
			e.Control.SizeChanged += new System.EventHandler(this.onControlSized);
			DrawControl(e.Control);
		}

		//A control is removed, disconnect from it's events and redraw the
		// virtual screen:
		private void onControlsRemoved(object sender, System.Windows.Forms.ControlEventArgs e)
		{
			e.Control.Move -= new System.EventHandler(this.onControlMoved);
			e.Control.SizeChanged -= new System.EventHandler(this.onControlSized);
			panel1.Invalidate();
		}

		//The new window was activated. If it is a forms designer window,
		// then draw the form on the virtual screen.
		public void WindowActivated(EnvDTE.Window gotFocus, EnvDTE.Window lostFocus)
		{
			if (gotFocus.Type == EnvDTE.vsWindowType.vsWindowTypeDocument)
			{
				try
				{
					System.ComponentModel.Design.IDesignerHost designerHost;
					designerHost = (System.ComponentModel.Design.IDesignerHost)gotFocus.Object;

					if (control != null)
					{
						control.ControlAdded -= new System.Windows.Forms.ControlEventHandler(this.onControlsAdded);
						control.ControlRemoved -= new System.Windows.Forms.ControlEventHandler(this.onControlsRemoved);
						control.Resize -= new System.EventHandler(this.onResize);
						foreach (System.Windows.Forms.Control ctl in control.Controls)
						{
							ctl.SizeChanged -= new System.EventHandler(this.onControlSized);
							ctl.Move -= new System.EventHandler(this.onControlMoved);
						}
					}

					control = (System.Windows.Forms.Control)designerHost.RootComponent;
					control.ControlAdded += new System.Windows.Forms.ControlEventHandler(this.onControlsAdded);
					control.ControlRemoved += new System.Windows.Forms.ControlEventHandler(this.onControlsRemoved);
					control.Resize += new System.EventHandler(this.onResize);
					foreach (System.Windows.Forms.Control ctl in control.Controls)
					{
						ctl.SizeChanged += new System.EventHandler(this.onControlSized);
						ctl.Move += new System.EventHandler(this.onControlMoved);
					}

					panel1.Invalidate();
				}
				catch (System.Exception)
				{
					control = null;
					panel1.Invalidate();
				}
			}
		}

		//A helper function so that we can use the DTE object:
		internal void SetDTE(EnvDTE._DTE dte)
		{
			_dte = dte;
			windowsEvents = (EnvDTE.WindowEvents)_dte.Events.get_WindowEvents(null);
			windowsEvents.WindowActivated += new EnvDTE._dispWindowEvents_WindowActivatedEventHandler(this.WindowActivated);
		}

		System.Windows.Forms.Control control;
		public FormLayoutCtl()
		{
			InitializeComponent();
		}

		//A constant sizing multiplier:
		const float padding = 60.0F;

		//Find the amount of scale that should be applied to each item that is drawn.
		// This is computed based on the size of the drawing area of the window.
		float ComputeScale()
		{
			System.Drawing.Rectangle rc = System.Windows.Forms.Screen.PrimaryScreen.Bounds;
			float min = (float)System.Math.Min(panel1.Width, panel1.Height);
			min = (float)System.Math.Max(min, 200.0); //Don't shrink too small
			min -= padding;
			return ((min * 1.0F) / (rc.Width * 1.0F));
		}

		//Size the virtual monitor based on the real monitor:
		void ComputeVirtualScreen(out float X, out float Y)
		{
			System.Drawing.Rectangle rc = System.Windows.Forms.Screen.PrimaryScreen.Bounds;
			float scale = ComputeScale();
			X = rc.Width * scale;
			Y = rc.Height * scale;
		}

		void ComputeTopOfRect(out float X, out float Y)
		{
			float vscreenX;
			float vscreenY;
			ComputeVirtualScreen(out vscreenX, out vscreenY);
			X = (panel1.Width - vscreenX) / 2.0F;
			Y = (panel1.Height - vscreenY) / 2.0F;
		}

		void DrawControl(System.Windows.Forms.Control ctl)
		{
			float scale = ComputeScale();
			float vscreenX;
			float vscreenY;
			float topOfRectX;
			float topOfRectY;

			ComputeVirtualScreen(out vscreenX, out vscreenY);
			ComputeTopOfRect(out topOfRectX, out topOfRectY);

			System.Drawing.Graphics g = System.Drawing.Graphics.FromHwnd(panel1.Handle);
			g.DrawRectangle(System.Drawing.Pens.Black, topOfRectX + (padding * scale) + (ctl.Left * scale), topOfRectY + (padding * scale) + (ctl.Top * scale), ctl.Width * scale, ctl.Height * scale);
		}

		private void panel1_Paint(object sender, System.Windows.Forms.PaintEventArgs e)
		{
			System.Drawing.Graphics g = System.Drawing.Graphics.FromHwnd(panel1.Handle);

			g.DrawRectangle(System.Drawing.Pens.Gray, 0, 0, panel1.Width - 1, panel1.Height - 1);

			float scale = ComputeScale();
			float vscreenX;
			float vscreenY;
			float topOfRectX;
			float topOfRectY;

			ComputeVirtualScreen(out vscreenX, out vscreenY);
			ComputeTopOfRect(out topOfRectX, out topOfRectY);

			//Draw the 'monitor' (stage 1)
			const float monitorPadding = 40;
			float leftX = topOfRectX - (monitorPadding * scale);
			float topY = topOfRectY - (monitorPadding * scale);
			float rightX = leftX + vscreenX + (monitorPadding * 2 * scale);
			float bottomY = topY + vscreenY + (monitorPadding * 2 * scale);
			g.FillRectangle(System.Drawing.Brushes.LightGray, leftX, topY, vscreenX + (monitorPadding * 2 * scale), vscreenY + (monitorPadding * 2 * scale));
			//Top edge
			g.DrawLine(System.Drawing.Pens.Gray, leftX, topY, rightX, topY);
			//Left edge
			g.DrawLine(System.Drawing.Pens.Gray, leftX, topY, leftX, bottomY);
			//Top inside edge
			g.DrawLine(System.Drawing.Pens.White, leftX + 1, topY + 1, rightX - 1, topY + 1);
			//Left inside edge
			g.DrawLine(System.Drawing.Pens.White, leftX + 1, topY + 1, leftX + 1, bottomY - 1);
			//Bottom edge
			g.DrawLine(System.Drawing.Pens.Black, leftX, bottomY, rightX, bottomY);
			//Right edge
			g.DrawLine(System.Drawing.Pens.Black, rightX, topY, rightX, bottomY);
			//Bottom inside edge
			g.DrawLine(System.Drawing.Pens.DarkGray, leftX + 1, bottomY - 1, rightX - 1, bottomY - 1);
			//Right inside edge
			g.DrawLine(System.Drawing.Pens.DarkGray, rightX - 1, topY + 1, rightX - 1, bottomY - 1);

			//Draw the monitor base
			float monitorPaddingXscale = monitorPadding * scale;
			float vscreenXDiv4 = (vscreenX / 4.0F);
			float leftXPlusvscreenXDiv4 = vscreenXDiv4 + leftX;
			g.FillRectangle(System.Drawing.Brushes.LightGray, leftXPlusvscreenXDiv4, bottomY + 1, (vscreenX / 2.0F) + (monitorPaddingXscale * 2), monitorPaddingXscale);
			//Bottom outer edge
			g.DrawLine(System.Drawing.Pens.Black, leftXPlusvscreenXDiv4 - 1, bottomY + 1 + monitorPaddingXscale, rightX - vscreenXDiv4, bottomY + 1 + monitorPaddingXscale);
			//Right outer edge
			g.DrawLine(System.Drawing.Pens.Black, rightX - vscreenXDiv4, bottomY + 1 + monitorPaddingXscale, rightX - vscreenXDiv4, bottomY);
			//Right inside edge
			g.DrawLine(System.Drawing.Pens.DarkGray, rightX - vscreenXDiv4 - 1, bottomY + monitorPaddingXscale, rightX - 1 - vscreenXDiv4, bottomY + 1);
			//Left outer edge
			g.DrawLine(System.Drawing.Pens.Gray, leftXPlusvscreenXDiv4 - 1, bottomY, leftXPlusvscreenXDiv4 - 1, (bottomY + monitorPaddingXscale) + 1);
			//Left inside edge
			g.DrawLine(System.Drawing.Pens.White, leftXPlusvscreenXDiv4, (bottomY + monitorPaddingXscale), leftXPlusvscreenXDiv4, bottomY + 1);
			//Bottom inside edge
			g.DrawLine(System.Drawing.Pens.Gray, leftXPlusvscreenXDiv4, bottomY + monitorPaddingXscale, rightX - vscreenXDiv4 - 1, bottomY + monitorPaddingXscale);

			g.FillRectangle(new System.Drawing.SolidBrush(System.Drawing.SystemColors.Desktop), topOfRectX - 1, topOfRectY - 1, vscreenX + 2, vscreenY + 2);

			//Draw the 'monitor' (stage 2)
			//Draw the top edge
			g.DrawLine(System.Drawing.Pens.Black, topOfRectX, topOfRectY - 1, topOfRectX + vscreenX, topOfRectY - 1);
			//Draw the left edge
			g.DrawLine(System.Drawing.Pens.Black, topOfRectX, topOfRectY - 1, topOfRectX, topOfRectY + vscreenY);
			//Draw the bottom edge
			g.DrawLine(System.Drawing.Pens.DarkGray, topOfRectX, topOfRectY + vscreenY, topOfRectX + vscreenX, topOfRectY + vscreenY);
			//Draw the right edge
			g.DrawLine(System.Drawing.Pens.DarkGray, topOfRectX + vscreenX, topOfRectY - 1, topOfRectX + vscreenX, topOfRectY + vscreenY);

			//Draw the bottom outside edge
			g.DrawLine(System.Drawing.Pens.DarkGray, topOfRectX - 1, topOfRectY - 2, topOfRectX + vscreenX + 1, topOfRectY - 2);
			//Draw the right outside edge
			g.DrawLine(System.Drawing.Pens.White, topOfRectX + vscreenX + 1, topOfRectY - 2, topOfRectX + vscreenX + 1, topOfRectY + vscreenY + 1);
			//Draw the bottom outside edge
			g.DrawLine(System.Drawing.Pens.White, topOfRectX - 1, topOfRectY + vscreenY + 1, topOfRectX + vscreenX + 1, topOfRectY + vscreenY + 1);
			//Draw the left outside edge
			g.DrawLine(System.Drawing.Pens.DarkGray, topOfRectX - 1, topOfRectY - 2, topOfRectX - 1, topOfRectY + vscreenY + 1);

			if (control != null)
			{
				float vformX = (control.Width * scale);
				float vformY = (control.Height * scale);

				//If the control is a form and it has one, then draw the titlebar
				if (control is System.Windows.Forms.Form)
				{
					System.Windows.Forms.Form form = (System.Windows.Forms.Form)control;
					if (form.FormBorderStyle != System.Windows.Forms.FormBorderStyle.None)
					{
						g.FillRectangle(System.Drawing.Brushes.White, topOfRectX + (padding * scale), topOfRectY + (padding * scale) - (System.Windows.Forms.SystemInformation.CaptionHeight * scale), vformX, (System.Windows.Forms.SystemInformation.CaptionHeight * scale) - 1);
					}
				}

				g.FillRectangle(new System.Drawing.SolidBrush(control.BackColor), topOfRectX + (padding * scale), topOfRectY + (padding * scale) - 1, vformX, vformY);

				//Draw any controls that may be on the form:
				foreach (System.Windows.Forms.Control ctl in control.Controls)
				{
					DrawControl(ctl);
					//g.DrawRectangle(System.Drawing.Pens.Black, topOfRectX + (padding*scale) + (ctl.Left*scale), topOfRectY + (padding*scale) + (ctl.Top*scale), ctl.Width*scale, ctl.Height*scale);
				}
			}
		}

		private void panel1_Resize(object sender, EventArgs e)
		{
			panel1.Invalidate();
		}
	}
}
