using System;
using System.Collections.Generic;
using System.Drawing;
using Decal.Adapter;
using VirindiViewService;

namespace MosswartMassacre.Views
{
    /// <summary>
    /// In-game vital sharing overlay — a DxHud drawn directly onto the game
    /// surface so the visual matches UtilityBelt's NetworkUI
    /// (DrawCharNameBackground / DrawRangeIndicator). One row per peer
    /// (plus the local character), showing character name, direction arrow
    /// pointing at the peer, distance in meters, HP bar across the full
    /// name area, and Stamina + Mana bars side-by-side on the bottom half.
    /// Colours match the MosswartOverlord player sidebar palette.
    ///
    /// Hold Ctrl + left-drag to reposition the overlay (same behaviour as
    /// UB's NetworkUI). The position is persisted to plugin settings.
    /// </summary>
    internal static class VitalSharingOverlayView
    {
        // Layout constants — identical to UB NetworkUI so rows line up the same.
        private const int ROW_SIZE = 20;
        private const int CHAR_NAME_WIDTH = 180;
        private const int HEALTH_BAR_HEIGHT = 11;
        private const int RANGE_WIDTH = 34;
        private const int PADDING = 4;
        private const int HUD_WIDTH = CHAR_NAME_WIDTH + PADDING + ROW_SIZE + RANGE_WIDTH + PADDING;

        // Colors matched to MosswartOverlord player sidebar (#ff4444 / #ffaa00 / #4488ff)
        private const int HUD_OPACITY = 200;
        private static readonly Color COLOR_HP_FILL = Color.FromArgb(HUD_OPACITY, 255, 68, 68);
        private static readonly Color COLOR_HP_BG = Color.FromArgb(HUD_OPACITY, 60, 0, 0);
        private static readonly Color COLOR_STA_FILL = Color.FromArgb(HUD_OPACITY, 255, 170, 0);
        private static readonly Color COLOR_STA_BG = Color.FromArgb(HUD_OPACITY, 70, 30, 0);
        private static readonly Color COLOR_MANA_FILL = Color.FromArgb(HUD_OPACITY, 68, 136, 255);
        private static readonly Color COLOR_MANA_BG = Color.FromArgb(HUD_OPACITY, 0, 20, 55);
        private static readonly Color COLOR_BORDER = Color.Black;
        private static readonly Color COLOR_TEXT = Color.White;
        private static readonly Color COLOR_TEXT_DARK = Color.FromArgb(255, 0, 0, 0);
        private static readonly Color COLOR_DRAG_BORDER = Color.Yellow;

        private static DxHud _hud;
        private static System.Windows.Forms.Timer _redrawTimer;
        private static bool _visible;
        private static int _lastRowCount;

        // Drag state
        private static bool _isHoldingControl;
        private static bool _isDragging;
        private static Point _dragStart;
        private static Point _dragBaseHudPos;
        private static bool _windowMessageHooked;

        public static bool IsVisible => _visible;

        public static void ToggleWindow()
        {
            try
            {
                if (_visible) Hide();
                else Show();
            }
            catch (Exception ex)
            {
                PluginCore.WriteToChat($"[VitalShare] Overlay toggle error: {ex.Message}");
            }
        }

        public static void Show()
        {
            try
            {
                if (_visible && _hud != null)
                {
                    _hud.Enabled = true;
                    return;
                }

                var rows = Math.Max(1, CountRows());
                var size = new Size(HUD_WIDTH, rows * ROW_SIZE + 5);
                var loc = new Point(
                    PluginSettings.Instance.VitalSharingOverlayX,
                    PluginSettings.Instance.VitalSharingOverlayY);
                _hud = new DxHud(loc, size, 0)
                {
                    Enabled = true,
                    Alpha = 255,
                };
                _visible = true;
                _lastRowCount = rows;

                if (_redrawTimer == null)
                {
                    _redrawTimer = new System.Windows.Forms.Timer();
                    _redrawTimer.Interval = 250; // 4 fps is plenty for vital bars
                    _redrawTimer.Tick += (s, e) => Render();
                }
                _redrawTimer.Start();

                HookWindowMessages();
                Render();
            }
            catch (Exception ex)
            {
                PluginCore.WriteToChat($"[VitalShare] Overlay show error: {ex.Message}");
            }
        }

