using Quasar.Common.Messages;
using Quasar.Common.Networking;
using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Runtime.InteropServices;
using System.Threading;
using System.Windows.Forms;

namespace Quasar.Client.Messages
{
    public class EyesHandler : IMessageProcessor
    {
        private Thread _thread;
        private volatile bool _running;
        private EyesForm _form;

        public bool CanExecute(IMessage message) => message is DoStartEyes || message is DoStopEyes;
        public bool CanExecuteFrom(ISender sender) => true;

        public void Execute(ISender sender, IMessage message)
        {
            if (message is DoStartEyes) Start();
            else if (message is DoStopEyes) Stop();
        }

        private void Start()
        {
            if (_running) return;
            _running = true;
            _thread = new Thread(RunEyes) { IsBackground = true };
            _thread.SetApartmentState(ApartmentState.STA);
            _thread.Start();
        }

        private void Stop()
        {
            _running = false;
            try
            {
                var f = _form;
                if (f != null && !f.IsDisposed)
                    f.Invoke(new Action(() => { if (!f.IsDisposed) f.Close(); }));
            }
            catch { }
        }

        private void RunEyes()
        {
            _form = new EyesForm();
            Application.Run(_form);
            _form = null;
            _running = false;
        }
    }

    internal class EyesForm : Form
    {
        [DllImport("user32.dll")] private static extern bool GetCursorPos(out POINT pt);
        [DllImport("user32.dll")] private static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);
        private static readonly IntPtr HWND_TOPMOST = new IntPtr(-1);
        private const uint SWP_NOMOVE = 0x0002, SWP_NOSIZE = 0x0001, SWP_NOACTIVATE = 0x0010;
        [StructLayout(LayoutKind.Sequential)] private struct POINT { public int X, Y; }

        private readonly System.Windows.Forms.Timer _timer;
        private readonly Random _rng = new Random();

        // Animation state
        private bool _blinking;
        private int _ticksSinceBlink;
        private bool _twitching;
        private float _twitchDX, _twitchDY;
        private int _pulseFrame;

        // Eye geometry
        private const float CX1 = 72f, CX2 = 216f, CY = 72f;
        private const float RX = 58f;           // half-width
        private const float IRIS_R = 24f;

        public EyesForm()
        {
            FormBorderStyle = FormBorderStyle.None;
            TopMost = true;
            ShowInTaskbar = false;
            BackColor = Color.Lime;
            TransparencyKey = Color.Lime;
            StartPosition = FormStartPosition.Manual;
            Size = new Size(300, 150);
            DoubleBuffered = true;

            var screen = Screen.PrimaryScreen.WorkingArea;
            Location = new Point(screen.Right - 318, screen.Bottom - 168);

            _timer = new System.Windows.Forms.Timer { Interval = 30 };
            _timer.Tick += OnTick;
            _timer.Start();
        }

