using System;
using System.ComponentModel;
using System.Drawing;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Security.Permissions;
using System.Windows.Forms;
using Vanara.PInvoke;

namespace Vanara.Windows.Shell
{
	/// <summary>Provides access to the functionality of the taskbar button.</summary>
	/// <seealso cref="System.ComponentModel.Component"/>
	[ProvideProperty("AppID", typeof(Form))]
	[ProvideProperty("TaskbarButtonTooltip", typeof(Form))]
	[ProvideProperty("TaskbarButtonOverlay", typeof(Form))]
	[ProvideProperty("TaskbarButtonOverlayTooltip", typeof(Form))]
	[ProvideProperty("TaskbarButtonProgressState", typeof(Form))]
	[ProvideProperty("TaskbarButtonProgressValue", typeof(Form))]
	[ProvideProperty("JumpList", typeof(Form))]
	[ProvideProperty("TaskbarButtonThumbnails", typeof(Form))]
	public class TaskbarButton : ExtenderProviderBase<Form>, INotifyPropertyChanged
	{
		private const string category = "Taskbar Button";

		static TaskbarButton() => Application.AddMessageFilter(new ThumbButtonMessageFilter());

		/// <summary>Initializes a new instance of the <see cref="TaskbarButton"/> class.</summary>
		public TaskbarButton()
		{
			TaskbarButtonCreated += OnTaskbarButtonCreated;
			ThumbnailButtonClick += OnThumbnailButtonClick;
		}

		/// <summary>Initializes a new instance of the <see cref="TaskbarButton"/> class.</summary>
		/// <param name="container">The container of this component.</param>
		/// <exception cref="System.ArgumentNullException">container</exception>
		public TaskbarButton(IContainer container) : this()
		{
			if (container is null)
				throw new ArgumentNullException(nameof(container));
			container.Add(this);
		}

		/// <summary>
		/// Occurs when the system reports a taskbar button has been created. The first parameter will contain the HWND of the window for
		/// which the button was created.
		/// </summary>
		public static event Action<HWND> TaskbarButtonCreated;

		/// <summary>
		/// Occurs when the system reports a thumbnail button has been clicked. The first parameter contains the HWND of the window shown by
		/// the thumbnail and the second contains the Command ID of the button that was clicked.
		/// </summary>
		public static event Action<HWND, int> ThumbnailButtonClick;

		/// <summary>Occurs when a property has changed.</summary>
		public event PropertyChangedEventHandler PropertyChanged;

		/// <summary>Gets a value indicating whether the taskbar button has been created.</summary>
		/// <value><see langword="true"/> if the taskbar button was created; otherwise, <see langword="false"/>.</value>
		[Browsable(false)]
		public bool IsTaskbarButtonCreated { get; internal set; } = false;

		/// <summary>Signals the object that initialization is starting.</summary>
		public override void BeginInit()
		{
			base.BeginInit();
			if (Container is Form f && f.ShowInTaskbar)
				TaskbarList.ActivateTaskbarItem(f);
		}

		/// <summary>Gets the application identifier.</summary>
		/// <param name="form">The form.</param>
		/// <returns>The application identifier.</returns>
		[Category(category), DisplayName("AppID"), DefaultValue(null)]
		[Description("Gets or sets the application identifier.")]
		public string GetAppID(Form form) => GetPropertyValue<string>(form);

		/// <summary>Gets the jumplist to display on this taskbar button.</summary>
		/// <param name="form">The form.</param>
		/// <returns>The jumplist.</returns>
		[Category(category), DisplayName("JumpList"), Localizable(true)]
		[Description("Gets the jumplist to display with the taskbar button.")]
		public JumpList GetJumpList(Form form)
		{
			var ret = GetPropertyValue<JumpList>(form);
			if (ret is null)
			{
				ret = new JumpList();
				SetPropertyValue(form, ret, "JumpList");
			}
			return ret;
		}

		/// <summary>Gets the overlay icon to dispaly on a taskbar button to indicate application status or a notification to the user.</summary>
		/// <param name="form">The form.</param>
		/// <returns>The overlay icon.</returns>
		[Category(category), DisplayName("TaskbarButtonOverlay"), DefaultValue(null), Localizable(true)]
		[Description("Gets or sets the overlay icon to dispaly on a taskbar button.")]
		public Icon GetTaskbarButtonOverlay(Form form) => GetPropertyValue<Icon>(form);