        public static void Hide()
        {
            try
            {
                _redrawTimer?.Stop();
                UnhookWindowMessages();
                if (_hud != null)
                {
                    _hud.Enabled = false;
                    _hud.Dispose();
                    _hud = null;
                }
                _visible = false;
                _isDragging = false;
            }
            catch (Exception ex)
            {
                PluginCore.WriteToChat($"[VitalShare] Overlay hide error: {ex.Message}");
            }
        }

        // ---------- Window message / drag handling ----------

        private static void HookWindowMessages()
        {
            if (_windowMessageHooked) return;
            try
            {
                CoreManager.Current.WindowMessage += OnWindowMessage;
                _windowMessageHooked = true;
            }
            catch (Exception ex)
            {
                PluginCore.WriteToChat($"[VitalShare] Hook error: {ex.Message}");
            }
        }

        private static void UnhookWindowMessages()
        {
            if (!_windowMessageHooked) return;
            try
            {
                CoreManager.Current.WindowMessage -= OnWindowMessage;
            }
            catch { }
            _windowMessageHooked = false;
            _isHoldingControl = false;
            _isDragging = false;
        }

        // Window message constants (from winuser.h)
        private const int WM_KEYDOWN = 0x0100;
        private const int WM_KEYUP = 0x0101;
        private const int WM_MOUSEMOVE = 0x0200;
        private const int WM_LBUTTONDOWN = 0x0201;
        private const int WM_LBUTTONUP = 0x0202;
        private const int VK_CONTROL = 0x11;

        private const int CLOSE_BTN_SIZE = 14;

        private static Rectangle HudRect()
        {
            if (_hud == null) return Rectangle.Empty;
            return new Rectangle(_hud.Location.X, _hud.Location.Y, HUD_WIDTH, Math.Max(1, _lastRowCount) * ROW_SIZE + 5);
        }

        /// <summary>Hud-local rect for the close button (only used while Ctrl is held).</summary>
        private static Rectangle CloseBtnRectLocal()
        {
            return new Rectangle(HUD_WIDTH - CLOSE_BTN_SIZE - 2, 2, CLOSE_BTN_SIZE, CLOSE_BTN_SIZE);
        }

        /// <summary>Screen-space rect for the close button at the current HUD location.</summary>
        private static Rectangle CloseBtnRectScreen()
        {
            if (_hud == null) return Rectangle.Empty;
            var local = CloseBtnRectLocal();
            return new Rectangle(_hud.Location.X + local.X, _hud.Location.Y + local.Y, local.Width, local.Height);
        }

        private static void OnWindowMessage(object sender, WindowMessageEventArgs e)
        {
            try
            {
                if (!_visible || _hud == null) return;

                switch (e.Msg)
                {
                    case WM_KEYDOWN:
                        if ((int)e.WParam == VK_CONTROL) _isHoldingControl = true;
                        return;
                    case WM_KEYUP:
                        if ((int)e.WParam == VK_CONTROL)
                        {
                            _isHoldingControl = false;
                            _isDragging = false;
                        }
                        return;
                }

                if (!_isHoldingControl) return;

                var mouse = new Point(e.LParam);

                switch (e.Msg)
                {
                    case WM_LBUTTONDOWN:
                        // Ctrl+click the close button in the top-right corner to close.
                        if (CloseBtnRectScreen().Contains(mouse))
                        {
                            e.Eat = true;
                            Hide();
                            break;
                        }
                        if (HudRect().Contains(mouse))
                        {
                            _isDragging = true;
                            _dragStart = mouse;
                            _dragBaseHudPos = _hud.Location;
                            e.Eat = true;
                        }
                        break;
                    case WM_MOUSEMOVE:
                        if (_isDragging)
                        {
                            int nx = _dragBaseHudPos.X + (mouse.X - _dragStart.X);
                            int ny = _dragBaseHudPos.Y + (mouse.Y - _dragStart.Y);
                            _hud.Location = new Point(nx, ny);
                            e.Eat = true;
                        }
                        break;
                    case WM_LBUTTONUP:
                        if (_isDragging)
                        {
                            _isDragging = false;
                            PluginSettings.Instance.VitalSharingOverlayX = _hud.Location.X;
                            PluginSettings.Instance.VitalSharingOverlayY = _hud.Location.Y;
                            e.Eat = true;
                        }
                        break;
                }
            }
            catch (Exception ex)
            {
                PluginCore.WriteToChat($"[VitalShare] WindowMessage error: {ex.Message}");
            }
        }

        // ---------- Row enumeration ----------

        private static int CountRows()
        {
            int n = VitalSharingTracker.GetPeerSnapshots()?.Length ?? 0;
            return n + 1; // self row
        }

