/*
 * Copyright © 2015 David Hall
 *
 * Design Notes:-
 * --------------
 * TableLayoutPanels are the containers to hold the sub sections
 * (FlowLayoutPanels), while the FlowLayoutPanels holds and performs the layout
 * of the controls:
 *
 * - Main area (tableLayoutPanelMainArea):
 *   + Main icon (pictureBoxMainIcon).
 *   + Main information (flowLayoutPanelMainAreaControls).
 *   + Content (flowLayoutPanelMainAreaControls).
 *   + Expanded information (flowLayoutPanelMainAreaControls).
 *   + Progressbar (flowLayoutPanelMainAreaControls).
 *   + Radio buttons (flowLayoutPanelMainAreaControls).
 *   + Command links. (flowLayoutPanelMainAreaControls).
 * - Sub area (tableLayoutPanelSubArea).
 *   + Expand/Collapse button (flowLayoutPanelSubAreaControls).
 *   + Verification checkbox (flowLayoutPanelSubAreaControls).
 *   + Custom/Common buttons (flowLayoutPanelsSubAreaButtons).
 * - Footer area (tableLayoutPanelFooterArea).
 *   + Footer text (flowLayoutFooterAreaText).
 * - Footer expanded information (flowLayoutFooterExpandedInformationArea).
 *   + Expanded information (flowLayoutPanelFooterExpandedInformationAreaText).
 *
 * Limitations:
 * - Form issues:
 *   + No form icon when TaskDialog.CanBeMinimized is true.
 *     * Note: .Net does not provide an easy way to convert bitmap to icon.
 *   + Might not support right to left layouts.
 *   + "Retry" button will be added in a wrong order.
 *   + Command links has no accelerator key support.
 *     * Note: CommandLink class draws the text instead of letting the button
 *       handle the text.
 *
 * References:
 * - http://www.codeproject.com/KB/vista/Vista_TaskDialog_Wrapper.aspx
 * - http://www.codeproject.com/Articles/21276/Vista-TaskDialog-Wrapper-and-Emulator
 * - http://www.codeproject.com/Articles/17026/TaskDialog-for-WinForms
 *
 * Revision Control:-
 * ------------------
 * Created On: 2007 November 26
 * Major updates: 2015 Nov 6
 */

#if TASKDIALOG_EMULATE
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Text;
using System.Text.RegularExpressions;
using System.Windows.Forms;
using static Vanara.PInvoke;
using static Vanara.PInvoke;
using static Vanara.PInvoke;

namespace Vanara.Windows.Forms
{
	/// <summary>
	/// Tries to emulate the Task Dialog. Form will be called when the call to
	/// Task Dialog is not supported.
	/// </summary>
	internal class EmulateTaskDialog : Form
	{
		private const int hPadding = 10;
		private System.ComponentModel.IContainer components;
		private Button defaultButton;
		private RadioButton defaultRadioButton;
		private EmulateExpInfoButton expandedInfoButton;
		private FlowLayoutPanel flowLayoutPanelFooterAreaText;
		private FlowLayoutPanel flowLayoutPanelFooterExpandedInformationText;
		private FlowLayoutPanel flowLayoutPanelMainAreaControls;
		private FlowLayoutPanel flowLayoutPanelSubAreaButtons;
		private FlowLayoutPanel flowLayoutPanelSubAreaControls;
		private bool isExpanded = true;
		private LinkLabel linkLabelExpandedInformation;
		private PictureBox pictureBoxFooterAreaIcon;
		private PictureBox pictureBoxMainAreaIcon;
		private ProgressBar progressBar;
		private Panel tableLayoutPanel1;
		private Panel tableLayoutPanel2;
		private Panel tableLayoutPanel3;
		private Panel tableLayoutPanel4;
		private TableLayoutPanel tableLayoutPanelFooterArea;
		private TableLayoutPanel tableLayoutPanelFooterExpandedInformationArea;
		private TableLayoutPanel tableLayoutPanelMainArea;
		private TableLayoutPanel tableLayoutPanelSubArea;
		private readonly TaskDialog taskDialog;
		private readonly Dictionary<int, Button> taskDialogButtons = new Dictionary<int, Button>();
		private Timer timer;
		private uint timerTickCount;

		public EmulateTaskDialog(TaskDialog newTaskDialog)
		{
			// http://dotnetperls.com/Content/Segoe-Tahoma-Windows-Forms.aspx
			// http://www.codeproject.com/KB/cs/AdjustingFontAndLayout.aspx
			// For some discussion about setting program fonts.
			Font = SystemFonts.MessageBoxFont;

			InitializeComponent();

			taskDialog = newTaskDialog;

			Reset();
		}

		public int TaskDialogRadioButtonResult { get; private set; }

		public int TaskDialogResult { get; private set; }

		public bool TaskDialogVerificationFlagChecked { get; private set; }

		/// <summary>
		/// This will return the formatted contents of the form. Useful when
		/// the user press "ctrl + c" on the form.
		/// </summary>
		/// <returns></returns>
		public override string ToString()
		{
			var sb = new StringBuilder();

			sb.AppendLine("[Window Title]");
			sb.AppendLine(Text);

			if (!string.IsNullOrEmpty(taskDialog.MainInstruction))
			{
				sb.AppendLine("");
				sb.AppendLine("[Main Instruction]");
				sb.AppendLine(taskDialog.MainInstruction);
			}

			if (!string.IsNullOrEmpty(taskDialog.Content))
			{
				sb.AppendLine("");
				sb.AppendLine("[Content]");
				sb.AppendLine(taskDialog.Content);
			}

			if (!string.IsNullOrEmpty(taskDialog.ExpandedInformation) && !taskDialog.ExpandFooterArea)
			{
				sb.AppendLine("");
				sb.AppendLine("[Expanded Information]");
				sb.AppendLine(taskDialog.ExpandedInformation);
			}

			// There should be a max of two controls here.
			foreach (Control control in flowLayoutPanelSubAreaControls.Controls)
			{
				if (control is EmulateExpInfoButton)
				{
					// Should be the expand/collapse button.
					sb.AppendLine("");
					sb.Append((isExpanded ? "[^] " : "[v] "));
					sb.Append(control.Text);
					sb.Append("  ");
				}

				var box = control as CheckBox;
				if (box == null) continue;
				sb.Append(box.Checked ? "[X] " : "[ ] ");
				sb.Append(control.Text);
				sb.Append("  ");
			}

			// Should contains only buttons. As the layout direction is right
			// to left, need to get by reverse order. Dictionary does not keep
			// the order of buttons added in. So this work around is used.
			for (var i = flowLayoutPanelSubAreaButtons.Controls.Count - 1; i >= 0; i--)
			{
				var control = flowLayoutPanelSubAreaButtons.Controls[i];
				if (!(control is Button)) continue;
				sb.Append("[" + control.Text + "]");
				sb.Append(" ");
			}

			sb.AppendLine("");

			if (!string.IsNullOrEmpty(taskDialog.Footer))
			{
				sb.AppendLine("");
				sb.AppendLine("[Footer]");
				sb.AppendLine(taskDialog.Footer);
			}

			if (!string.IsNullOrEmpty(taskDialog.ExpandedInformation) && taskDialog.ExpandFooterArea)
			{
				sb.AppendLine("");
				sb.AppendLine("[Expanded Information]");
				sb.AppendLine(taskDialog.ExpandedInformation);
			}

			return sb.ToString();
		}