        private void OnTick(object sender, EventArgs e)
        {
            _pulseFrame++;
            _ticksSinceBlink++;

            if (!_blinking && _ticksSinceBlink > 90 && _rng.Next(60) == 0)
            {
                _blinking = true;
                _ticksSinceBlink = 0;
                var t = new System.Windows.Forms.Timer { Interval = 180 };
                t.Tick += (s, _) => { _blinking = false; t.Stop(); t.Dispose(); };
                t.Start();
            }

            if (!_twitching && _rng.Next(90) == 0)
            {
                _twitching = true;
                _twitchDX = (float)(_rng.NextDouble() * 18 - 9);
                _twitchDY = (float)(_rng.NextDouble() * 10 - 5);
                var t = new System.Windows.Forms.Timer { Interval = 60 };
                t.Tick += (s, _) => { _twitching = false; t.Stop(); t.Dispose(); };
                t.Start();
            }

            // Re-assert topmost every tick so fullscreen apps can't push us under
            SetWindowPos(Handle, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
            Invalidate();
        }

        // Realistic eyelid outline — natural bezier curves
        private static GraphicsPath MakeEyePath(float cx, float cy)
        {
            var path = new GraphicsPath();
            // Inner (left) corner sits slightly below center; outer (right) slightly above
            float lx = cx - RX, rx = cx + RX;
            float innerY = cy + 4f, outerY = cy;

            // Upper lid: fast rise near inner corner, peak above cx, sweeps to outer corner
            path.AddBezier(
                lx,           innerY,
                cx - RX * 0.3f, cy - 50f,
                cx + RX * 0.1f, cy - 52f,
                rx,           outerY);

            // Lower lid: gentle drop from outer corner, flatter belly, rises to inner corner
            path.AddBezier(
                rx,           outerY,
                cx + RX * 0.4f, cy + 22f,
                cx - RX * 0.2f, cy + 24f,
                lx,           innerY);

            path.CloseFigure();
            return path;
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            var g = e.Graphics;
            g.SmoothingMode = SmoothingMode.AntiAlias;
            g.InterpolationMode = InterpolationMode.HighQualityBicubic;

            GetCursorPos(out POINT cursor);
            var origin = PointToScreen(Point.Empty);

            DrawEye(g, CX1, CY, cursor, origin);
            DrawEye(g, CX2, CY, cursor, origin);
        }

        private void DrawEye(Graphics g, float cx, float cy, POINT cursor, Point origin)
        {
            using (var eyePath = MakeEyePath(cx, cy))
            {
                var bounds = eyePath.GetBounds();

                // ── Sclera — slightly cool white ────────────────────────────────
                using (var b = new SolidBrush(Color.FromArgb(242, 242, 248)))
                    g.FillPath(b, eyePath);

                // ── Blood veins — thin red beziers across the white ─────────────
                DrawVeins(g, cx, cy, eyePath);

                if (!_blinking)
                {
                    // ── Cursor tracking ──────────────────────────────────────────
                    float dx = cursor.X - (origin.X + cx);
                    float dy = cursor.Y - (origin.Y + cy);
                    if (_twitching) { dx += _twitchDX; dy += _twitchDY; }

                    float dist = (float)Math.Sqrt(dx * dx + dy * dy);
                    float pulse = (float)Math.Sin(_pulseFrame * 0.04) * 1.5f;
                    float irisR = IRIS_R + pulse;
                    float pupilR = irisR * 0.68f; // huge dilated pupil = menacing
                    float maxTravel = irisR - pupilR - 1f;
                    float px = cx, py = cy;
                    if (dist > 0)
                    {
                        float ratio = Math.Min(dist, maxTravel) / dist;
                        px = cx + dx * ratio;
                        py = cy + dy * ratio;
                    }

                    // Clip iris/pupil to sclera shape
                    var clipState = g.Save();
                    g.SetClip(eyePath);

                    // Upper lid shadow — dark gradient bleeding down from top of lid
                    var lidShadowRect = new RectangleF(bounds.X, bounds.Y - 2, bounds.Width, bounds.Height * 0.55f);
                    using (var lgb = new LinearGradientBrush(
                        new PointF(0, bounds.Y),
                        new PointF(0, bounds.Y + bounds.Height * 0.55f),
                        Color.FromArgb(90, 20, 10, 5),
                        Color.FromArgb(0, 0, 0, 0)))
                        g.FillRectangle(lgb, lidShadowRect);

                    // Iris — dark forest green (realistic + eerie)
                    using (var b = new SolidBrush(Color.FromArgb(38, 68, 42)))
                        g.FillEllipse(b, px - irisR, py - irisR, irisR * 2, irisR * 2);

                    // Iris texture — radial spokes
                    using (var pen = new Pen(Color.FromArgb(80, 55, 90, 58), 1f))
                    {
                        for (int a = 0; a < 360; a += 10)
                        {
                            float rad = a * (float)Math.PI / 180f;
                            float cos = (float)Math.Cos(rad), sin = (float)Math.Sin(rad);
                            g.DrawLine(pen,
                                px + (pupilR + 1f) * cos, py + (pupilR + 1f) * sin,
                                px + (irisR - 1f) * cos,  py + (irisR - 1f) * sin);
                        }
                    }

                    // Dark limbal ring
                    using (var pen = new Pen(Color.FromArgb(14, 22, 14), 2f))
                        g.DrawEllipse(pen, px - irisR, py - irisR, irisR * 2, irisR * 2);

                    // Massive dilated pupil
                    g.FillEllipse(Brushes.Black, px - pupilR, py - pupilR, pupilR * 2, pupilR * 2);

                    g.Restore(clipState);

                    // Specular highlights — two dots, natural positions
                    g.FillEllipse(Brushes.White, px - irisR * 0.55f, py - irisR * 0.7f, 6f, 5f);
                    using (var b = new SolidBrush(Color.FromArgb(160, 255, 255, 255)))
                        g.FillEllipse(b, px + irisR * 0.2f, py + irisR * 0.1f, 3f, 3f);
                }

                // ── Eyelid fill on blink (dark skin tone) ───────────────────────
                if (_blinking)
                {
                    using (var b = new SolidBrush(Color.FromArgb(168, 130, 108)))
                        g.FillPath(b, eyePath);
                }

                // ── Upper eyelid outline — thick dark line ───────────────────────
                float lx = cx - RX, rx = cx + RX;
                float innerY = cy + 4f, outerY = cy;
                using (var pen = new Pen(Color.FromArgb(28, 16, 10), 2.5f))
                {
                    using (var upperPath = new GraphicsPath())
                    {
                        upperPath.AddBezier(
                            lx,           innerY,
                            cx - RX * 0.3f, cy - 50f,
                            cx + RX * 0.1f, cy - 52f,
                            rx,           outerY);
                        g.DrawPath(pen, upperPath);
                    }
                }

                // ── Lower eyelid outline — thinner ──────────────────────────────
                using (var pen = new Pen(Color.FromArgb(50, 30, 20), 1.5f))
                {
                    using (var lowerPath = new GraphicsPath())
                    {
                        lowerPath.AddBezier(
                            rx,           outerY,
                            cx + RX * 0.4f, cy + 22f,
                            cx - RX * 0.2f, cy + 24f,
                            lx,           innerY);
                        g.DrawPath(pen, lowerPath);
                    }
                }

                // ── Eyelashes — upper lid only ───────────────────────────────────
                DrawEyelashes(g, cx, cy);

                // ── Pink inner corner (caruncle) ─────────────────────────────────
                using (var b = new SolidBrush(Color.FromArgb(200, 195, 90, 85)))
                    g.FillEllipse(b, lx - 2f, cy - 3f, 10f, 8f);

                // ── Red lower waterline ──────────────────────────────────────────
                using (var pen = new Pen(Color.FromArgb(120, 180, 50, 50), 1.5f))
                {
                    using (var waterline = new GraphicsPath())
                    {
                        waterline.AddBezier(
                            rx - 4f,     outerY + 8f,
                            cx + RX * 0.3f, cy + 16f,
                            cx - RX * 0.15f, cy + 17f,
                            lx + 4f,     innerY + 6f);
                        g.DrawPath(pen, waterline);
                    }
                }
            }
        }

        private void DrawVeins(Graphics g, float cx, float cy, GraphicsPath clipPath)
        {
            var clipState = g.Save();
            g.SetClip(clipPath);

            // Each row: x1,y1, cx1,cy1, cx2,cy2, x2,y2  (bezier control points)
            float[][] veins = {
                new float[]{ cx-RX+3,  cy+2,  cx-RX+22, cy-6,   cx-IRIS_R-14, cy-4,  cx-IRIS_R-4, cy-2 },
                new float[]{ cx-RX+5,  cy+8,  cx-RX+18, cy+14,  cx-IRIS_R-10, cy+9,  cx-IRIS_R-3, cy+5 },
                new float[]{ cx-RX+8,  cy-6,  cx-RX+24, cy-16,  cx-IRIS_R-8,  cy-11, cx-IRIS_R-2, cy-7 },
                new float[]{ cx+RX-3,  cy+2,  cx+RX-20, cy-5,   cx+IRIS_R+12, cy-3,  cx+IRIS_R+3, cy-1 },
                new float[]{ cx+RX-6,  cy-8,  cx+RX-22, cy-14,  cx+IRIS_R+9,  cy-9,  cx+IRIS_R+2, cy-5 },
                new float[]{ cx-6,     cy-50, cx-4,     cy-30,  cx-2,         cy-IRIS_R-8, cx-1, cy-IRIS_R-2 },
            };

            using (var pen = new Pen(Color.FromArgb(190, 185, 25, 25), 1f))
            {
                foreach (var v in veins)
                    g.DrawBezier(pen, v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7]);
            }

            using (var pen = new Pen(Color.FromArgb(110, 185, 25, 25), 0.8f))
            {
                g.DrawBezier(pen, cx-RX+14, cy+5,  cx-RX+28, cy+10, cx-IRIS_R-6, cy+12, cx-IRIS_R-1, cy+8);
                g.DrawBezier(pen, cx+RX-14, cy-3,  cx+RX-28, cy-9,  cx+IRIS_R+5, cy-11, cx+IRIS_R+1, cy-7);
            }

            g.Restore(clipState);
        }