        private static List<VitalSharingTracker.PeerSnapshot> BuildRows()
        {
            var rows = new List<VitalSharingTracker.PeerSnapshot>();
            var self = VitalSharingTracker.GetSelfSnapshot();
            if (self != null) rows.Add(self);

            var peers = VitalSharingTracker.GetPeerSnapshots();
            if (peers != null)
            {
                Array.Sort(peers, (a, b) =>
                    string.Compare(a.CharacterName, b.CharacterName, StringComparison.OrdinalIgnoreCase));
                foreach (var p in peers)
                {
                    if (self != null && string.Equals(p.CharacterName, self.CharacterName, StringComparison.OrdinalIgnoreCase))
                        continue;
                    rows.Add(p);
                }
            }
            return rows;
        }

        // ---------- Rendering ----------

        private static void Render()
        {
            try
            {
                if (_hud == null || !_visible) return;

                var rows = BuildRows();
                if (rows.Count == 0) return;

                if (rows.Count != _lastRowCount)
                {
                    _lastRowCount = rows.Count;
                    var loc = _hud.Location;
                    _hud.Dispose();
                    _hud = new DxHud(loc, new Size(HUD_WIDTH, rows.Count * ROW_SIZE + 5), 0)
                    {
                        Enabled = true,
                        Alpha = 255,
                    };
                }

                var tex = _hud.Texture;
                tex.BeginRender();
                try
                {
                    tex.Fill(new Rectangle(0, 0, HUD_WIDTH, rows.Count * ROW_SIZE + 5), Color.Transparent);

                    int offset = 0;
                    foreach (var p in rows)
                    {
                        DrawRowBackground(tex, offset, p);
                        offset += ROW_SIZE;
                    }

                    // Direction arrows (everyone except the self row)
                    offset = 0;
                    for (int i = 0; i < rows.Count; i++)
                    {
                        if (i > 0) DrawDirectionArrow(tex, offset, rows[i]);
                        offset += ROW_SIZE;
                    }

                    // Drag-highlight border + close button when Ctrl is held
                    if (_isHoldingControl)
                    {
                        var w = HUD_WIDTH;
                        var h = rows.Count * ROW_SIZE + 5;
                        tex.DrawLine(new PointF(0, 0), new PointF(w - 1, 0), COLOR_DRAG_BORDER, 1);
                        tex.DrawLine(new PointF(w - 1, 0), new PointF(w - 1, h - 1), COLOR_DRAG_BORDER, 1);
                        tex.DrawLine(new PointF(w - 1, h - 1), new PointF(0, h - 1), COLOR_DRAG_BORDER, 1);
                        tex.DrawLine(new PointF(0, h - 1), new PointF(0, 0), COLOR_DRAG_BORDER, 1);

                        // Close button: red box with a white X (top-right corner)
                        var btn = CloseBtnRectLocal();
                        tex.Fill(btn, Color.FromArgb(220, 160, 20, 20));
                        tex.DrawLine(new PointF(btn.Left, btn.Top), new PointF(btn.Right - 1, btn.Top), Color.White, 1);
                        tex.DrawLine(new PointF(btn.Right - 1, btn.Top), new PointF(btn.Right - 1, btn.Bottom - 1), Color.White, 1);
                        tex.DrawLine(new PointF(btn.Right - 1, btn.Bottom - 1), new PointF(btn.Left, btn.Bottom - 1), Color.White, 1);
                        tex.DrawLine(new PointF(btn.Left, btn.Bottom - 1), new PointF(btn.Left, btn.Top), Color.White, 1);
                        // X glyph
                        tex.DrawLine(new PointF(btn.Left + 3, btn.Top + 3),
                                     new PointF(btn.Right - 4, btn.Bottom - 4), Color.White, 2);
                        tex.DrawLine(new PointF(btn.Right - 4, btn.Top + 3),
                                     new PointF(btn.Left + 3, btn.Bottom - 4), Color.White, 2);
                    }

                    // Text passes
                    tex.BeginText("Arial", 7, 100, false, 1, 255);
                    try
                    {
                        offset = 0;
                        for (int i = 0; i < rows.Count; i++)
                        {
                            DrawRowText(tex, offset, rows[i], isSelf: i == 0);
                            offset += ROW_SIZE;
                        }
                    }
                    finally { tex.EndText(); }

                    tex.BeginText("Arial", 5, 100, false);
                    try
                    {
                        offset = 0;
                        foreach (var p in rows)
                        {
                            DrawRowTextSmall(tex, offset, p);
                            offset += ROW_SIZE;
                        }
                    }
                    finally { tex.EndText(); }
                }
                finally
                {
                    tex.EndRender();
                }
            }
            catch (Exception ex)
            {
                PluginCore.WriteToChat($"[VitalShare] Overlay render error: {ex.Message}");
            }
        }