		/// <summary>
		/// Gets the overlay tooltip to dispaly on a taskbar button to indicate application status or a notification to the user.
		/// </summary>
		/// <param name="form">The form.</param>
		/// <returns>The overlay tooltip.</returns>
		[Category(category), DisplayName("TaskbarButtonOverlayTooltip"), DefaultValue(null), Localizable(true)]
		[Description("Gets or sets the overlay tooltip to dispaly on a taskbar button.")]
		public string GetTaskbarButtonOverlayTooltip(Form form) => GetPropertyValue<string>(form);

		/// <summary>Gets the state of the progress indicator displayed on a taskbar button.</summary>
		/// <param name="form">
		/// The window in which the progress of an operation is being shown. This window's associated taskbar button will display the
		/// progress bar.
		/// </param>
		/// <returns>The current state of the progress button.</returns>
		[Category(category), DisplayName("TaskbarButtonProgressState"), DefaultValue(TaskbarButtonProgressState.None)]
		[Description("Gets or sets the state of the progress indicator displayed on a taskbar button.")]
		public TaskbarButtonProgressState GetTaskbarButtonProgressState(Form form) => GetPropertyValue(form, TaskbarButtonProgressState.None);

		/// <summary>
		/// Displays or updates a progress bar hosted in a taskbar button to show the specific percentage completed of the full operation.
		/// </summary>
		/// <param name="form">The window whose associated taskbar button is being used as a progress indicator.</param>
		/// <returns>The proportion of the operation that has been completed at the time the method is called.</returns>
		[Category(category), DisplayName("TaskbarButtonProgressValue"), DefaultValue(0.0f)]
		[Description("Gets or sets the percentage completion for the progress bar hosted in a taskbar button.")]
		public float GetTaskbarButtonProgressValue(Form form) => GetPropertyValue(form, 0.0f);

		/// <summary>Gets the taskbar button thumbnails.</summary>
		/// <param name="form">The window owning the taskbar button thumbnails.</param>
		/// <returns>A collection of thumbnails.</returns>
		[Category(category), DisplayName("TaskbarButtonThumbnails")]
		[Description("Gets the list of thumbnails associated with the taskbar button.")]
		public TaskbarButtonThumbnails GetTaskbarButtonThumbnails(Form form)
		{
			var ret = GetPropertyValue<TaskbarButtonThumbnails>(form);
			if (ret is null)
			{
				ret = new TaskbarButtonThumbnails(form);
				SetPropertyValue(form, ret, "TaskbarButtonThumbnails");
			}
			return ret;
		}

		/// <summary>Gets the description displayed on the tooltip of the taskbar button.</summary>
		/// <param name="form">The form.</param>
		/// <returns>The description</returns>
		[Category(category), DisplayName("TaskbarButtonTooltip"), DefaultValue(null), Localizable(true)]
		[Description("Gets or sets the description displayed on the tooltip of the taskbar button.")]
		public string GetTaskbarButtonTooltip(Form form) => GetPropertyValue<string>(form);

		/// <summary>Sets the application identifier.</summary>
		/// <param name="form">The form.</param>
		/// <param name="value">The value.</param>
		public void SetAppID(Form form, string value)
		{
			if (SetPropertyValue(form, value) && IsTaskbarButtonCreated)
				ApplySetting(form, value);
		}

		/// <summary>Sets the overlay icon to dispaly on a taskbar button to indicate application status or a notification to the user.</summary>
		/// <param name="form">The form.</param>
		/// <param name="value">The overlay icon to apply.</param>
		public void SetTaskbarButtonOverlay(Form form, Icon value)
		{
			if (SetPropertyValue(form, value) && IsTaskbarButtonCreated)
				ApplySetting(form, value);
		}

		/// <summary>
		/// Gets the overlay tooltip to dispaly on a taskbar button to indicate application status or a notification to the user.
		/// </summary>
		/// <param name="form">The form.</param>
		/// <param name="value">The overlay tooltip.</param>
		public void SetTaskbarButtonOverlayTooltip(Form form, string value)
		{
			if (SetPropertyValue(form, value) && IsTaskbarButtonCreated)
				ApplySetting(form, value);
		}

		/// <summary>Sets the type and state of the progress indicator displayed on a taskbar button.</summary>
		/// <param name="form">
		/// The window in which the progress of an operation is being shown. This window's associated taskbar button will display the
		/// progress bar.
		/// </param>
		/// <param name="value">The current state of the progress button. Specify only one of the enum values.</param>
		public void SetTaskbarButtonProgressState(Form form, TaskbarButtonProgressState value)
		{
			if (SetPropertyValue(form, value) && IsTaskbarButtonCreated)
				ApplySetting(form, value);
		}

