using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;

namespace Pilz.UI;


internal class HighlightPanel : Control
{

    private Dictionary<Control, eHighlightColor> _Highlights = null;
    private List<HighlightRegion> _HighlightRegions = new List<HighlightRegion>();

    public HighlightPanel(Dictionary<Control, eHighlightColor> highlights)
    {
        _Highlights = highlights;
        SetStyle(ControlStyles.UserPaint, true);
        SetStyle(ControlStyles.AllPaintingInWmPaint, true);
        SetStyle(ControlStyles.Opaque, true);
        SetStyle(ControlStyles.ResizeRedraw, true);
        SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
        SetStyle(ControlStyles.Selectable, false);
    }

    protected override void OnPaint(PaintEventArgs e)
    {
        var g = e.Graphics;
        g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;

        foreach (HighlightRegion highlightRegion in _HighlightRegions)
        {
            Color[] colors = GetHighlightColors(highlightRegion.HighlightColor);
            var r = highlightRegion.Bounds;
            var back = highlightRegion.BackColor;
            r.Inflate(1, 1);
            DisplayHelp.FillRectangle(g, r, back);
            r.Inflate(-1, -1);
            DisplayHelp.FillRoundedRectangle(g, r, 2, colors[0]);
            r.Inflate(-2, -2);
            DisplayHelp.DrawRectangle(g, colors[2], r);
            r.Inflate(1, 1);
            DisplayHelp.DrawRoundedRectangle(g, colors[1], r, 2);
        }

        base.OnPaint(e);
    }

    private Color[] GetHighlightColors(eHighlightColor color)
    {
        Color[] colors = new Color[3];

        if (color == eHighlightColor.Blue)
        {
            colors[0] = GetColor(172, 0x6A9CD4);
            colors[1] = GetColor(0x6A9CD4);
            colors[2] = GetColor(0x5D7EA4);
        }
        else if (color == eHighlightColor.Orange)
        {
            colors[0] = GetColor(172, 0xFF9C00);
            colors[1] = GetColor(0xFF9C00);
            colors[2] = GetColor(0xCC6600);
        }
        else if (color == eHighlightColor.Green)
        {
            colors[0] = GetColor(172, 0x71B171);
            colors[1] = GetColor(0x71B171);
            colors[2] = GetColor(0x339933);
        }
        else if (color == eHighlightColor.Custom)
        {
            if (_CustomHighlightColors is null || _CustomHighlightColors.Length < 3)
            {
                colors[0] = Color.Red;
                colors[1] = Color.Red;
                colors[2] = Color.Red;
            }
            else
            {
                colors[0] = _CustomHighlightColors[0];
                colors[1] = _CustomHighlightColors[1];
                colors[2] = _CustomHighlightColors[2];
            }
        }
        else
        {
            colors[0] = GetColor(172, 0xC63030);
            colors[1] = GetColor(0xC63030);
            colors[2] = GetColor(0x990000);
        }

        return colors;
    }

    protected override void OnVisibleChanged(EventArgs e)
    {
        if (Visible && !_UpdatingRegion)
            UpdateRegion();
        base.OnVisibleChanged(e);
    }

    protected override void OnHandleCreated(EventArgs e)
    {
        if (!_RegionInitialized)
            UpdateRegion();
        base.OnHandleCreated(e);
    }

    private bool _RegionInitialized = false;
    private bool _UpdatingRegion = false;

    internal void UpdateRegion()
    {
        if (_UpdatingRegion || !IsHandleCreated)
            return;

        try
        {
            _UpdatingRegion = true;
            Region = null;
            _HighlightRegions.Clear();
            if (_Highlights is null)
                return;

            if (_Highlights.Count == 0 && _FocusHighlightControl is null)
            {
                Visible = false;
                return;
            }

            bool processFocusControl = true;
            Region region = null;

            foreach (KeyValuePair<Control, eHighlightColor> item in _Highlights)
            {
                if (item.Value == eHighlightColor.None || !GetIsVisible(item.Key))
                    continue;
                if (ReferenceEquals(item.Key, _FocusHighlightControl))
                    processFocusControl = false;
                var r = GetControlRect(item.Key);
                if (r.IsEmpty)
                    continue;
                r.Inflate(2, 2);
                _HighlightRegions.Add(new HighlightRegion(r, GetBackColor(item.Key.Parent), item.Value));

                if (region is null)
                    region = new Region(r);
                else
                {
                    region.Union(r);
                }

                r.Inflate(-3, -3);
                region.Exclude(r);
            }

            if (processFocusControl && _FocusHighlightControl is not null && _FocusHighlightControl.Visible)
            {
                var r = GetControlRect(_FocusHighlightControl);

                if (!r.IsEmpty)
                {
                    r.Inflate(2, 2);
                    _HighlightRegions.Add(new HighlightRegion(r, GetBackColor(_FocusHighlightControl.Parent), _FocusHighlightColor));

                    if (region is null)
                        region = new Region(r);
                    else
                    {
                        region.Union(r);
                    }

                    r.Inflate(-3, -3);
                    region.Exclude(r);
                }
            }

            Region = region;

            if (region is null)
                Visible = false;
            else if (!Visible)
            {
                Visible = true;
                BringToFront();
            }

            Invalidate();
        }
        finally
        {
            _UpdatingRegion = false;
            _RegionInitialized = true;
        }
    }

