using Quasar.Common.Messages;
using System;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;

namespace Quasar.Server.Forms
{
    // 2-octave piano (C4–B5).  Mouse-click or keyboard to play.
    // Keyboard layout (standard virtual piano):
    //   White (lower): Z X C V B N M  Q W E R T Y U
    //   Black  (lower): S D   G H J    2 3   5 6 7
    public class FrmPiano : Form
    {
        private readonly Action<DoPlayNote> _send;

        // ── Note data ────────────────────────────────────────────────────────
        private static readonly string[] _names  = { "C4","C#4","D4","D#4","E4","F4","F#4","G4","G#4","A4","A#4","B4","C5","C#5","D5","D#5","E5","F5","F#5","G5","G#5","A5","A#5","B5" };
        private static readonly int[]    _freqs  = { 262, 277, 294, 311, 330, 349, 370, 392, 415, 440, 466, 494, 523, 554, 587, 622, 659, 698, 740, 784, 831, 880, 932, 988 };
        private static readonly bool[]   _black  = { false,true,false,true,false,false,true,false,true,false,true,false, false,true,false,true,false,false,true,false,true,false,true,false };

        // Computer-key mapping (index aligns with _names)
        private static readonly Keys[] _keyMap =
        {
            Keys.Z, Keys.S, Keys.X, Keys.D, Keys.C, Keys.V, Keys.G, Keys.B, Keys.H, Keys.N, Keys.J, Keys.M,
            Keys.Q, Keys.D2, Keys.W, Keys.D3, Keys.E, Keys.R, Keys.D5, Keys.T, Keys.D6, Keys.Y, Keys.D7, Keys.U
        };

        // ── Layout constants ─────────────────────────────────────────────────
        private const int WW = 38, WH = 150, BW = 22, BH = 92, PAD = 10, TOP = 32;

        private readonly Rectangle[] _rects = new Rectangle[24];
        private int _pressedIdx = -1;
        private readonly Stopwatch _slideThrottle = Stopwatch.StartNew();

        // Cached brushes
        private static readonly Color _pressedWhite = Color.FromArgb(100, 180, 255);
        private static readonly Color _pressedBlack = Color.FromArgb(60, 120, 220);
        private static readonly Color _bgColor       = Color.FromArgb(28, 28, 28);

        public FrmPiano(Action<DoPlayNote> send)
        {
            _send = send;

            Text             = "Piano";
            FormBorderStyle  = FormBorderStyle.FixedSingle;
            MaximizeBox      = false;
            StartPosition    = FormStartPosition.CenterScreen;
            KeyPreview       = true;
            DoubleBuffered   = true;
            ClientSize       = new Size(PAD * 2 + 14 * WW, TOP + WH + 20);
            BackColor        = _bgColor;

            BuildRects();

            Paint     += OnPaint;
            MouseDown += OnMouseDown;
            MouseUp   += (s, a) => { _pressedIdx = -1; Invalidate(); };
            MouseMove += OnMouseMove;
            KeyDown   += OnKeyDown;
            KeyUp     += (s, a) => { _pressedIdx = -1; Invalidate(); };
        }

        // ── Geometry ─────────────────────────────────────────────────────────

        private void BuildRects()
        {
            // White-key index for each note (black keys get -1)
            int[] whiteIdx = new int[24];
            int w = 0;
            for (int i = 0; i < 24; i++)
                whiteIdx[i] = _black[i] ? -1 : w++;

            // White key rects
            for (int i = 0; i < 24; i++)
                if (!_black[i])
                    _rects[i] = new Rectangle(PAD + whiteIdx[i] * WW, TOP, WW, WH);

            // Black key rects: centered on the gap between the two adjacent white keys
            // Left white-key note index for each black note
            int[] leftWhite = { 0, 2, 5, 7, 9, 12, 14, 17, 19, 21 };
            int[] blackNote = { 1, 3, 6, 8, 10, 13, 15, 18, 20, 22 };
            for (int b = 0; b < blackNote.Length; b++)
            {
                int lw = whiteIdx[leftWhite[b]];
                _rects[blackNote[b]] = new Rectangle(PAD + (lw + 1) * WW - BW / 2, TOP, BW, BH);
            }
        }

