using System;
using System.IO.Ports;
using System.Linq;
using System.Management;

namespace Elwig.Helpers {
    public sealed class SerialPortWatcher : IDisposable {

        private readonly ManagementEventWatcher _deviceArrivalWatcher;
        private readonly ManagementEventWatcher _deviceRemovalWatcher;

        private string[] _knownPorts;

        public event EventHandler<string>? SerialPortConnected;
        public event EventHandler<string>? SerialPortDisconnected;

        public SerialPortWatcher() {
            _knownPorts = SerialPort.GetPortNames();
            _deviceArrivalWatcher = new ManagementEventWatcher("SELECT * FROM Win32_DeviceChangeEvent WHERE EventType = 2");
            _deviceArrivalWatcher.EventArrived += OnDeviceArrived;
            _deviceRemovalWatcher = new ManagementEventWatcher("SELECT * FROM Win32_DeviceChangeEvent WHERE EventType = 3");
            _deviceRemovalWatcher.EventArrived += OnDeviceRemoved;
            _deviceArrivalWatcher.Start();
            _deviceRemovalWatcher.Start();

        }

        private void OnDeviceArrived(object sender, EventArrivedEventArgs evt) {
            App.MainDispatcher.Invoke(() => {
                // "synchronized"
                string[] currentPorts = SerialPort.GetPortNames();
                var newPorts = currentPorts.Except(_knownPorts).ToArray();
                foreach (var port in newPorts)
                    SerialPortConnected?.Invoke(this, port);
                _knownPorts = currentPorts;
            });
        }

        private void OnDeviceRemoved(object sender, EventArrivedEventArgs evt) {
            App.MainDispatcher.Invoke(() => {
                // "synchronized"
                string[] currentPorts = SerialPort.GetPortNames();
                var removedPorts = _knownPorts.Except(currentPorts).ToArray();
                foreach (var port in removedPorts)
                    SerialPortDisconnected?.Invoke(this, port);
                _knownPorts = currentPorts;
            });
        }

        public void Dispose() {
            try {
                _deviceArrivalWatcher?.Stop();
                _deviceRemovalWatcher?.Stop();
            } finally {
                _deviceArrivalWatcher?.Dispose();
                _deviceRemovalWatcher?.Dispose();
            }
        }
    }
}
