using System;
using System.Drawing;
using System.Windows.Forms;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Runtime.InteropServices;

namespace WeifenLuo.WinFormsUI.Docking
{
	partial class DockPanel
	{
		//  This class comes from Jacob Slusser's MdiClientController class:
		//  http://www.codeproject.com/cs/miscctrl/mdiclientcontroller.asp
		private class MdiClientController : NativeWindow, IComponent, IDisposable
		{
			private bool m_autoScroll = true;
			private BorderStyle m_borderStyle = BorderStyle.Fixed3D;
			private MdiClient m_mdiClient = null;
			private Form m_parentForm = null;
			private ISite m_site = null;

			public MdiClientController()
			{
			}

			public void Dispose()
			{
				Dispose(true);
				GC.SuppressFinalize(this);
			}

			protected virtual void Dispose(bool disposing)
			{
				if (disposing)
				{
					lock (this)
					{
						if (Site != null && Site.Container != null)
							Site.Container.Remove(this);

						if (Disposed != null)
							Disposed(this, EventArgs.Empty);
					}
				}
			}

			public bool AutoScroll
			{
				get { return m_autoScroll; }
				set
				{
					// By default the MdiClient control scrolls. It can appear though that
					// there are no scrollbars by turning them off when the non-client
					// area is calculated. I decided to expose this method following
					// the .NET vernacular of an AutoScroll property.
					m_autoScroll = value;
					if (MdiClient != null)
						UpdateStyles();
				}
			}

			public BorderStyle BorderStyle
			{
				set
				{
					// Error-check the enum.
					if (!Enum.IsDefined(typeof(BorderStyle), value))
						throw new InvalidEnumArgumentException();

					m_borderStyle = value;

					if (MdiClient == null)
						return;

					// This property can actually be visible in design-mode,
					// but to keep it consistent with the others,
					// prevent this from being show at design-time.
					if (Site != null && Site.DesignMode)
						return;

					// There is no BorderStyle property exposed by the MdiClient class,
					// but this can be controlled by Win32 functions. A Win32 ExStyle
					// of WS_EX_CLIENTEDGE is equivalent to a Fixed3D border and a
					// Style of WS_BORDER is equivalent to a FixedSingle border.

					// This code is inspired Jason Dori's article:
					// "Adding designable borders to user controls".
					// http://www.codeproject.com/cs/miscctrl/CsAddingBorders.asp

					// Get styles using Win32 calls
					int style = NativeMethods.GetWindowLong(MdiClient.Handle, (int)Win32.GetWindowLongIndex.GWL_STYLE);
					int exStyle = NativeMethods.GetWindowLong(MdiClient.Handle, (int)Win32.GetWindowLongIndex.GWL_EXSTYLE);

					// Add or remove style flags as necessary.
					switch (m_borderStyle)
					{
						case BorderStyle.Fixed3D:
							exStyle |= (int)Win32.WindowExStyles.WS_EX_CLIENTEDGE;
							style &= ~((int)Win32.WindowStyles.WS_BORDER);
							break;

						case BorderStyle.FixedSingle:
							exStyle &= ~((int)Win32.WindowExStyles.WS_EX_CLIENTEDGE);
							style |= (int)Win32.WindowStyles.WS_BORDER;
							break;

						case BorderStyle.None:
							style &= ~((int)Win32.WindowStyles.WS_BORDER);
							exStyle &= ~((int)Win32.WindowExStyles.WS_EX_CLIENTEDGE);
							break;
					}

					// Set the styles using Win32 calls
					NativeMethods.SetWindowLong(MdiClient.Handle, (int)Win32.GetWindowLongIndex.GWL_STYLE, style);
					NativeMethods.SetWindowLong(MdiClient.Handle, (int)Win32.GetWindowLongIndex.GWL_EXSTYLE, exStyle);

					// Cause an update of the non-client area.
					UpdateStyles();
				}
			}

			public MdiClient MdiClient
			{
				get { return m_mdiClient; }
			}

