// 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;

namespace System.Windows.Forms.ButtonInternal;

internal class ButtonFlatAdapter : ButtonBaseAdapter
{
    private const int BorderSize = 1;

    internal ButtonFlatAdapter(ButtonBase control) : base(control) { }

    private void PaintBackground(PaintEventArgs e, Rectangle r, Color backColor)
    {
        Rectangle rect = r;
        rect.Inflate(-Control.FlatAppearance.BorderSize, -Control.FlatAppearance.BorderSize);
        Control.PaintBackground(e, rect, backColor, rect.Location);
    }

    internal override void PaintUp(PaintEventArgs e, CheckState state)
    {
        bool hasCustomBorder = Control.FlatAppearance.BorderSize != BorderSize || !Control.FlatAppearance.BorderColor.IsEmpty;

        ColorData colors = PaintFlatRender(e).Calculate();
        LayoutData layout = PaintFlatLayout(
            up: !Control.FlatAppearance.CheckedBackColor.IsEmpty
                || (SystemInformation.HighContrast ? state != CheckState.Indeterminate : state == CheckState.Unchecked),
            check: !hasCustomBorder && SystemInformation.HighContrast && state == CheckState.Checked,
            Control.FlatAppearance.BorderSize).Layout();

        // Paint with the BorderColor if set.
        if (!Control.FlatAppearance.BorderColor.IsEmpty)
        {
            colors.WindowFrame = Control.FlatAppearance.BorderColor;
        }

        Rectangle r = Control.ClientRectangle;

        Color backColor = Control.BackColor;

        if (!Control.FlatAppearance.CheckedBackColor.IsEmpty)
        {
            switch (state)
            {
                case CheckState.Checked:
                    backColor = Control.FlatAppearance.CheckedBackColor;
                    break;
                case CheckState.Indeterminate:
                    backColor = Control.FlatAppearance.CheckedBackColor.MixColor(colors.ButtonFace);
                    break;
            }
        }
        else
        {
            switch (state)
            {
                case CheckState.Checked:
                    backColor = colors.Highlight;
                    break;
                case CheckState.Indeterminate:
                    backColor = colors.Highlight.MixColor(colors.ButtonFace);
                    break;
            }
        }

        PaintBackground(e, r, IsHighContrastHighlighted() ? SystemColors.Highlight : backColor);

        if (Control.IsDefault)
        {
            r.Inflate(-1, -1);
        }

        PaintImage(e, layout);
        PaintField(e, layout, colors, IsHighContrastHighlighted() ? SystemColors.HighlightText : colors.WindowText, drawFocus: false);

        if (Control.Focused && Control.ShowFocusCues)
        {
            DrawFlatFocus(e, layout.Focus, colors.Options.HighContrast ? colors.WindowText : colors.ContrastButtonShadow);
        }

        if (!(Control.IsDefault && Control.Focused && (Control.FlatAppearance.BorderSize == 0)))
        {
            DrawDefaultBorder(e, r, colors.WindowFrame, Control.IsDefault);
        }

        // Always check if the BorderSize is not the default.If not, we need to paint with the BorderSize set by the user.
        if (hasCustomBorder)
        {
            if (Control.FlatAppearance.BorderSize != BorderSize)
            {
                DrawFlatBorderWithSize(e, r, colors.WindowFrame, Control.FlatAppearance.BorderSize);
            }
            else
            {
                ControlPaint.DrawBorderSimple(e, r, colors.WindowFrame);
            }
        }
        else if (state == CheckState.Checked && SystemInformation.HighContrast)
        {
            ControlPaint.DrawBorderSimple(e, r, colors.WindowFrame);
            ControlPaint.DrawBorderSimple(e, r, colors.ButtonShadow);
        }
        else if (state == CheckState.Indeterminate)
        {
            Draw3DLiteBorder(e, r, colors, up: false);
        }
        else
        {
            ControlPaint.DrawBorderSimple(e, r, colors.WindowFrame);
        }
    }

    internal override void PaintDown(PaintEventArgs e, CheckState state)
    {
        bool hasCustomBorder = (Control.FlatAppearance.BorderSize != BorderSize || !Control.FlatAppearance.BorderColor.IsEmpty);

        ColorData colors = PaintFlatRender(e).Calculate();
        LayoutData layout = PaintFlatLayout(
            !Control.FlatAppearance.CheckedBackColor.IsEmpty
                || (SystemInformation.HighContrast ? state != CheckState.Indeterminate : state == CheckState.Unchecked),
            !hasCustomBorder && SystemInformation.HighContrast && state == CheckState.Checked,
            Control.FlatAppearance.BorderSize).Layout();

        // Paint with the BorderColor if Set.
        if (!Control.FlatAppearance.BorderColor.IsEmpty)
        {
            colors.WindowFrame = Control.FlatAppearance.BorderColor;
        }

        Rectangle r = Control.ClientRectangle;

        Color backColor = Control.BackColor;

        if (!Control.FlatAppearance.MouseDownBackColor.IsEmpty)
        {
            backColor = Control.FlatAppearance.MouseDownBackColor;
        }
        else
        {
            switch (state)
            {
                case CheckState.Unchecked:
                case CheckState.Checked:
                    backColor = colors.Options.HighContrast ? colors.ButtonShadow : colors.LowHighlight;
                    break;
                case CheckState.Indeterminate:
                    backColor = colors.ButtonFace.MixColor(colors.Options.HighContrast
                        ? colors.ButtonShadow
                        : colors.LowHighlight);
                    break;
            }
        }

        PaintBackground(e, r, backColor);

        if (Control.IsDefault)
        {
            r.Inflate(-1, -1);
        }

        PaintImage(e, layout);
        PaintField(e, layout, colors, colors.WindowText, drawFocus: false);

        if (Control.Focused && Control.ShowFocusCues)
        {
            DrawFlatFocus(e, layout.Focus, colors.Options.HighContrast ? colors.WindowText : colors.ContrastButtonShadow);
        }

        if (!(Control.IsDefault && Control.Focused && (Control.FlatAppearance.BorderSize == 0)))
        {
            DrawDefaultBorder(e, r, colors.WindowFrame, Control.IsDefault);
        }

        // Always check if the BorderSize is not the default.If not, we need to paint with the BorderSize set by the user.
        if (hasCustomBorder)
        {
            if (Control.FlatAppearance.BorderSize != BorderSize)
            {
                DrawFlatBorderWithSize(e, r, colors.WindowFrame, Control.FlatAppearance.BorderSize);
            }
            else
            {
                ControlPaint.DrawBorderSimple(e, r, colors.WindowFrame);
            }
        }
        else if (state == CheckState.Checked && SystemInformation.HighContrast)
        {
            ControlPaint.DrawBorderSimple(e, r, colors.WindowFrame);
            ControlPaint.DrawBorderSimple(e, r, colors.ButtonShadow);
        }
        else if (state == CheckState.Indeterminate)
        {
            Draw3DLiteBorder(e, r, colors, up: false);
        }
        else
        {
            ControlPaint.DrawBorderSimple(e, r, colors.WindowFrame);
        }
    }