		internal void EnableButton(int buttonId, bool enable)
		{
			Button btn;
			if (taskDialogButtons.TryGetValue(buttonId, out btn))
			{
				btn.Enabled = enable;
				btn.Invalidate();
			}
		}

		internal void EnableRadioButton(int buttonId, bool enable)
		{
			foreach (var c in flowLayoutPanelMainAreaControls.Controls)
			{
				var r = c as RadioButton;
				if (r != null && int.Equals(r.Tag, buttonId))
				{
					r.Enabled = enable;
					r.Invalidate();
				}
			}
		}

		internal void PerformButtonClick(int buttonId)
		{
			Button btn;
			if (taskDialogButtons.TryGetValue(buttonId, out btn))
				btn.PerformClick();
		}

		internal void PerformRadioButtonClick(int buttonId)
		{
			foreach (var c in flowLayoutPanelMainAreaControls.Controls)
			{
				var r = c as RadioButton;
				if (r != null && int.Equals(r.Tag, buttonId))
					r.PerformClick();
			}
		}

		internal void PerformVerificationClick(bool checkedState, bool setKeyboardFocusToCheckBox)
		{
			if (flowLayoutPanelSubAreaControls.Controls.ContainsKey("VerificationText"))
			{
				var cb = (CheckBox)flowLayoutPanelSubAreaControls.Controls["VerificationText"];
				cb.Checked = checkedState;
				if (setKeyboardFocusToCheckBox)
					cb.Focus();
			}
		}

		internal void SetButtonElevationRequiredState(int buttonId, bool required)
		{
			Button btn;
			if (taskDialogButtons.TryGetValue(buttonId, out btn))
			{
				var link = btn as CommandLink;
				if (link != null)
				{
					link.ShowShield = required;
				}
				else if (Environment.OSVersion.Version.Major >= 6)
				{
					btn.FlatStyle = required ? FlatStyle.System : FlatStyle.Standard;
					SendMessage(btn.Handle, (int)ButtonMessage.BCM_SETSHIELD, 0, required ? 1 : 0);
				}
				btn.Invalidate();
			}
		}

		internal void SetCommonIcon(TaskDialogIconElement elem, TaskDialogIcon icon)
		{
			switch (elem)
			{
				case TaskDialogIconElement.TDIE_ICON_MAIN:
					SetMainIcon(icon);
					break;

				case TaskDialogIconElement.TDIE_ICON_FOOTER:
					SetFooterIcon(icon);
					break;

				default:
					return;
			}
			Invalidate();
		}

		internal void SetElementText(TaskDialogElement elem, string text)
		{
			switch (elem)
			{
				case TaskDialogElement.TDE_CONTENT:
					if (flowLayoutPanelMainAreaControls.Controls.ContainsKey("Content"))
						flowLayoutPanelMainAreaControls.Controls["Content"].Text = text;
					break;

				case TaskDialogElement.TDE_EXPANDED_INFORMATION:
					if (flowLayoutPanelMainAreaControls.Controls.ContainsKey("ExpandedInformation"))
						flowLayoutPanelMainAreaControls.Controls["ExpandedInformation"].Text = text;
					if (flowLayoutPanelFooterExpandedInformationText.Controls.ContainsKey("ExpandedInformation"))
						flowLayoutPanelFooterExpandedInformationText.Controls["ExpandedInformation"].Text = text;
					break;

				case TaskDialogElement.TDE_FOOTER:
					if (flowLayoutPanelFooterAreaText.Controls.ContainsKey("Footer"))
						flowLayoutPanelFooterAreaText.Controls["Footer"].Text = text;
					break;

				case TaskDialogElement.TDE_MAIN_INSTRUCTION:
					if (flowLayoutPanelMainAreaControls.Controls.ContainsKey("MainInstruction"))
						flowLayoutPanelMainAreaControls.Controls["MainInstruction"].Text = text;
					break;

				default:
					return;
			}
			Invalidate();
		}

		internal void SetIcon(TaskDialogIconElement elem, Icon icon)
		{
			switch (elem)
			{
				case TaskDialogIconElement.TDIE_ICON_MAIN:
					SetMainIcon(icon);
					break;

				case TaskDialogIconElement.TDIE_ICON_FOOTER:
					SetFooterIcon(icon);
					break;

				default:
					return;
			}
			Invalidate();
		}

		internal void SetMarqueeProgressBar(bool v)
		{
			if (progressBar != null)
			{
				progressBar.Style = v ? ProgressBarStyle.Marquee : ProgressBarStyle.Continuous;
				progressBar.Invalidate();
			}
		}

		internal void SetProgressBarRange(int min, int max)
		{
			if (progressBar != null)
			{
				progressBar.Minimum = min;
				progressBar.Maximum = max;
				progressBar.Invalidate();
			}
		}

		internal void SetProgressBarState(ProgressBarState value)
		{
			if (progressBar != null && Environment.OSVersion.Version.Major >= 6)
			{
				const uint PBM_SETSTATE = 1040;
				SendMessage(progressBar.Handle, PBM_SETSTATE, (int)value, 0);
				progressBar.Invalidate();
			}
		}

		internal void SetProgressBarValue(int value)
		{
			if (progressBar == null) return;
			progressBar.Value = value;
			progressBar.Invalidate();
		}

		/// <summary>
		/// Clean up any resources being used.
		/// </summary>
		/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
		protected override void Dispose(bool disposing)
		{
			if (disposing)
				components?.Dispose();
			base.Dispose(disposing);
		}

		protected override void OnClosed(EventArgs e)
		{
			base.OnClosed(e);
			taskDialog.OnClosed();
		}

		protected override void OnKeyDown(KeyEventArgs e)
		{
			if (taskDialog.AllowDialogCancellation && e.KeyCode == Keys.Escape)
			{
				CloseForm();
				e.Handled = true;
			}
			base.OnKeyDown(e);
		}

		protected override void OnLoad(EventArgs e)
		{
			base.OnLoad(e);

			taskDialog.InitializeButtonState();
			taskDialog.ProgressBar.Initialize();
			taskDialog.OnLoad();
		}