			[Browsable(false)]
			public Form ParentForm
			{
				get { return m_parentForm; }
				set
				{
					// If the ParentForm has previously been set,
					// unwire events connected to the old parent.
					if (m_parentForm != null)
					{
						m_parentForm.HandleCreated -= new EventHandler(ParentFormHandleCreated);
						m_parentForm.MdiChildActivate -= new EventHandler(ParentFormMdiChildActivate);
					}

					m_parentForm = value;

					if (m_parentForm == null)
						return;

					// If the parent form has not been created yet,
					// wait to initialize the MDI client until it is.
					if (m_parentForm.IsHandleCreated)
					{
						InitializeMdiClient();
						RefreshProperties();
					}
					else
						m_parentForm.HandleCreated += new EventHandler(ParentFormHandleCreated);

					m_parentForm.MdiChildActivate += new EventHandler(ParentFormMdiChildActivate);
				}
			}

			public ISite Site
			{
				get { return m_site; }
				set
				{
					m_site = value;

					if (m_site == null)
						return;

					// If the component is dropped onto a form during design-time,
					// set the ParentForm property.
					IDesignerHost host = (value.GetService(typeof(IDesignerHost)) as IDesignerHost);
					if (host != null)
					{
						Form parent = host.RootComponent as Form;
						if (parent != null)
							ParentForm = parent;
					}
				}
			}

			public void RenewMdiClient()
			{
				// Reinitialize the MdiClient and its properties.
				InitializeMdiClient();
				RefreshProperties();
			}

			public event EventHandler Disposed;

			public event EventHandler HandleAssigned;

			public event EventHandler MdiChildActivate;

			public event LayoutEventHandler Layout;

			protected virtual void OnHandleAssigned(EventArgs e)
			{
				// Raise the HandleAssigned event.
				if (HandleAssigned != null)
					HandleAssigned(this, e);
			}

			protected virtual void OnMdiChildActivate(EventArgs e)
			{
				// Raise the MdiChildActivate event
				if (MdiChildActivate != null)
					MdiChildActivate(this, e);
			}

			protected virtual void OnLayout(LayoutEventArgs e)
			{
				// Raise the Layout event
				if (Layout != null)
					Layout(this, e);
			}

			public event PaintEventHandler Paint;

			protected virtual void OnPaint(PaintEventArgs e)
			{
				// Raise the Paint event.
				if (Paint != null)
					Paint(this, e);
			}

			protected override void WndProc(ref Message m)
			{
				switch (m.Msg)
				{
					case (int)Win32.Msgs.WM_NCCALCSIZE:
						// If AutoScroll is set to false, hide the scrollbars when the control
						// calculates its non-client area.
						if (!AutoScroll)
							NativeMethods.ShowScrollBar(m.HWnd, (int)Win32.ScrollBars.SB_BOTH, 0 /*false*/);
						break;
				}

				base.WndProc(ref m);
			}

			private void ParentFormHandleCreated(object sender, EventArgs e)
			{
				// The form has been created, unwire the event, and initialize the MdiClient.
				this.m_parentForm.HandleCreated -= new EventHandler(ParentFormHandleCreated);
				InitializeMdiClient();
				RefreshProperties();
			}

			private void ParentFormMdiChildActivate(object sender, EventArgs e)
			{
				OnMdiChildActivate(e);
			}

			private void MdiClientLayout(object sender, LayoutEventArgs e)
			{
				OnLayout(e);
			}

			private void MdiClientHandleDestroyed(object sender, EventArgs e)
			{
				// If the MdiClient handle has been released, drop the reference and
				// release the handle.
				if (m_mdiClient != null)
				{
					m_mdiClient.HandleDestroyed -= new EventHandler(MdiClientHandleDestroyed);
					m_mdiClient = null;
				}

				ReleaseHandle();
			}

			private void InitializeMdiClient()
			{
				// If the mdiClient has previously been set, unwire events connected
				// to the old MDI.
				if (MdiClient != null)
				{
					MdiClient.HandleDestroyed -= new EventHandler(MdiClientHandleDestroyed);
					MdiClient.Layout -= new LayoutEventHandler(MdiClientLayout);
				}

				if (ParentForm == null)
					return;

				// Get the MdiClient from the parent form.
				foreach (Control control in ParentForm.Controls)
				{
					// If the form is an MDI container, it will contain an MdiClient control
					// just as it would any other control.

					m_mdiClient = control as MdiClient;
					if (m_mdiClient == null)
						continue;

					// Assign the MdiClient Handle to the NativeWindow.
					ReleaseHandle();
					AssignHandle(MdiClient.Handle);

					// Raise the HandleAssigned event.
					OnHandleAssigned(EventArgs.Empty);

					// Monitor the MdiClient for when its handle is destroyed.
					MdiClient.HandleDestroyed += new EventHandler(MdiClientHandleDestroyed);
					MdiClient.Layout += new LayoutEventHandler(MdiClientLayout);

					break;
				}
			}