    private static Color GetColor(int rgb)
    {
        if (rgb == -1)
            return Color.Empty;
        else
        {
            return Color.FromArgb((rgb & 0xFF0000) >> 16, (rgb & 0xFF00) >> 8, rgb & 0xFF);
        }
    }

    private static Color GetColor(int alpha, int rgb)
    {
        if (rgb == -1)
            return Color.Empty;
        else
        {
            return Color.FromArgb(alpha, (rgb & 0xFF0000) >> 16, (rgb & 0xFF00) >> 8, rgb & 0xFF);
        }
    }

    private bool GetIsVisible(Control control)
    {
        if (!control.Visible)
            return false;
        if (control.Parent is null || !control.IsHandleCreated)
            return control.Visible;
        var rect = new Pilz.Win32.Native.RECT();
        Pilz.Win32.Native.User32.GetWindowRect(control.Handle, ref rect);
        var pp = control.Parent.PointToClient(new Point(rect.Left + 3, rect.Top + 3));
        var handle = Pilz.Win32.Native.User32.ChildWindowFromPointEx(control.Parent.Handle, new Pilz.Win32.Native.POINT(pp.X, pp.Y), (uint)Pilz.Win32.Native.WindowFromPointFlags.CWP_SKIPINVISIBLE);
        if (handle == IntPtr.Zero)
            return control.Visible;
        var c = Control.FromHandle(handle);

        if (c is not null && !ReferenceEquals(c, control) && !ReferenceEquals(c, this) && !ReferenceEquals(c, control.Parent))
            return false;

        return control.Visible;
    }

    private Color GetBackColor(Control control)
    {
        var backColor = control.BackColor;

        if (backColor.IsEmpty || backColor == Color.Transparent)
            backColor = SystemColors.Control;
        else if (backColor.A < 255)
        {
            backColor = Color.FromArgb(255, backColor);
        }

        return backColor;
    }

    protected override void OnResize(EventArgs e)
    {
        UpdateRegion();
        base.OnResize(e);
    }

    private Rectangle GetControlRect(Control c)
    {
        if (!c.IsHandleCreated)
            return Rectangle.Empty;
        var rect = default(Pilz.Win32.Native.RECT);
        Pilz.Win32.Native.User32.GetWindowRect(c.Handle, ref rect);
        var p = this.PointToClient(rect.Location);
        return new Rectangle(p, rect.Size);
    }

    private struct HighlightRegion
    {
        public Rectangle Bounds;
        public Color BackColor;
        public eHighlightColor HighlightColor;

        public HighlightRegion(Rectangle bounds, Color backColor, eHighlightColor highlightColor)
        {
            Bounds = bounds;
            BackColor = backColor;
            HighlightColor = highlightColor;
        }
    }

    private Control _FocusHighlightControl;

    public Control FocusHighlightControl
    {
        get
        {
            return _FocusHighlightControl;
        }
        set
        {
            _FocusHighlightControl = value;
        }
    }

    private eHighlightColor _FocusHighlightColor = eHighlightColor.Blue;

    public eHighlightColor FocusHighlightColor
    {
        get
        {
            return _FocusHighlightColor;
        }
        set
        {
            _FocusHighlightColor = value;
        }
    }

    private Color[] _CustomHighlightColors = null;

    public Color[] CustomHighlightColors
    {
        get
        {
            return _CustomHighlightColors;
        }
        set
        {
            _CustomHighlightColors = value;
        }
    }
}