// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

#nullable disable

using System.Collections;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Drawing;

namespace System.Windows.Forms.Design;

/// <summary>
///  This is the designer for the list view control. It implements hit testing for
///  the items in the list view.
/// </summary>
internal class ListViewDesigner : ControlDesigner
{
    private DesignerActionListCollection _actionLists;
    private HDHITTESTINFO _hdrhit;
    private bool _inShowErrorDialog;

    /// <summary>
    ///  Retrieves a list of associated components. These are components that should be included
    ///  in a cut or copy operation on this component.
    /// </summary>
    public override ICollection AssociatedComponents
    {
        get
        {
            ListView lv = Control as ListView;
            if (lv is not null)
            {
                return lv.Columns;
            }

            return base.AssociatedComponents;
        }
    }

    private bool OwnerDraw
    {
        get
        {
            return (bool)ShadowProperties[nameof(OwnerDraw)];
        }
        set
        {
            ShadowProperties[nameof(OwnerDraw)] = value;
        }
    }

    private View View
    {
        get
        {
            return ((ListView)Component).View;
        }
        set
        {
            ((ListView)Component).View = value;
            if (value == View.Details)
            {
                HookChildHandles((HWND)Control.Handle);
            }
        }
    }

    protected override unsafe bool GetHitTest(Point point)
    {
        // We override GetHitTest to make the header in report view UI-active.

        ListView listView = (ListView)Component;
        if (listView.View == View.Details)
        {
            Point listViewPoint = Control.PointToClient(point);
            HWND hwndHit = PInvoke.ChildWindowFromPointEx(listView, listViewPoint, CWP_FLAGS.CWP_SKIPINVISIBLE);

            if (!hwndHit.IsNull && hwndHit != listView.Handle)
            {
                HWND headerHwnd = (HWND)PInvokeCore.SendMessage(listView, PInvoke.LVM_GETHEADER);
                if (hwndHit == headerHwnd)
                {
                    PInvokeCore.MapWindowPoints(HWND.Null, headerHwnd, ref point);
                    _hdrhit.pt = point;
                    PInvokeCore.SendMessage(headerHwnd, PInvoke.HDM_HITTEST, 0, ref _hdrhit);
                    if (_hdrhit.flags == HEADER_HITTEST_INFO_FLAGS.HHT_ONDIVIDER)
                        return true;
                }
            }
        }

        return false;
    }

    public override void Initialize(IComponent component)
    {
        ListView lv = (ListView)component;
        OwnerDraw = lv.OwnerDraw;
        lv.OwnerDraw = false;
        lv.UseCompatibleStateImageBehavior = false;

        AutoResizeHandles = true;

        base.Initialize(component);
        if (lv.View == View.Details)
        {
            HookChildHandles((HWND)Control.Handle);
        }
    }

    protected override void PreFilterProperties(IDictionary properties)
    {
        PropertyDescriptor ownerDrawProp = (PropertyDescriptor)properties["OwnerDraw"];

        if (ownerDrawProp is not null)
        {
            properties["OwnerDraw"] = TypeDescriptor.CreateProperty(typeof(ListViewDesigner), ownerDrawProp, []);
        }

        PropertyDescriptor viewProp = (PropertyDescriptor)properties["View"];

        if (viewProp is not null)
        {
            properties["View"] = TypeDescriptor.CreateProperty(typeof(ListViewDesigner), viewProp, []);
        }

        base.PreFilterProperties(properties);
    }

    protected override unsafe void WndProc(ref Message m)
    {
        switch (m.Msg)
        {
            case (int)PInvokeCore.WM_NOTIFY:
            case (int)MessageId.WM_REFLECT_NOTIFY:
                NMHDR* nmhdr = (NMHDR*)(nint)m.LParamInternal;
                if (nmhdr->code == PInvoke.HDN_ENDTRACKW)
                {
                    // Re-codegen if the columns have been resized
                    try
                    {
                        GetService<IComponentChangeService>().OnComponentChanged(Component);
                    }
                    catch (InvalidOperationException ex)
                    {
                        if (_inShowErrorDialog)
                        {
                            return;
                        }

                        _inShowErrorDialog = true;
                        try
                        {
                            ShowErrorDialog(Component.Site.GetService<IUIService>(), ex, (ListView)Component);
                        }
                        finally
                        {
                            _inShowErrorDialog = false;
                        }

                        return;
                    }
                }

                break;
        }

        base.WndProc(ref m);
    }

    public override DesignerActionListCollection ActionLists
    {
        get
        {
            if (_actionLists is null)
            {
                _actionLists = new DesignerActionListCollection();
                _actionLists.Add(new ListViewActionList(this));
            }

            return _actionLists;
        }
    }

    private static void ShowErrorDialog(IUIService uiService, InvalidOperationException ex, Control control)
    {
        if (uiService is not null)
        {
            uiService.ShowError(ex);
        }
        else
        {
            string message = ex.Message;
            if (message is null || message.Length == 0)
            {
                message = ex.ToString();
            }

            RTLAwareMessageBox.Show(control, message, null, MessageBoxButtons.OK, MessageBoxIcon.Exclamation,
                    MessageBoxDefaultButton.Button1, 0);
        }
    }
}