		protected override void WndProc(ref Message m)
		{
			if (Enum.IsDefined(typeof(TaskDialogMessage), (uint)m.Msg))
			{
				switch ((TaskDialogMessage)m.Msg)
				{
					case TaskDialogMessage.TDM_CLICK_BUTTON:
						PerformButtonClick(m.WParam.ToInt32());
						break;

					case TaskDialogMessage.TDM_SET_MARQUEE_PROGRESS_BAR:
						SetMarqueeProgressBar(m.WParam.ToInt32() == 1);
						break;

					case TaskDialogMessage.TDM_SET_PROGRESS_BAR_STATE:
						SetProgressBarState((ProgressBarState)m.WParam.ToInt32());
						break;

					case TaskDialogMessage.TDM_SET_PROGRESS_BAR_RANGE:
						var ab = unchecked(IntPtr.Size == 8 ? (uint)m.LParam.ToInt64() : (uint)m.LParam.ToInt32());
						int a = unchecked((short)ab);
						int b = unchecked((short)(ab >> 16));
						SetProgressBarRange(a, b);
						break;

					case TaskDialogMessage.TDM_SET_PROGRESS_BAR_POS:
						SetProgressBarValue(m.WParam.ToInt32());
						break;

					case TaskDialogMessage.TDM_SET_PROGRESS_BAR_MARQUEE:
						SetProgressBarValue(m.LParam.ToInt32());
						break;

					case TaskDialogMessage.TDM_SET_ELEMENT_TEXT:
					case TaskDialogMessage.TDM_UPDATE_ELEMENT_TEXT:
						SetElementText((TaskDialogElement)m.WParam.ToInt32(), Marshal.PtrToStringAuto(m.LParam));
						break;

					case TaskDialogMessage.TDM_CLICK_RADIO_BUTTON:
						PerformRadioButtonClick(m.WParam.ToInt32());
						break;

					case TaskDialogMessage.TDM_ENABLE_BUTTON:
						EnableButton(m.WParam.ToInt32(), m.LParam.ToInt32() == 1);
						break;

					case TaskDialogMessage.TDM_ENABLE_RADIO_BUTTON:
						EnableRadioButton(m.WParam.ToInt32(), m.LParam.ToInt32() == 1);
						break;

					case TaskDialogMessage.TDM_CLICK_VERIFICATION:
						PerformVerificationClick(m.WParam.ToInt32() == 1, m.LParam.ToInt32() == 1);
						break;

					case TaskDialogMessage.TDM_SET_BUTTON_ELEVATION_REQUIRED_STATE:
						SetButtonElevationRequiredState(m.WParam.ToInt32(), m.LParam.ToInt32() == 1);
						break;

					case TaskDialogMessage.TDM_UPDATE_ICON:
						if (Enum.IsDefined(typeof(TaskDialogIcon), m.LParam.ToInt32()))
							SetCommonIcon((TaskDialogIconElement)m.WParam.ToInt32(), (TaskDialogIcon)m.LParam.ToInt32());
						else
							SetIcon((TaskDialogIconElement)m.WParam.ToInt32(), Icon.FromHandle(m.LParam));
						break;
				}
			}

			base.WndProc(ref m);
		}

		/// <summary>
		/// Creates a standard button with a common event handler to close the
		/// the form and return the button's Id.
		/// </summary>
		/// <param name="tag">The button's Id.</param>
		/// <param name="text">The button's name.</param>
		/// <returns></returns>
		private Button BuildControlButton(int tag, string text)
		{
			var button = new Button { Tag = tag, Text = text, Margin = new Padding(7, 0, 0, 7) };
			button.Width -= 5;
			button.Click += button_Click;

			taskDialogButtons.Add(tag, button);

			return button;
		}

		/// <summary>
		/// Creates a standard link label and will convert any anchor links to
		/// a proper link.
		/// </summary>
		/// <param name="margin">The margin of the link label.</param>
		/// <param name="text">The text of the link label.</param>
		/// <param name="name">The name assigned to the label.</param>
		/// <returns>A <see cref="LinkLabel"/> matching the parameters.</returns>
		private LinkLabel BuildControlLinkLabel(Padding margin, string text, string name)
		{
			var linkLabel = new LinkLabel
			{
				AutoSize = true,
				Font = new Font(Font.FontFamily, 9),
				LinkArea = new LinkArea(),
				Margin = margin,
				Name = name,
				Text = text
			};
			linkLabel.LinkClicked += linkLabel_LinkClicked;

			if (taskDialog.EnableHyperlinks)
			{
				linkLabel = ParseLinkLabel(linkLabel);
			}

			return linkLabel;
		}

		/// <summary>
		/// Main entry method to build the form.
		/// </summary>
		private void BuildForm()
		{
			ControlBox = taskDialog.AllowDialogCancellation;
			if ((MinimizeBox = taskDialog.CanBeMinimized) == true)
			{
				// If can be minimized, ControlBox must be enabled.
				ControlBox = true;
				ShowInTaskbar = true;

				// TODO: Form icon will be the TaskDialog.MainIcon. Problem is .Net framework does not provide a way to generate an icon.
			}
			StartPosition = taskDialog.PositionRelativeToWindow ? FormStartPosition.CenterParent : FormStartPosition.CenterScreen;
			Text = string.IsNullOrEmpty(taskDialog.WindowTitle) ? "" : taskDialog.WindowTitle;

			BuildFormMainArea();
			BuildFormFooterArea();
			// Build the sub area last because of the possibility of having the expanded information
			// in the footer. Then building the "expand/collapse" button will be accurate.
			BuildFormSubArea();

			SetFormHeight();
		}

		private void BuildFormFooterArea()
		{
			if (!string.IsNullOrEmpty(taskDialog.Footer))
			{
				if (taskDialog.CustomFooterIcon != null)
					SetFooterIcon(taskDialog.CustomFooterIcon);
				else
					SetFooterIcon(taskDialog.FooterIcon);

				flowLayoutPanelFooterAreaText.Controls.Add(BuildControlLinkLabel(new Padding(0, 0, 0, 11), taskDialog.Footer, "Footer"));
			}
			else
			{
				tableLayoutPanelFooterArea.AutoSize = false;
				tableLayoutPanelFooterArea.Height = 0;
				tableLayoutPanelFooterArea.Visible = false;

				tableLayoutPanel2.Height = 0;
				tableLayoutPanel2.Visible = false;
			}

			if (taskDialog.ExpandFooterArea && !string.IsNullOrEmpty(taskDialog.ExpandedInformation))
			{
				linkLabelExpandedInformation = BuildControlLinkLabel(new Padding(0), taskDialog.ExpandedInformation, "ExpandedInformation");

				flowLayoutPanelFooterExpandedInformationText.Controls.Add(linkLabelExpandedInformation);

				if (!taskDialog.ExpandedByDefault)
				{
					ToggleExpandedInformationState();
				}
			}
			else
			{
				flowLayoutPanelFooterExpandedInformationText.AutoSize = false;
				flowLayoutPanelFooterExpandedInformationText.Height = 0;
				tableLayoutPanelFooterExpandedInformationArea.Visible = false;

				tableLayoutPanel3.Height = tableLayoutPanel4.Height = 0;
				tableLayoutPanel3.Visible = tableLayoutPanel4.Visible = false;
			}
		}