			private void RefreshProperties()
			{
				// Refresh all the properties
				BorderStyle = m_borderStyle;
				AutoScroll = m_autoScroll;
			}

			private void UpdateStyles()
			{
				// To show style changes, the non-client area must be repainted. Using the
				// control's Invalidate method does not affect the non-client area.
				// Instead use a Win32 call to signal the style has changed.
				NativeMethods.SetWindowPos(MdiClient.Handle, IntPtr.Zero, 0, 0, 0, 0,
					Win32.FlagsSetWindowPos.SWP_NOACTIVATE |
					Win32.FlagsSetWindowPos.SWP_NOMOVE |
					Win32.FlagsSetWindowPos.SWP_NOSIZE |
					Win32.FlagsSetWindowPos.SWP_NOZORDER |
					Win32.FlagsSetWindowPos.SWP_NOOWNERZORDER |
					Win32.FlagsSetWindowPos.SWP_FRAMECHANGED);
			}
		}

		private MdiClientController m_mdiClientController = null;
		private MdiClientController GetMdiClientController()
		{
			if (m_mdiClientController == null)
			{
				m_mdiClientController = new MdiClientController();
				m_mdiClientController.HandleAssigned += new EventHandler(MdiClientHandleAssigned);
				m_mdiClientController.MdiChildActivate += new EventHandler(ParentFormMdiChildActivate);
				m_mdiClientController.Layout += new LayoutEventHandler(MdiClient_Layout);
			}

			return m_mdiClientController;
		}

		private void ParentFormMdiChildActivate(object sender, EventArgs e)
		{
			if (GetMdiClientController().ParentForm == null)
				return;

			Control control = GetMdiClientController().ParentForm.ActiveMdiChild;
			IDockContent content = m_contents.Find(control);
			if (content == null)
				return;

			if (content.DockHandler.DockPanel == this && content.DockHandler.Pane != null)
				content.DockHandler.Pane.ActiveContent = content;
		}

		private bool MdiClientExists
		{
			get { return GetMdiClientController().MdiClient != null; }
		}

		private void SetMdiClientBounds(Rectangle bounds)
		{
			GetMdiClientController().MdiClient.Bounds = bounds;
		}

		private void SuspendMdiClientLayout()
		{
			if (GetMdiClientController().MdiClient != null)
				GetMdiClientController().MdiClient.SuspendLayout();
		}

		private void ResumeMdiClientLayout(bool perform)
		{
			if (GetMdiClientController().MdiClient != null)
				GetMdiClientController().MdiClient.ResumeLayout(perform);
		}

		private void PerformMdiClientLayout()
		{
			if (GetMdiClientController().MdiClient != null)
				GetMdiClientController().MdiClient.PerformLayout();
		}

		// Called when:
		// 1. DockPanel.DocumentStyle changed
		// 2. DockPanel.Visible changed
		// 3. MdiClientController.Handle assigned
		private void SetMdiClient()
		{
			MdiClientController controller = GetMdiClientController();

			if (this.DocumentStyle == DocumentStyle.DockingMdi)
			{
				controller.AutoScroll = false;
				controller.BorderStyle = BorderStyle.None;
				if (MdiClientExists)
					controller.MdiClient.Dock = DockStyle.Fill;
			}
			else if (DocumentStyle == DocumentStyle.DockingSdi || DocumentStyle == DocumentStyle.DockingWindow)
			{
				controller.AutoScroll = true;
				controller.BorderStyle = BorderStyle.Fixed3D;
				if (MdiClientExists)
					controller.MdiClient.Dock = DockStyle.Fill;
			}
			else if (this.DocumentStyle == DocumentStyle.SystemMdi)
			{
				controller.AutoScroll = true;
				controller.BorderStyle = BorderStyle.Fixed3D;
				if (controller.MdiClient != null)
				{
					controller.MdiClient.Dock = DockStyle.None;
					controller.MdiClient.Bounds = SystemMdiClientBounds;
				}
			}
		}

		internal Rectangle RectangleToMdiClient(Rectangle rect)
		{
			if (MdiClientExists)
				return GetMdiClientController().MdiClient.RectangleToClient(rect);
			else
				return Rectangle.Empty;
		}
	}
}