        private static double Ratio(int cur, int max)
        {
            if (max <= 0) return 0;
            var r = (double)cur / max;
            if (r < 0) return 0;
            if (r > 1) return 1;
            return r;
        }

        private static void DrawRowBackground(DxTexture tex, int offset, VitalSharingTracker.PeerSnapshot p)
        {
            var area = new Rectangle(1, offset + 1, CHAR_NAME_WIDTH, ROW_SIZE - 2);
            double hp = Ratio(p.CurrentHealth, p.MaxHealth);
            double sta = Ratio(p.CurrentStamina, p.MaxStamina);
            double mana = Ratio(p.CurrentMana, p.MaxMana);

            // Health bar (full row width, top half)
            tex.Fill(new Rectangle(area.Left, area.Top, CHAR_NAME_WIDTH, HEALTH_BAR_HEIGHT), COLOR_HP_BG);
            tex.Fill(new Rectangle(area.Left, area.Top, (int)(CHAR_NAME_WIDTH * hp), HEALTH_BAR_HEIGHT), COLOR_HP_FILL);

            // Stamina bar (left half, bottom)
            int halfW = CHAR_NAME_WIDTH / 2;
            int bottomH = area.Height - HEALTH_BAR_HEIGHT;
            tex.Fill(new Rectangle(area.Left, area.Top + HEALTH_BAR_HEIGHT, halfW, bottomH), COLOR_STA_BG);
            tex.Fill(new Rectangle(area.Left, area.Top + HEALTH_BAR_HEIGHT, (int)(halfW * sta), bottomH), COLOR_STA_FILL);

            // Mana bar (right half, bottom)
            tex.Fill(new Rectangle(area.Left + halfW, area.Top + HEALTH_BAR_HEIGHT, halfW, bottomH), COLOR_MANA_BG);
            tex.Fill(new Rectangle(area.Left + halfW, area.Top + HEALTH_BAR_HEIGHT, (int)(halfW * mana), bottomH), COLOR_MANA_FILL);

            // Outer border around the vital area
            var tl = new PointF(area.Left, area.Top);
            var tr = new PointF(area.Left + CHAR_NAME_WIDTH, area.Top);
            var br = new PointF(area.Left + CHAR_NAME_WIDTH, area.Top + area.Height);
            var bl = new PointF(area.Left, area.Top + area.Height);
            tex.DrawLine(tl, tr, COLOR_BORDER, 1);
            tex.DrawLine(tr, br, COLOR_BORDER, 1);
            tex.DrawLine(br, bl, COLOR_BORDER, 1);
            tex.DrawLine(bl, tl, COLOR_BORDER, 1);
        }

        // Draw a small triangle pointing toward the peer's position relative
        // to the local character, tinted red by distance.
        private static void DrawDirectionArrow(DxTexture tex, int offset, VitalSharingTracker.PeerSnapshot p)
        {
            if (!p.HasPosition) return;

            double selfEW, selfNS;
            double myHeading;
            try
            {
                var me = Coordinates.Me;
                selfEW = me.EW;
                selfNS = me.NS;
                myHeading = CoreManager.Current.Actions.Heading;
            }
            catch { return; }

            double dx = p.EW - selfEW;
            double dy = p.NS - selfNS;
            double distance = Math.Sqrt(dx * dx + dy * dy) * 240.0; // 1 coord = 240m
            if (distance < 0.1) return;

            // Angle from north (positive Y), clockwise. atan2(dx, dy) gives
            // 0 when target is due north, rotating east positively.
            double worldAngleDeg = Math.Atan2(dx, dy) * 180.0 / Math.PI;
            double relative = worldAngleDeg - myHeading;
            double rad = relative * Math.PI / 180.0;

            // Arrow anchored in the small square just right of the vital area
            float cx = CHAR_NAME_WIDTH + PADDING + ROW_SIZE / 2f;
            float cy = offset + ROW_SIZE / 2f;
            float size = ROW_SIZE * 0.35f;

            // Tip in direction of `rad`, base corners 140° behind
            double tipX = cx + Math.Sin(rad) * size;
            double tipY = cy - Math.Cos(rad) * size;
            double leftRad = rad + (140.0 * Math.PI / 180.0);
            double rightRad = rad - (140.0 * Math.PI / 180.0);
            double leftX = cx + Math.Sin(leftRad) * size * 0.75;
            double leftY = cy - Math.Cos(leftRad) * size * 0.75;
            double rightX = cx + Math.Sin(rightRad) * size * 0.75;
            double rightY = cy - Math.Cos(rightRad) * size * 0.75;

            int fade = (int)Math.Max(0, Math.Min(255, 255 - distance * 2));
            var tint = Color.FromArgb(255, 255, fade, fade);

            tex.DrawLine(new PointF((float)tipX, (float)tipY), new PointF((float)leftX, (float)leftY), tint, 2);
            tex.DrawLine(new PointF((float)leftX, (float)leftY), new PointF((float)rightX, (float)rightY), tint, 2);
            tex.DrawLine(new PointF((float)rightX, (float)rightY), new PointF((float)tipX, (float)tipY), tint, 2);
        }