		private void BuildFormMainArea()
		{
			var colorBg = Color.White;
			var colorFg = Color.FromArgb(0, 0, 51, 153);

			// Main icon
			if (taskDialog.CustomMainIcon != null)
				SetMainIcon(taskDialog.CustomMainIcon);
			else
			{
				SetMainIcon(taskDialog.MainIcon);
				switch (taskDialog.MainIcon)
				{
					case TaskDialogIcon.SecurityError:
						colorFg = Color.White;
						colorBg = Color.FromArgb(199, 1, 0);
						break;
					case TaskDialogIcon.SecurityWarning:
						colorFg = Color.Black;
						colorBg = Color.FromArgb(248, 191, 35);
						break;
					case TaskDialogIcon.SecuritySuccess:
						colorFg = Color.White;
						colorBg = Color.FromArgb(38, 134, 41);
						break;
					case TaskDialogIcon.ShieldBlue:
						colorFg = Color.White;
						colorBg = Color.FromArgb(16, 100, 131);
						break;
					case TaskDialogIcon.ShieldGray:
						colorFg = Color.White;
						colorBg = Color.FromArgb(160, 147, 138);
						break;
				}
			}

			// Main instruction
			if (!string.IsNullOrEmpty(taskDialog.MainInstruction))
			{
				var label = new Label
				{
					AutoSize = true,
					Name = "MainInstruction",
					Text = taskDialog.MainInstruction,
					Font = new Font(Font.FontFamily, 12),
					ForeColor = colorFg,
					Margin = new Padding(0, 0, 0, hPadding)
				};

				flowLayoutPanelMainAreaControls.Controls.Add(label);
				tableLayoutPanelMainArea.BackColor = colorBg;
			}

			// Content
			if (!string.IsNullOrEmpty(taskDialog.Content))
			{
				flowLayoutPanelMainAreaControls.Controls.Add(BuildControlLinkLabel(new Padding(0, 0, 0, hPadding), taskDialog.Content, "Content"));
			}

			// Expanded info
			if (!string.IsNullOrEmpty(taskDialog.ExpandedInformation) && !taskDialog.ExpandFooterArea)
			{
				linkLabelExpandedInformation = BuildControlLinkLabel(new Padding(0, 0, 0, hPadding), taskDialog.ExpandedInformation, "ExpandedInformation");

				flowLayoutPanelMainAreaControls.Controls.Add(linkLabelExpandedInformation);

				if (!taskDialog.ExpandedByDefault)
				{
					ToggleExpandedInformationState();
				}
			}

			// ProgressBar
			if (taskDialog.ShowProgressBar || taskDialog.ShowMarqueeProgressBar)
			{
				progressBar = new ProgressBar
				{
					Margin = new Padding(2, 0, 0, hPadding),
					Size = new Size(flowLayoutPanelMainAreaControls.Width - 6, 15)
				};
				if (taskDialog.ShowMarqueeProgressBar)
					progressBar.Style = ProgressBarStyle.Marquee;

				flowLayoutPanelMainAreaControls.Controls.Add(progressBar);
			}

			// Radio buttons
			for (var i = 0; i < taskDialog.RadioButtons.Count; i++)
			{
				var radioButton = new RadioButton
				{
					Text = taskDialog.RadioButtons[i].ButtonText,
					Tag = taskDialog.RadioButtons[i].ButtonId,
					AutoSize = true,
					Margin = new Padding(14, 0, 0, i == (taskDialog.RadioButtons.Count - 1) ? hPadding : 3)
				};
				radioButton.Click += radioButton_Click;
				// Select the first radio button by default.
				if ((i == 0) || (taskDialog.DefaultRadioButton == taskDialog.RadioButtons[i].ButtonId))
					defaultRadioButton = radioButton;

				flowLayoutPanelMainAreaControls.Controls.Add(radioButton);
			}

			// Command links
			if (taskDialog.UseCommandLinks)
			{
				foreach (var t in taskDialog.Buttons)
				{
					var cmdLink = new CommandLink
					{
						Tag = t.ButtonId,
						Text = t.ButtonText,
						Margin = new Padding(1, 0, 0, 1)
					};
					cmdLink.Size = cmdLink.GetPreferredSize(new Size(flowLayoutPanelMainAreaControls.Width - 10, 0));
					cmdLink.Click += button_Click;

					taskDialogButtons.Add(t.ButtonId, cmdLink);

					flowLayoutPanelMainAreaControls.Controls.Add(cmdLink);
				}
			}
		}

		private void BuildFormSubArea()
		{
			// --------------- Left side of the sub control area. ---------------
			// Check if there is any expanded information first.
			if (!string.IsNullOrEmpty(taskDialog.ExpandedInformation))
			{
				expandedInfoButton = new EmulateExpInfoButton(isExpanded, taskDialog.ExpandedControlText,
					taskDialog.CollapsedControlText) {Margin = Padding.Empty};
				expandedInfoButton.Click += expandedInfoButton_Click;

				flowLayoutPanelSubAreaControls.Controls.Add(expandedInfoButton);
				// Smallest element in this area.
				tableLayoutPanelSubArea.MinimumSize = new Size(0, 33);
			}

			if (!string.IsNullOrEmpty(taskDialog.VerificationText))
			{
				var checkBox = new CheckBox
				{
					Checked = taskDialog.VerificationFlagChecked,
					Margin = new Padding(3, 3, 0, 0),
					MaximumSize = new Size(165, 0),
					Name = "VerificationText",
					Text = taskDialog.VerificationText,
					Width = 165
				};

				checkBox.Click += verificationCheckBox_Click;

				// AdjustControls doesn't take in account the checkbox, so need
				// to work around it.
				var size = AdjustControls.GetBestSize(checkBox, checkBox.Text, new Rectangle(0, 0, checkBox.Width - 52, checkBox.Height));
				checkBox.Height = size.Height + 6;

				flowLayoutPanelSubAreaControls.Controls.Add(checkBox);
			}

			// --------------- Right side of the sub control area. ---------------
			var commBtns = taskDialog.CommonButtons;

			// If there is no button, by default there will be an "Okay" button.
			if (commBtns == TaskDialogCommonButtons.None && taskDialog.Buttons.Count == 0)
				commBtns = TaskDialogCommonButtons.Ok;

			var requiredTotalButtonsWidth = 0;

			var commonButtons = (TaskDialogCommonButtons[])Enum.GetValues(typeof(TaskDialogCommonButtons));

			// Iterate through all the common buttons. Get by reverse order
			// as the layout direction is right to left.
			for (var i = commonButtons.Length - 1; i >= 0; i--)
			{
				// There is no "None" button.
				if (!commonButtons[i].Equals(TaskDialogCommonButtons.None))
				{
					// Now to check which button is needed.
					if ((commBtns & commonButtons[i]).Equals(commonButtons[i]))
					{
						// TaskDialogCommonButtons enums is not the same as DialogResult enums. Need
						// to use another way of getting the correct DialogResult value.
						Button button;

						// DialogResult does not contain a "Close" enum, return value by TaskDialog is int 8.
						if (commonButtons[i].Equals(TaskDialogCommonButtons.Close))
						{
							button = BuildControlButton(8, TaskDialogCommonButtons.Close.ToString());
						}
						// No "Ok" button. Is it a typo for TaskDialogCommonButtons in TaskDialog.cs?
						else if (commonButtons[i].Equals(TaskDialogCommonButtons.Ok))
						{
							button = BuildControlButton((int)DialogResult.OK, DialogResult.OK.ToString());
						}
						else
						{
							var result = (DialogResult)Enum.Parse(typeof(DialogResult), commonButtons[i].ToString());
							button = BuildControlButton((int)result, result.ToString());
						}

						button.TabIndex = i + taskDialog.Buttons.Count;

						flowLayoutPanelSubAreaButtons.Controls.Add(button);

						requiredTotalButtonsWidth += button.Width + button.Margin.Left + button.Margin.Right;
					}
				}
			}

			// Custom buttons. Get by reverse order as the layout direction
			// is right to left.
			if (!taskDialog.UseCommandLinks)
			{
				for (var i = taskDialog.Buttons.Count - 1; i >= 0; i--)
				{
					var button = BuildControlButton(taskDialog.Buttons[i].ButtonId, taskDialog.Buttons[i].ButtonText);
					flowLayoutPanelSubAreaButtons.Controls.Add(button);

					button.TabIndex = i;

					requiredTotalButtonsWidth += button.Width + button.Margin.Left + button.Margin.Right;
				}
			}

			// Check if there is anything in the sub area.
			if ((requiredTotalButtonsWidth == 0) && (string.IsNullOrEmpty(taskDialog.ExpandedInformation)) && (string.IsNullOrEmpty(taskDialog.VerificationText)))
			{
				tableLayoutPanel1.Visible = false;
				tableLayoutPanel1.Height = 0;
				tableLayoutPanelSubArea.Visible = false;
			}
			else
			{
				// Padding on the right.
				requiredTotalButtonsWidth += 15;

				if (requiredTotalButtonsWidth > flowLayoutPanelSubAreaButtons.Width)
				{
					Width += requiredTotalButtonsWidth - flowLayoutPanelSubAreaButtons.Width;
				}
			}
		}