		/// <summary>
		/// Displays or updates a progress bar hosted in a taskbar button to show the specific percentage completed of the full operation.
		/// </summary>
		/// <param name="form">The window whose associated taskbar button is being used as a progress indicator.</param>
		/// <param name="value">
		/// The proportion of the operation that has been completed at the time the method is called. This value must be between 0.0f and
		/// </param>
		public void SetTaskbarButtonProgressValue(Form form, float value)
		{
			if (value < 0f || value > 1.0f)
				throw new ArgumentOutOfRangeException(nameof(value), "Progress value must be a number between 0 and 1, inclusive.");
			if (SetPropertyValue(form, value) && IsTaskbarButtonCreated)
				ApplySetting(form, value);
		}

		/// <summary>Sets the description displayed on the tooltip of the taskbar button.</summary>
		/// <param name="form">The form.</param>
		/// <param name="value">The description.</param>
		public void SetTaskbarButtonTooltip(Form form, string value)
		{
			if (SetPropertyValue(form, value) && IsTaskbarButtonCreated)
				ApplySetting(form, value);
		}

		/// <summary>Calls the <see cref="PropertyChanged"/> event.</summary>
		/// <param name="form">The form.</param>
		/// <param name="propName">Name of the changed property.</param>
		protected virtual void OnProperyChanged(Form form, string propName) => PropertyChanged?.Invoke(form, new PropertyChangedEventArgs(propName));

		private static string GetUniqueProcessID() => System.Reflection.Assembly.GetEntryAssembly()?.GetName().Name ?? null;

		private void ApplySetting(Form form, object value, [CallerMemberName] string propName = "")
		{
			if (propName.StartsWith("Set"))
				propName = propName.Remove(0, 3);
			switch (propName)
			{
				case "AppID":
					TaskbarList.SetWindowAppId(form.Handle, (string)value);
					break;

				case "TaskbarButtonTooltip":
					TaskbarList.SetThumbnailTooltip(form, (string)value);
					break;

				case "TaskbarButtonOverlay":
					TaskbarList.SetOverlayIcon(form, (Icon)value, GetTaskbarButtonOverlayTooltip(form));
					break;

				case "TaskbarButtonOverlayTooltip":
					TaskbarList.SetOverlayIcon(form, GetTaskbarButtonOverlay(form), (string)value);
					break;

				case "TaskbarButtonProgressState":
					TaskbarList.SetProgressState(form, (TaskbarButtonProgressState)value);
					break;

				case "TaskbarButtonProgressValue":
					TaskbarList.SetProgressValue(form, (ulong)(100000 * (float)value), 100000);
					break;

				case "TaskbarButtonThumbnails":
					GetTaskbarButtonThumbnails(form).ResetToolbar();
					break;

				case "JumpList":
					GetJumpList(form).ApplySettings(GetAppID(form));
					break;

				default:
					throw new InvalidOperationException("Unrecognized setting name.");
			}
			OnProperyChanged(form, propName);
		}

		private void OnTaskbarButtonCreated(HWND hWnd)
		{
			IsTaskbarButtonCreated = true;

			// Apply any settings for this window
			var form = ExtendedComponents.FirstOrDefault(f => f.Handle == hWnd);
			if (form is null) return;
			foreach (var kv in propHash[form])
				ApplySetting(form, kv.Value, kv.Key);
		}

		private void OnThumbnailButtonClick(HWND hWnd, int buttonId)
		{
			var form = ExtendedComponents.FirstOrDefault(f => f.Handle == hWnd);
			if (form is null) return;
			var th = GetTaskbarButtonThumbnails(form);
			if (th is null) return;
			if (buttonId >= 0 && buttonId < th.Toolbar.Buttons.Count)
				th.Toolbar.Buttons[buttonId].InvokeClick();
		}

		private void ResetJumpList(Form form) => propHash[form].Remove("JumpList");

		private void ResetTaskbarButtonThumbnails(Form form) => propHash[form].Remove("TaskbarButtonThumbnails");

		private bool ShouldSerializeJumpList(Form form) => GetJumpList(form).Count > 0;

		private bool ShouldSerializeTaskbarButtonThumbnails(Form form) => GetTaskbarButtonThumbnails(form).Count > 0;

		[SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)]
		private class ThumbButtonMessageFilter : IMessageFilter
		{
			public bool PreFilterMessage(ref Message m)
			{
				if (m.Msg == Shell32.WM_TASKBARBUTTONCREATED)
					TaskbarButtonCreated(m.HWnd);
				else if (m.Msg == (int)User32.WindowMessage.WM_COMMAND && Macros.HIWORD(m.WParam) == Shell32.THBN_CLICKED)
				{
					ThumbnailButtonClick(m.HWnd, Macros.LOWORD(m.WParam));
					return true;
				}
				return false;
			}
		}

		// TODO: ??: SetActiveAlt, MarkFullscreenWindow
	}
}