        private static string FormatDistance(double meters)
        {
            if (meters < 1000) return meters.ToString("N0");
            if (meters < 10000) return (meters / 1000).ToString("N1") + "k";
            return (meters / 1000).ToString("N0") + "k";
        }

        private static void DrawRowText(DxTexture tex, int offset, VitalSharingTracker.PeerSnapshot p, bool isSelf)
        {
            var area = new Rectangle(0, offset, HUD_WIDTH, ROW_SIZE);
            // name (top-left)
            tex.WriteText(p.CharacterName, COLOR_TEXT, WriteTextFormats.SingleLine,
                new Rectangle(area.X + 4, area.Y + 1, CHAR_NAME_WIDTH, ROW_SIZE));
            // hp % (top-right of name area)
            int hpPct = p.MaxHealth > 0 ? (int)Math.Round(100.0 * p.CurrentHealth / p.MaxHealth) : 0;
            tex.WriteText($"{hpPct}%", COLOR_TEXT, WriteTextFormats.Right,
                new Rectangle(area.X + 2, area.Y + 1, CHAR_NAME_WIDTH - 4, ROW_SIZE));

            // range text to the right of the arrow square
            if (!isSelf)
            {
                if (p.HasPosition)
                {
                    double selfEW, selfNS;
                    try
                    {
                        var me = Coordinates.Me;
                        selfEW = me.EW; selfNS = me.NS;
                    }
                    catch { return; }
                    double dx = p.EW - selfEW;
                    double dy = p.NS - selfNS;
                    double distance = Math.Sqrt(dx * dx + dy * dy) * 240.0;
                    int fade = (int)Math.Max(0, Math.Min(255, 255 - distance * 2));
                    var tint = Color.FromArgb(255, 255, fade, fade);
                    tex.WriteText(FormatDistance(distance), tint, WriteTextFormats.SingleLine,
                        new Rectangle(CHAR_NAME_WIDTH + PADDING + ROW_SIZE, offset + 5, RANGE_WIDTH, 12));
                }
                else
                {
                    tex.WriteText("??", Color.Gray, WriteTextFormats.SingleLine,
                        new Rectangle(CHAR_NAME_WIDTH + PADDING + ROW_SIZE, offset + 5, RANGE_WIDTH, 12));
                }
            }
        }

        private static void DrawRowTextSmall(DxTexture tex, int offset, VitalSharingTracker.PeerSnapshot p)
        {
            var area = new Rectangle(0, offset, HUD_WIDTH, ROW_SIZE);
            int staPct = p.MaxStamina > 0 ? (int)Math.Round(100.0 * p.CurrentStamina / p.MaxStamina) : 0;
            int manaPct = p.MaxMana > 0 ? (int)Math.Round(100.0 * p.CurrentMana / p.MaxMana) : 0;
            // stamina % bottom-left
            tex.WriteText($"{staPct}%", COLOR_TEXT_DARK, WriteTextFormats.SingleLine,
                new Rectangle(area.X + 4, area.Y + HEALTH_BAR_HEIGHT, CHAR_NAME_WIDTH, ROW_SIZE));
            // mana % bottom-right-half
            tex.WriteText($"{manaPct}%", COLOR_TEXT_DARK, WriteTextFormats.SingleLine,
                new Rectangle(area.X + 4 + (CHAR_NAME_WIDTH / 2), area.Y + HEALTH_BAR_HEIGHT, CHAR_NAME_WIDTH, ROW_SIZE));
        }
    }
}
