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

using System.Drawing;
using System.Windows.Forms.VisualStyles;

namespace System.Windows.Forms;

/// <summary>
///  This is a rendering class for the ComboBox control.
/// </summary>
public static class ComboBoxRenderer
{
    // Make this per-thread, so that different threads can safely use these methods.
    [ThreadStatic]
    private static VisualStyleRenderer? t_visualStyleRenderer;

    private static readonly VisualStyleElement s_comboBoxElement = Application.IsDarkModeEnabled
        ? VisualStyleElement.CreateElement($"{Control.DarkModeIdentifier}_{Control.ComboBoxButtonThemeIdentifier}::{Control.ComboboxClassIdentifier}", 1, 1)
        : VisualStyleElement.ComboBox.DropDownButton.Normal;

    private static readonly VisualStyleElement s_textBoxElement = VisualStyleElement.TextBox.TextEdit.Normal;

    /// <summary>
    ///  Returns true if this class is supported for the current OS and user/application settings,
    ///  otherwise returns false.
    /// </summary>
    public static bool IsSupported => VisualStyleRenderer.IsSupported; // no downlevel support

    private static void DrawBackground(Graphics g, Rectangle bounds, ComboBoxState state)
    {
        t_visualStyleRenderer!.DrawBackground(g, bounds);

        // for disabled ComboBoxes, comctl does not use the window BackColor, so
        // we don't refill here in that case.
        if (state != ComboBoxState.Disabled)
        {
            Color windowColor = t_visualStyleRenderer.GetColor(ColorProperty.FillColor);
            if (windowColor != SystemColors.Window)
            {
                Rectangle fillRect = t_visualStyleRenderer.GetBackgroundContentRectangle(g, bounds);
                fillRect.Inflate(-2, -2);
                // then we need to re-fill the background.
                g.FillRectangle(SystemBrushes.Window, fillRect);
            }
        }
    }

    /// <summary>
    ///  Renders the textbox part of a ComboBox control.
    /// </summary>
    public static void DrawTextBox(Graphics g, Rectangle bounds, ComboBoxState state)
    {
        InitializeRenderer(s_textBoxElement, (int)state);

        DrawBackground(g, bounds, state);
    }

    /// <summary>
    ///  Renders the textbox part of a ComboBox control.
    /// </summary>
    public static void DrawTextBox(Graphics g, Rectangle bounds, string? comboBoxText, Font? font, ComboBoxState state)
    {
        DrawTextBox(g, bounds, comboBoxText, font, TextFormatFlags.TextBoxControl, state);
    }

    /// <summary>
    ///  Renders the textbox part of a ComboBox control.
    /// </summary>
    public static void DrawTextBox(Graphics g, Rectangle bounds, string? comboBoxText, Font? font, Rectangle textBounds, ComboBoxState state)
    {
        DrawTextBox(g, bounds, comboBoxText, font, textBounds, TextFormatFlags.TextBoxControl, state);
    }

    /// <summary>
    ///  Renders the textbox part of a ComboBox control.
    /// </summary>
    public static void DrawTextBox(Graphics g, Rectangle bounds, string? comboBoxText, Font? font, TextFormatFlags flags, ComboBoxState state)
    {
        InitializeRenderer(s_textBoxElement, (int)state);

        Rectangle textBounds = t_visualStyleRenderer.GetBackgroundContentRectangle(g, bounds);
        textBounds.Inflate(-2, -2);
        DrawTextBox(g, bounds, comboBoxText, font, textBounds, flags, state);
    }

    /// <summary>
    ///  Renders the textbox part of a ComboBox control.
    /// </summary>
    public static void DrawTextBox(Graphics g, Rectangle bounds, string? comboBoxText, Font? font, Rectangle textBounds, TextFormatFlags flags, ComboBoxState state)
    {
        InitializeRenderer(s_textBoxElement, (int)state);

        DrawBackground(g, bounds, state);
        Color textColor = t_visualStyleRenderer.GetColor(ColorProperty.TextColor);
        TextRenderer.DrawText(g, comboBoxText, font, textBounds, textColor, flags);
    }

    /// <summary>
    ///  Renders a ComboBox drop-down button.
    /// </summary>
    public static void DrawDropDownButton(Graphics g, Rectangle bounds, ComboBoxState state)
    {
        using DeviceContextHdcScope hdc = new(g);
        DrawDropDownButtonForHandle(hdc, bounds, state, HWND.Null);
    }

    /// <summary>
    ///  Renders a ComboBox drop-down button in per-monitor scenario.
    /// </summary>
    /// <param name="hdc">Device context.</param>
    /// <param name="bounds">Dropdown button bounds.</param>
    /// <param name="state">State.</param>
    /// <param name="hwnd">Handle of the control.</param>
    internal static void DrawDropDownButtonForHandle(HDC hdc, Rectangle bounds, ComboBoxState state, HWND hwnd)
    {
        InitializeRenderer(s_comboBoxElement, (int)state);
        t_visualStyleRenderer.DrawBackground(hdc, bounds, hwnd);
    }

    [MemberNotNull(nameof(t_visualStyleRenderer))]
    private static void InitializeRenderer(VisualStyleElement visualStyleElement, int state)
    {
        if (t_visualStyleRenderer is null)
        {
            t_visualStyleRenderer = new VisualStyleRenderer(visualStyleElement.ClassName, visualStyleElement.Part, state);
        }
        else
        {
            t_visualStyleRenderer.SetParameters(visualStyleElement.ClassName, visualStyleElement.Part, state);
        }
    }
}