        // ── Painting ─────────────────────────────────────────────────────────

        private void OnPaint(object sender, PaintEventArgs e)
        {
            Graphics g = e.Graphics;
            g.SmoothingMode = SmoothingMode.AntiAlias;

            // Header label
            using (var f = new Font("Segoe UI", 9.5f, FontStyle.Bold))
                g.DrawString("Piano  ·  Z X C V B N M | Q W E R T Y U", f, Brushes.Silver, PAD, 6);

            // White keys first
            for (int i = 0; i < 24; i++)
            {
                if (_black[i]) continue;
                bool pressed = _pressedIdx == i;
                var r = _rects[i];

                using (var br = new SolidBrush(pressed ? _pressedWhite : Color.White))
                    g.FillRectangle(br, r);

                g.DrawRectangle(Pens.Black, r);

                // Key label
                using (var f = new Font("Segoe UI", 6.5f))
                {
                    char ch = KeyChar(i);
                    g.DrawString(ch.ToString(), f, Brushes.DimGray,
                        r.X + (WW - 8) / 2, r.Bottom - 17);
                }
            }

            // Black keys on top
            for (int i = 0; i < 24; i++)
            {
                if (!_black[i]) continue;
                bool pressed = _pressedIdx == i;
                var r = _rects[i];

                using (var br = new SolidBrush(pressed ? _pressedBlack : Color.FromArgb(28, 28, 28)))
                    g.FillRectangle(br, r);

                // Subtle highlight gradient on un-pressed black key
                if (!pressed)
                {
                    using (var lgb = new LinearGradientBrush(r, Color.FromArgb(80, 80, 80), Color.FromArgb(28, 28, 28), LinearGradientMode.Horizontal))
                        g.FillRectangle(lgb, new Rectangle(r.X, r.Y, 5, r.Height));
                }

                g.DrawRectangle(Pens.Black, r);

                // Key label
                using (var f = new Font("Segoe UI", 5.5f))
                {
                    char ch = KeyChar(i);
                    g.DrawString(ch.ToString(), f, Brushes.DimGray,
                        r.X + 4, r.Bottom - 14);
                }
            }
        }

        private char KeyChar(int idx)
        {
            var k = _keyMap[idx];
            if (k >= Keys.D0 && k <= Keys.D9) return (char)('0' + (k - Keys.D0));
            return (char)k; // letter keys: Keys.A = 65 = 'A'
        }

        // ── Input ─────────────────────────────────────────────────────────────

        private void OnMouseDown(object sender, MouseEventArgs e)
        {
            // Black keys sit on top — test them first
            for (int i = 0; i < 24; i++)
                if (_black[i] && _rects[i].Contains(e.Location)) { Press(i); return; }
            for (int i = 0; i < 24; i++)
                if (!_black[i] && _rects[i].Contains(e.Location)) { Press(i); return; }
        }

        private void OnMouseMove(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.None) return;
            // Throttle slide to avoid flooding the connection
            if (_slideThrottle.ElapsedMilliseconds < 80) return;
            for (int i = 0; i < 24; i++)
                if (_black[i] && _rects[i].Contains(e.Location)) { if (_pressedIdx != i) { _slideThrottle.Restart(); Press(i); } return; }
            for (int i = 0; i < 24; i++)
                if (!_black[i] && _rects[i].Contains(e.Location)) { if (_pressedIdx != i) { _slideThrottle.Restart(); Press(i); } return; }
        }

        private void OnKeyDown(object sender, KeyEventArgs e)
        {
            for (int i = 0; i < _keyMap.Length; i++)
                if (_keyMap[i] == e.KeyCode) { if (_pressedIdx != i) Press(i); return; }
        }

        private void Press(int idx)
        {
            _pressedIdx = idx;
            Invalidate();
            _send(new DoPlayNote { Frequency = _freqs[idx], DurationMs = 1000 });
        }
    }
}