    internal override void PaintOver(PaintEventArgs e, CheckState state)
    {
        if (SystemInformation.HighContrast)
        {
            PaintUp(e, state);
        }
        else
        {
            bool hasCustomBorder = Control.FlatAppearance.BorderSize != BorderSize || !Control.FlatAppearance.BorderColor.IsEmpty;

            ColorData colors = PaintFlatRender(e).Calculate();
            LayoutData layout = PaintFlatLayout(
                up: !Control.FlatAppearance.CheckedBackColor.IsEmpty || state == CheckState.Unchecked,
                check: false,
                Control.FlatAppearance.BorderSize).Layout();

            // Paint with the BorderColor if Set.
            if (!Control.FlatAppearance.BorderColor.IsEmpty)
            {
                colors.WindowFrame = Control.FlatAppearance.BorderColor;
            }

            Rectangle r = Control.ClientRectangle;

            Color backColor;
            if (!Control.FlatAppearance.MouseOverBackColor.IsEmpty)
            {
                backColor = Control.FlatAppearance.MouseOverBackColor;
            }
            else if (!Control.FlatAppearance.CheckedBackColor.IsEmpty)
            {
                backColor = state is CheckState.Checked or CheckState.Indeterminate
                    ? Control.FlatAppearance.CheckedBackColor.MixColor(colors.LowButtonFace)
                    : colors.LowButtonFace;
            }
            else
            {
                backColor = state is CheckState.Indeterminate
                    ? colors.ButtonFace.MixColor(colors.LowButtonFace)
                    : colors.LowButtonFace;
            }

            PaintBackground(e, r, IsHighContrastHighlighted() ? SystemColors.Highlight : backColor);

            if (Control.IsDefault)
            {
                r.Inflate(-1, -1);
            }

            PaintImage(e, layout);
            PaintField(
                e,
                layout,
                colors,
                IsHighContrastHighlighted() ? SystemColors.HighlightText : colors.WindowText,
                drawFocus: false);

            if (Control.Focused && Control.ShowFocusCues)
            {
                DrawFlatFocus(e, layout.Focus, colors.ContrastButtonShadow);
            }

            if (!(Control.IsDefault && Control.Focused && (Control.FlatAppearance.BorderSize == 0)))
            {
                DrawDefaultBorder(e, r, colors.WindowFrame, Control.IsDefault);
            }

            // Always check if the BorderSize is not the default.If not, we need to paint with the BorderSize set by the user.
            if (hasCustomBorder)
            {
                if (Control.FlatAppearance.BorderSize != BorderSize)
                {
                    DrawFlatBorderWithSize(e, r, colors.WindowFrame, Control.FlatAppearance.BorderSize);
                }
                else
                {
                    ControlPaint.DrawBorderSimple(e, r, colors.WindowFrame);
                }
            }
            else if (state == CheckState.Unchecked)
            {
                ControlPaint.DrawBorderSimple(e, r, colors.WindowFrame);
            }
            else
            {
                Draw3DLiteBorder(e, r, colors, up: false);
            }
        }
    }

    protected override LayoutOptions Layout(PaintEventArgs e) =>
        PaintFlatLayout(up: false, check: true, Control.FlatAppearance.BorderSize);

    internal static LayoutOptions PaintFlatLayout(
        bool up,
        bool check,
        int borderSize,
        Rectangle clientRectangle,
        Padding padding,
        bool isDefault,
        Font font,
        string text,
        bool enabled,
        ContentAlignment textAlign,
        RightToLeft rtl)
    {
        LayoutOptions layout = CommonLayout(clientRectangle, padding, isDefault, font, text, enabled, textAlign, rtl);
        layout.BorderSize = borderSize + (check ? 1 : 0);
        layout.PaddingSize = check ? 1 : 2;
        layout.FocusOddEvenFixup = false;
        layout.TextOffset = !up;
        layout.ShadowedText = SystemInformation.HighContrast;

        return layout;
    }

    private LayoutOptions PaintFlatLayout(bool up, bool check, int borderSize)
    {
        LayoutOptions layout = CommonLayout();
        layout.BorderSize = borderSize + (check ? 1 : 0);
        layout.PaddingSize = check ? 1 : 2;
        layout.FocusOddEvenFixup = false;
        layout.TextOffset = !up;
        layout.ShadowedText = SystemInformation.HighContrast;

        return layout;
    }
}