		private void button_Click(object sender, EventArgs e)
		{
			var id = (int)((Button)sender).Tag;
			if (taskDialog.OnButtonClicked(id))
				return;

			TaskDialogResult = id;
			CloseForm();
		}

		/// <summary>
		/// Closes the form and populate the necessary return results.
		/// </summary>
		private void CloseForm()
		{
			if (taskDialog.RadioButtons.Count > 0)
			{
				// Check all the radio buttons.
				foreach (Control control in flowLayoutPanelMainAreaControls.Controls)
				{
					var button = control as RadioButton;
					if (button == null) continue;
					var radioButton = button;
					if (!radioButton.Checked) continue;
					TaskDialogRadioButtonResult = (int)radioButton.Tag;
					break;
				}
			}

			if (!string.IsNullOrEmpty(taskDialog.VerificationText))
			{
				foreach (Control control in flowLayoutPanelSubAreaControls.Controls)
				{
					var box = control as CheckBox;
					if (box == null) continue;
					TaskDialogVerificationFlagChecked = box.Checked;
					break;
				}
			}

			timer.Stop();

			Close();
		}

		/// <summary>
		/// Detect user pressing "ctrl + c".
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void EmulateTaskDialog_KeyDown(object sender, KeyEventArgs e)
		{
			var notWanted = Keys.Alt | Keys.Shift;

			if ((e.Modifiers & notWanted) == 0)
			{
				if ((e.Modifiers & Keys.Control) == Keys.Control)
				{
					if (e.KeyCode.Equals(Keys.C))
					{
						Clipboard.SetText(ToString());
					}
				}
			}
		}

		private void expandedInfoButton_Click(object sender, EventArgs e)
		{
			ToggleExpandedInformationState();

			expandedInfoButton.ToogleState();

			SetFormHeight();

			taskDialog.OnExpanded(isExpanded);
		}

