// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

#nullable disable

using System.Diagnostics;
using System.Drawing;
using System.Runtime.InteropServices;
using static Interop;

namespace System.Windows.Forms
{
    /// <summary>
    ///  This is the toolstrip used for merging the [:)]    [_][#][X] buttons onto an
    ///  mdi parent when an MDI child is maximized.
    /// </summary>
    internal partial class MdiControlStrip : MenuStrip
    {
        private readonly ToolStripMenuItem _system;
        private readonly ToolStripMenuItem _close;
        private readonly ToolStripMenuItem _minimize;
        private readonly ToolStripMenuItem _restore;
        private MenuStrip _mergedMenu;

        private IWin32Window _target;

        /// <summary> target is ideally the MDI Child to send the system commands to.
        ///  although there's nothing MDI child specific to it... you could have this
        ///  a toplevel window.
        /// </summary>
        public MdiControlStrip(IWin32Window target)
        {
            IntPtr hMenu = User32.GetSystemMenu(new HandleRef(this, Control.GetSafeHandle(target)), bRevert: BOOL.FALSE);
            _target = target;

            // The menu item itself takes care of enabledness and sending WM_SYSCOMMAND messages to the target.
            _minimize = new ControlBoxMenuItem(hMenu, User32.SC.MINIMIZE, target);
            _close = new ControlBoxMenuItem(hMenu, User32.SC.CLOSE, target);
            _restore = new ControlBoxMenuItem(hMenu, User32.SC.RESTORE, target);

            // The dropDown of the system menu is the one that talks to native.
            _system = new SystemMenuItem();

            // However in the event that the target handle changes we have to push the new handle into everyone.
            if (target is Control controlTarget)
            {
                controlTarget.HandleCreated += new EventHandler(OnTargetWindowHandleRecreated);
                controlTarget.Disposed += new EventHandler(OnTargetWindowDisposed);
            }

            // add in opposite order to how you want it merged
            Items.AddRange(new ToolStripItem[] { _minimize, _restore, _close, _system });

            SuspendLayout();
            foreach (ToolStripItem item in Items)
            {
                item.DisplayStyle = ToolStripItemDisplayStyle.Image;
                item.MergeIndex = 0;
                item.MergeAction = MergeAction.Insert;
                item.Overflow = ToolStripItemOverflow.Never;
                item.Alignment = ToolStripItemAlignment.Right;
                item.Padding = Padding.Empty;
                // image is not scaled well on high dpi devices. Setting property to fit to size.
                item.ImageScaling = ToolStripItemImageScaling.SizeToFit;
            }

            // set up the sytem menu

            _system.Image = GetTargetWindowIcon();
            _system.Visible = GetTargetWindowIconVisibility();
            _system.Alignment = ToolStripItemAlignment.Left;
            _system.DropDownOpening += new EventHandler(OnSystemMenuDropDownOpening);
            _system.ImageScaling = ToolStripItemImageScaling.None;
            _system.DoubleClickEnabled = true;
            _system.DoubleClick += new EventHandler(OnSystemMenuDoubleClick);
            _system.Padding = Padding.Empty;
            _system.ShortcutKeys = Keys.Alt | Keys.OemMinus;
            ResumeLayout(false);
        }

        public ToolStripMenuItem Close
        {
            get { return _close; }
        }

        internal MenuStrip MergedMenu
        {
            get
            {
                return _mergedMenu;
            }
            set
            {
                _mergedMenu = value;
            }
        }

        private Image GetTargetWindowIcon()
        {
            IntPtr hIcon = User32.SendMessageW(new HandleRef(this, GetSafeHandle(_target)), User32.WM.GETICON, (IntPtr)User32.ICON.SMALL, IntPtr.Zero);
            Icon icon = hIcon != IntPtr.Zero ? Icon.FromHandle(hIcon) : Form.DefaultIcon;
            Icon smallIcon = new Icon(icon, SystemInformation.SmallIconSize);

            Image systemIcon = smallIcon.ToBitmap();
            smallIcon.Dispose();

            return systemIcon;
        }

        private bool GetTargetWindowIconVisibility() => _target is not Form formTarget || formTarget.ShowIcon;

        public void updateIcon()
        {
            _system.Image = GetTargetWindowIcon();
            _system.Visible = GetTargetWindowIconVisibility();
        }

        protected internal override void OnItemAdded(ToolStripItemEventArgs e)
        {
            base.OnItemAdded(e);
            Debug.Assert(Items.Count <= 4, "Too many items in the MDIControlStrip.  How did we get into this situation?");
        }

        private void OnTargetWindowDisposed(object sender, EventArgs e)
        {
            UnhookTarget();
            _target = null;
        }

        private void OnTargetWindowHandleRecreated(object sender, EventArgs e)
        {
            // in the case that the handle for the form is recreated we need to set
            // up the handles to point to the new window handle for the form.

            _system.SetNativeTargetWindow(_target);
            _minimize.SetNativeTargetWindow(_target);
            _close.SetNativeTargetWindow(_target);
            _restore.SetNativeTargetWindow(_target);

            IntPtr hMenu = User32.GetSystemMenu(new HandleRef(this, Control.GetSafeHandle(_target)), bRevert: BOOL.FALSE);
            _system.SetNativeTargetMenu(hMenu);
            _minimize.SetNativeTargetMenu(hMenu);
            _close.SetNativeTargetMenu(hMenu);
            _restore.SetNativeTargetMenu(hMenu);

            // clear off the System DropDown.
            if (_system.HasDropDownItems)
            {
                // next time we need one we'll just fetch it fresh.
                _system.DropDown.Items.Clear();
                _system.DropDown.Dispose();
            }

            _system.Image = GetTargetWindowIcon();
            _system.Visible = GetTargetWindowIconVisibility();
        }

        private void OnSystemMenuDropDownOpening(object sender, EventArgs e)
        {
            if (!_system.HasDropDownItems && (_target is not null))
            {
                _system.DropDown = ToolStripDropDownMenu.FromHMenu(User32.GetSystemMenu(new HandleRef(this, Control.GetSafeHandle(_target)), bRevert: BOOL.FALSE), _target);
            }
            else if (MergedMenu is null)
            {
                _system.DropDown.Dispose();
            }
        }

        private void OnSystemMenuDoubleClick(object sender, EventArgs e)
        {
            Close.PerformClick();
        }

        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                UnhookTarget();
                _target = null;
            }

            base.Dispose(disposing);
        }

        private void UnhookTarget()
        {
            if (_target is not null)
            {
                if (_target is Control controlTarget)
                {
                    controlTarget.HandleCreated -= new EventHandler(OnTargetWindowHandleRecreated);
                    controlTarget.Disposed -= new EventHandler(OnTargetWindowDisposed);
                }

                _target = null;
            }
        }
    }
}