        private static void DrawEyelashes(Graphics g, float cx, float cy)
        {
            float lx = cx - RX, rx = cx + RX;
            float innerY = cy + 4f, outerY = cy;

            const int count = 12;
            using (var pen = new Pen(Color.FromArgb(220, 18, 10, 6), 1.5f))
            {
                for (int i = 0; i < count; i++)
                {
                    float t = (float)i / (count - 1);
                    // Sample point along the upper lid bezier (De Casteljau t)
                    float it = 1f - t;
                    float bx = it*it*it*lx         + 3*it*it*t*(cx - RX*0.3f) + 3*it*t*t*(cx + RX*0.1f) + t*t*t*rx;
                    float by = it*it*it*innerY      + 3*it*it*t*(cy - 50f)     + 3*it*t*t*(cy - 52f)     + t*t*t*outerY;

                    // Tangent direction at t (derivative of cubic bezier)
                    float tbx = 3*(it*it*(cx - RX*0.3f - lx) + 2*it*t*(cx + RX*0.1f - (cx - RX*0.3f)) + t*t*(rx - (cx + RX*0.1f)));
                    float tby = 3*(it*it*(cy - 50f - innerY)  + 2*it*t*(cy - 52f - (cy - 50f))          + t*t*(outerY - (cy - 52f)));

                    // Normal pointing outward (away from eye center = upward here)
                    float len = (float)Math.Sqrt(tbx*tbx + tby*tby);
                    if (len < 0.001f) continue;
                    float nx = -tby / len, ny = tbx / len;

                    // Vary lash length — longer in middle
                    float mid = Math.Abs(t - 0.45f) * 2f;
                    float lashLen = 14f - mid * 6f;

                    // Slight outward curl
                    float curlX = nx * lashLen + ny * lashLen * 0.18f;
                    float curlY = ny * lashLen - nx * lashLen * 0.18f;

                    g.DrawLine(pen, bx, by, bx + curlX, by + curlY);
                }
            }
        }

        protected override void Dispose(bool disposing)
        {
            if (disposing) _timer?.Dispose();
            base.Dispose(disposing);
        }
    }
}