		/// <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();
			tableLayoutPanelMainArea = new TableLayoutPanel();
			flowLayoutPanelMainAreaControls = new FlowLayoutPanel();
			pictureBoxMainAreaIcon = new PictureBox();
			tableLayoutPanel1 = new Panel();
			tableLayoutPanelSubArea = new TableLayoutPanel();
			flowLayoutPanelSubAreaButtons = new FlowLayoutPanel();
			flowLayoutPanelSubAreaControls = new FlowLayoutPanel();
			tableLayoutPanelFooterArea = new TableLayoutPanel();
			pictureBoxFooterAreaIcon = new PictureBox();
			flowLayoutPanelFooterAreaText = new FlowLayoutPanel();
			tableLayoutPanel2 = new Panel();
			tableLayoutPanel3 = new Panel();
			tableLayoutPanel4 = new Panel();
			tableLayoutPanelFooterExpandedInformationArea = new TableLayoutPanel();
			flowLayoutPanelFooterExpandedInformationText = new FlowLayoutPanel();
			timer = new Timer(components);
			tableLayoutPanelMainArea.SuspendLayout();
			((System.ComponentModel.ISupportInitialize)(pictureBoxMainAreaIcon)).BeginInit();
			tableLayoutPanelSubArea.SuspendLayout();
			tableLayoutPanelFooterArea.SuspendLayout();
			((System.ComponentModel.ISupportInitialize)(pictureBoxFooterAreaIcon)).BeginInit();
			tableLayoutPanelFooterExpandedInformationArea.SuspendLayout();
			SuspendLayout();
			//
			// tableLayoutPanelMainArea
			//
			tableLayoutPanelMainArea.AutoSize = true;
			tableLayoutPanelMainArea.BackColor = Color.White;
			tableLayoutPanelMainArea.ColumnCount = 2;
			tableLayoutPanelMainArea.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 32F));
			tableLayoutPanelMainArea.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100F));
			tableLayoutPanelMainArea.Controls.Add(flowLayoutPanelMainAreaControls, 1, 0);
			tableLayoutPanelMainArea.Controls.Add(pictureBoxMainAreaIcon, 0, 0);
			tableLayoutPanelMainArea.Dock = DockStyle.Top;
			tableLayoutPanelMainArea.Location = new Point(0, 0);
			tableLayoutPanelMainArea.Margin = new Padding(0);
			tableLayoutPanelMainArea.MinimumSize = new Size(0, 52);
			tableLayoutPanelMainArea.Name = "tableLayoutPanelMainArea";
			tableLayoutPanelMainArea.Padding = new Padding(10);
			tableLayoutPanelMainArea.RowCount = 1;
			tableLayoutPanelMainArea.RowStyles.Add(new RowStyle(SizeType.Percent, 100F));
			tableLayoutPanelMainArea.Size = new Size(460, 52);
			tableLayoutPanelMainArea.TabIndex = 0;
			//
			// flowLayoutPanelMainAreaControls
			//
			flowLayoutPanelMainAreaControls.AutoSize = true;
			flowLayoutPanelMainAreaControls.AutoSizeMode = AutoSizeMode.GrowAndShrink;
			flowLayoutPanelMainAreaControls.Dock = DockStyle.Fill;
			flowLayoutPanelMainAreaControls.FlowDirection = FlowDirection.TopDown;
			flowLayoutPanelMainAreaControls.Location = new Point(42, 10);
			flowLayoutPanelMainAreaControls.Margin = new Padding(0);
			flowLayoutPanelMainAreaControls.Name = "flowLayoutPanelMainAreaControls";
			flowLayoutPanelMainAreaControls.Size = new Size(408, 32);
			flowLayoutPanelMainAreaControls.TabIndex = 0;
			//
			// pictureBoxMainAreaIcon
			//
			pictureBoxMainAreaIcon.Location = new Point(10, 10);
			pictureBoxMainAreaIcon.Margin = new Padding(0);
			pictureBoxMainAreaIcon.Name = "pictureBoxMainAreaIcon";
			pictureBoxMainAreaIcon.Size = new Size(32, 32);
			pictureBoxMainAreaIcon.TabIndex = 1;
			pictureBoxMainAreaIcon.TabStop = false;
			//
			// tableLayoutPanel1
			//
			tableLayoutPanel1.BackColor = SystemColors.ControlLight;
			tableLayoutPanel1.Dock = DockStyle.Top;
			tableLayoutPanel1.Location = new Point(0, 52);
			tableLayoutPanel1.Margin = new Padding(0);
			tableLayoutPanel1.Name = "tableLayoutPanel1";
			tableLayoutPanel1.Size = new Size(460, 1);
			tableLayoutPanel1.TabIndex = 1;
			//
			// tableLayoutPanelSubArea
			//
			tableLayoutPanelSubArea.AutoSize = true;
			tableLayoutPanelSubArea.AutoSizeMode = AutoSizeMode.GrowAndShrink;
			tableLayoutPanelSubArea.BackColor = SystemColors.ButtonFace;
			tableLayoutPanelSubArea.ColumnCount = 2;
			tableLayoutPanelSubArea.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 175F));
			tableLayoutPanelSubArea.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100F));
			tableLayoutPanelSubArea.Controls.Add(flowLayoutPanelSubAreaButtons, 1, 0);
			tableLayoutPanelSubArea.Controls.Add(flowLayoutPanelSubAreaControls, 0, 0);
			tableLayoutPanelSubArea.Dock = DockStyle.Top;
			tableLayoutPanelSubArea.Location = new Point(0, 53);
			tableLayoutPanelSubArea.Margin = new Padding(0);
			tableLayoutPanelSubArea.MinimumSize = new Size(0, 33);
			tableLayoutPanelSubArea.Name = "tableLayoutPanelSubArea";
			tableLayoutPanelSubArea.Padding = new Padding(10, 9, 10, 9);
			tableLayoutPanelSubArea.RowCount = 1;
			tableLayoutPanelSubArea.RowStyles.Add(new RowStyle(SizeType.Percent, 100F));
			tableLayoutPanelSubArea.Size = new Size(460, 33);
			tableLayoutPanelSubArea.TabIndex = 1;
			//
			// flowLayoutPanelSubAreaButtons
			//
			flowLayoutPanelSubAreaButtons.AutoSize = true;
			flowLayoutPanelSubAreaButtons.AutoSizeMode = AutoSizeMode.GrowAndShrink;
			flowLayoutPanelSubAreaButtons.Dock = DockStyle.Fill;
			flowLayoutPanelSubAreaButtons.FlowDirection = FlowDirection.RightToLeft;
			flowLayoutPanelSubAreaButtons.Location = new Point(185, 9);
			flowLayoutPanelSubAreaButtons.Margin = new Padding(0);
			flowLayoutPanelSubAreaButtons.Name = "flowLayoutPanelSubAreaButtons";
			flowLayoutPanelSubAreaButtons.Size = new Size(265, 15);
			flowLayoutPanelSubAreaButtons.TabIndex = 1;
			flowLayoutPanelSubAreaButtons.WrapContents = false;
			//
			// flowLayoutPanelSubAreaControls
			//
			flowLayoutPanelSubAreaControls.AutoSize = true;
			flowLayoutPanelSubAreaControls.AutoSizeMode = AutoSizeMode.GrowAndShrink;
			flowLayoutPanelSubAreaControls.Dock = DockStyle.Fill;
			flowLayoutPanelSubAreaControls.FlowDirection = FlowDirection.TopDown;
			flowLayoutPanelSubAreaControls.Location = new Point(10, 9);
			flowLayoutPanelSubAreaControls.Margin = new Padding(0);
			flowLayoutPanelSubAreaControls.Name = "flowLayoutPanelSubAreaControls";
			flowLayoutPanelSubAreaControls.Size = new Size(175, 15);
			flowLayoutPanelSubAreaControls.TabIndex = 0;
			//
			// tableLayoutPanelFooterArea
			//
			tableLayoutPanelFooterArea.AutoSize = true;
			tableLayoutPanelFooterArea.BackColor = SystemColors.Control;
			tableLayoutPanelFooterArea.ColumnCount = 2;
			tableLayoutPanelFooterArea.ColumnStyles.Add(new ColumnStyle(SizeType.Absolute, 24F));
			tableLayoutPanelFooterArea.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100F));
			tableLayoutPanelFooterArea.Controls.Add(pictureBoxFooterAreaIcon, 0, 0);
			tableLayoutPanelFooterArea.Controls.Add(flowLayoutPanelFooterAreaText, 1, 0);
			tableLayoutPanelFooterArea.Dock = DockStyle.Top;
			tableLayoutPanelFooterArea.Location = new Point(0, 87);
			tableLayoutPanelFooterArea.Margin = new Padding(0);
			tableLayoutPanelFooterArea.Name = "tableLayoutPanelFooterArea";
			tableLayoutPanelFooterArea.Padding = new Padding(10, 10, 10, 0);
			tableLayoutPanelFooterArea.RowCount = 1;
			tableLayoutPanelFooterArea.RowStyles.Add(new RowStyle(SizeType.Percent, 100F));
			tableLayoutPanelFooterArea.Size = new Size(460, 26);
			tableLayoutPanelFooterArea.TabIndex = 2;
			//
			// pictureBoxFooterAreaIcon
			//
			pictureBoxFooterAreaIcon.Location = new Point(10, 10);
			pictureBoxFooterAreaIcon.Margin = new Padding(0);
			pictureBoxFooterAreaIcon.Name = "pictureBoxFooterAreaIcon";
			pictureBoxFooterAreaIcon.Size = new Size(16, 16);
			pictureBoxFooterAreaIcon.TabIndex = 0;
			pictureBoxFooterAreaIcon.TabStop = false;
			//
			// flowLayoutPanelFooterAreaText
			//
			flowLayoutPanelFooterAreaText.AutoSize = true;
			flowLayoutPanelFooterAreaText.Dock = DockStyle.Fill;
			flowLayoutPanelFooterAreaText.FlowDirection = FlowDirection.TopDown;
			flowLayoutPanelFooterAreaText.Location = new Point(34, 10);
			flowLayoutPanelFooterAreaText.Margin = new Padding(0);
			flowLayoutPanelFooterAreaText.Name = "flowLayoutPanelFooterAreaText";
			flowLayoutPanelFooterAreaText.Size = new Size(416, 16);
			flowLayoutPanelFooterAreaText.TabIndex = 1;
			//
			// tableLayoutPanel2
			//
			tableLayoutPanel2.BackColor = SystemColors.ControlLight;
			tableLayoutPanel2.Dock = DockStyle.Top;
			tableLayoutPanel2.Location = new Point(0, 86);
			tableLayoutPanel2.Margin = new Padding(0);
			tableLayoutPanel2.Name = "tableLayoutPanel2";
			tableLayoutPanel2.Size = new Size(460, 1);
			tableLayoutPanel2.TabIndex = 4;
			//
			// tableLayoutPanel3
			//
			tableLayoutPanel3.BackColor = SystemColors.ControlLight;
			tableLayoutPanel3.Dock = DockStyle.Top;
			tableLayoutPanel3.Location = new Point(0, 113);
			tableLayoutPanel3.Margin = new Padding(0);
			tableLayoutPanel3.Name = "tableLayoutPanel3";
			tableLayoutPanel3.Size = new Size(460, 1);
			tableLayoutPanel3.TabIndex = 7;
			//
			// tableLayoutPanel4
			//
			tableLayoutPanel4.BackColor = SystemColors.ControlLightLight;
			tableLayoutPanel4.Dock = DockStyle.Top;
			tableLayoutPanel4.Location = new Point(0, 114);
			tableLayoutPanel4.Margin = new Padding(0);
			tableLayoutPanel4.Name = "tableLayoutPanel4";
			tableLayoutPanel4.Size = new Size(460, 1);
			tableLayoutPanel4.TabIndex = 8;
			//
			// tableLayoutPanelFooterExpandedInformationArea
			//
			tableLayoutPanelFooterExpandedInformationArea.AutoSize = true;
			tableLayoutPanelFooterExpandedInformationArea.BackColor = SystemColors.ButtonFace;
			tableLayoutPanelFooterExpandedInformationArea.ColumnCount = 1;
			tableLayoutPanelFooterExpandedInformationArea.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100F));
			tableLayoutPanelFooterExpandedInformationArea.Controls.Add(flowLayoutPanelFooterExpandedInformationText, 0, 0);
			tableLayoutPanelFooterExpandedInformationArea.Dock = DockStyle.Top;
			tableLayoutPanelFooterExpandedInformationArea.Location = new Point(0, 115);
			tableLayoutPanelFooterExpandedInformationArea.Margin = new Padding(0);
			tableLayoutPanelFooterExpandedInformationArea.Name = "tableLayoutPanelFooterExpandedInformationArea";
			tableLayoutPanelFooterExpandedInformationArea.Padding = new Padding(10, 10, 10, 0);
			tableLayoutPanelFooterExpandedInformationArea.RowCount = 1;
			tableLayoutPanelFooterExpandedInformationArea.RowStyles.Add(new RowStyle(SizeType.Percent, 100F));
			tableLayoutPanelFooterExpandedInformationArea.Size = new Size(460, 10);
			tableLayoutPanelFooterExpandedInformationArea.TabIndex = 3;
			//
			// flowLayoutPanelFooterExpandedInformationText
			//
			flowLayoutPanelFooterExpandedInformationText.AutoSize = true;
			flowLayoutPanelFooterExpandedInformationText.Dock = DockStyle.Fill;
			flowLayoutPanelFooterExpandedInformationText.FlowDirection = FlowDirection.TopDown;
			flowLayoutPanelFooterExpandedInformationText.Location = new Point(10, 10);
			flowLayoutPanelFooterExpandedInformationText.Margin = new Padding(0);
			flowLayoutPanelFooterExpandedInformationText.Name = "flowLayoutPanelFooterExpandedInformationText";
			flowLayoutPanelFooterExpandedInformationText.Size = new Size(440, 1);
			flowLayoutPanelFooterExpandedInformationText.TabIndex = 0;
			//
			// timer
			//
			timer.Tick += timer_Tick;
			//
			// EmulateTaskDialog
			//
			AutoScaleDimensions = new SizeF(7F, 15F);
			AutoScaleMode = AutoScaleMode.Font;
			BackColor = SystemColors.Control;
			ClientSize = new Size(360, 200);
			Controls.Add(tableLayoutPanelFooterExpandedInformationArea);
			Controls.Add(tableLayoutPanel4);
			Controls.Add(tableLayoutPanel3);
			Controls.Add(tableLayoutPanelFooterArea);
			Controls.Add(tableLayoutPanel2);
			Controls.Add(tableLayoutPanelSubArea);
			Controls.Add(tableLayoutPanel1);
			Controls.Add(tableLayoutPanelMainArea);
			Font = new Font("Segoe UI", 9F, FontStyle.Regular, GraphicsUnit.Point, 0);
			FormBorderStyle = FormBorderStyle.FixedDialog;
			KeyPreview = true;
			MaximizeBox = false;
			Name = "EmulateTaskDialog";
			ShowInTaskbar = false;
			Text = @"EmulateTaskDialog";
			KeyDown += EmulateTaskDialog_KeyDown;
			tableLayoutPanelMainArea.ResumeLayout(false);
			tableLayoutPanelMainArea.PerformLayout();
			((System.ComponentModel.ISupportInitialize)(pictureBoxMainAreaIcon)).EndInit();
			tableLayoutPanelSubArea.ResumeLayout(false);
			tableLayoutPanelSubArea.PerformLayout();
			tableLayoutPanelFooterArea.ResumeLayout(false);
			tableLayoutPanelFooterArea.PerformLayout();
			((System.ComponentModel.ISupportInitialize)(pictureBoxFooterAreaIcon)).EndInit();
			tableLayoutPanelFooterExpandedInformationArea.ResumeLayout(false);
			tableLayoutPanelFooterExpandedInformationArea.PerformLayout();
			ResumeLayout(false);
			PerformLayout();
		}

		private void linkLabel_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
		{
			taskDialog.OnLinkClicked((string)e.Link.LinkData);
		}

		/// <summary>
		/// Parses a LinkLabel text and extract out all the anchor (a) tags.
		/// Then it will parse all the anchor tags and create the relevant
		/// links in the text.
		/// </summary>
		/// <param name="linkLabel"></param>
		/// <returns></returns>
		private LinkLabel ParseLinkLabel(LinkLabel linkLabel)
		{
			// Set up the text to parse.
			var text = linkLabel.Text;

			// Set up the regex for finding the link urls.
			var hrefPattern = new StringBuilder();
			// Start anchor tag and anything that comes before "href" tag.
			hrefPattern.Append("<a[^>]+");
			hrefPattern.Append("href\\s*=\\s*"); // Start href property.
												 // Three possibilities for "href":
												 // (1) enclosed in double quotes.
												 // (2) enclosed in single quotes.
												 // (3) enclosed in spaces.
			hrefPattern.Append("(?:\"(?<href>[^\"]*)\"|'(?<href>[^']*)'|(?<href>[^\"'>\\s]+))");
			// Grab the inner html too.
			hrefPattern.Append("[^>]*>(?<a>.*?)</a>"); // End of anchor tag.
			var hrefRegex = new Regex(hrefPattern.ToString(), RegexOptions.IgnoreCase);

			// Look for matches.
			var hrefCheck = hrefRegex.Match(text);

			while (hrefCheck.Success)
			{
				var href = hrefCheck.Groups["href"].Value;
				var innerText = hrefCheck.Groups["a"].Value;

				// Get the starting index of the anchor tag.
				var index = linkLabel.Text.IndexOf(hrefCheck.Value, StringComparison.Ordinal);
				// Replace it with the inner text, create a link and store the
				// link.
				linkLabel.Text = linkLabel.Text.Replace(hrefCheck.Value, innerText);
				linkLabel.Links.Add(index, innerText.Length, href);

				hrefCheck = hrefCheck.NextMatch();
			}

			return linkLabel;
		}

		private void radioButton_Click(object sender, EventArgs e)
		{
			var id = (int)((RadioButton)sender).Tag;
			taskDialog.OnRadioButtonClicked(id);
		}

		private void Reset()
		{
			BuildForm();

			// Setup the default settings.
			if (defaultRadioButton != null)
			{
				defaultRadioButton.Checked = true;
			}

			// Only can set focus after everything has been build.
			if (taskDialog.DefaultButton != 0)
			{
				// Set the default button.
				if (taskDialogButtons.TryGetValue(taskDialog.DefaultButton, out defaultButton))
				{
					defaultButton.Select();
				}
				else
				{
					if (flowLayoutPanelSubAreaButtons.Controls.Count > 0)
					{
						// Select left-most button.
						flowLayoutPanelSubAreaButtons.Controls[flowLayoutPanelSubAreaButtons.Controls.Count - 1].Select();
					}
				}
			}
			else
			{
				if (flowLayoutPanelSubAreaButtons.Controls.Count > 0)
				{
					// Set the left-most button to be the default. Dictionary does
					// not keep the order of the button that is added in, so this
					// work around is used.
					//
					// Layout right to left, left-most item is at the end of the list.
					flowLayoutPanelSubAreaButtons.Controls[flowLayoutPanelSubAreaButtons.Controls.Count - 1].Select();
				}
			}

			timer.Start();
		}

		private void SetFooterIcon(TaskDialogIcon icon)
		{
			SetFooterIcon(TaskDialog.IconFromTaskDialogIcon(icon));
		}

		private void SetFooterIcon(Icon icon)
		{
			pictureBoxFooterAreaIcon.Image = TaskDialog.GetSmallImage(icon);
			tableLayoutPanelFooterArea.ColumnStyles[0].Width = pictureBoxFooterAreaIcon.Image?.Width + 6 ?? 0;
		}

		private void SetFormHeight()
		{
			Height = (tableLayoutPanelMainArea.Height + 32)
				+ tableLayoutPanel1.Height // Separator.
										   // When empty, default height is 33. TableLayoutPanel refuses to
										   // shrink to 0 as there are items in it.
				+ ((tableLayoutPanelSubArea.Height > 33) ? tableLayoutPanelSubArea.Height : 0)
				+ tableLayoutPanel2.Height // Separator.
				+ ((tableLayoutPanelFooterArea.Height > 0) ? tableLayoutPanelFooterArea.Height : 0)
				+ tableLayoutPanel3.Height // Separator.
				+ tableLayoutPanel4.Height // Separator.
										   // When empty, default height is 6. TableLayoutPanel refuses to
										   // shrink to 0 as there are items in it.
				+ ((tableLayoutPanelFooterExpandedInformationArea.Height > hPadding) ? tableLayoutPanelFooterExpandedInformationArea.Height : 0);
		}

		private void SetMainIcon(TaskDialogIcon icon)
		{
			SetMainIcon(TaskDialog.IconFromTaskDialogIcon(icon));
		}

		private void SetMainIcon(Icon icon)
		{
			pictureBoxMainAreaIcon.Image = icon?.ToBitmap();
			tableLayoutPanelMainArea.ColumnStyles[0].Width = pictureBoxMainAreaIcon.Image?.Width + 10 ?? 0;
		}

		private void timer_Tick(object sender, EventArgs e)
		{
			timerTickCount += (uint)timer.Interval;

			if (taskDialog.OnTimer((int)timerTickCount))
				timerTickCount = 0;
		}

		private void ToggleExpandedInformationState()
		{
			if (taskDialog.ExpandFooterArea)
			{
				// Shrink/expand our upper "border".
				if (isExpanded)
				{
					tableLayoutPanel3.Visible = tableLayoutPanel4.Visible = false;
					Height -= tableLayoutPanel3.Height * 2;
				}
				else
				{
					tableLayoutPanel3.Visible = tableLayoutPanel4.Visible = true;
					Height += tableLayoutPanel3.Height * 2;
				}
			}

			if (isExpanded)
			{
				// Shrink the form first.
				Height -= linkLabelExpandedInformation.Height;
				linkLabelExpandedInformation.Visible = false;
				isExpanded = false;
			}
			else
			{
				linkLabelExpandedInformation.Visible = true;
				// Expand the form.
				Height += linkLabelExpandedInformation.Height;
				isExpanded = true;
			}
		}

		private void verificationCheckBox_Click(object sender, EventArgs e)
		{
			taskDialog.OnVerificationClicked(((CheckBox)sender).Checked);
		}

		private static class AdjustControls
		{
			/// <summary>
			/// Measure a multiline string
			/// </summary>
			/// <param name="gr">Graphics</param>
			/// <param name="text">string to measure</param>
			/// <param name="rect">Original rect. The width will be taken as fixed.</param>
			/// <param name="textboxControl">True if you want to measure the string for a textbox control</param>
			/// <returns>A Size object with the measure of the string according with the params</returns>
			public static Size GetBestSize(Graphics gr, string text, Rectangle rect, bool textboxControl)
			{
				RECT bounds = rect;
				using (var dc = new SafeDCHandle(gr))
				{
					var flags = DrawTextFlags.DT_CALCRECT | DrawTextFlags.DT_WORDBREAK;
					if (textboxControl) flags |= DrawTextFlags.DT_EDITCONTROL;
					DrawText(dc, text, text.Length, ref bounds, flags);
				}
				return new Size(bounds.Right - bounds.Left, bounds.Bottom - bounds.Top + (textboxControl ? 6 : 0));
			}

			/// <summary>
			/// Measure a multiline string for a Control
			///
			/// http://www.mobilepractices.com/2008/01/making-multiline-measurestring-work.html
			/// </summary>
			/// <param name="control">control</param>
			/// <param name="text">string to measure</param>
			/// <param name="rect">Original rect. The width will be taken as fixed.</param>
			/// <returns>A Size object with the measure of the string according with the params</returns>
			public static Size GetBestSize(Control control, string text, Rectangle rect)
			{
				Size result;
				using (var gr = control.CreateGraphics())
					using (var hDC = new SafeDCHandle(gr))
						using (new SafeDCObjectHandle(hDC, control.Font.ToHfont()))
							result = GetBestSize(gr, text, rect, control is TextBox);
				return result;
			}
		}
	}
}
#endif