using System.Reactive.Subjects;
using HidLibrary;

namespace HidSliders.Hid
{
    public class HidInterface
    {
        private IHidDevice? _device;
        private Subject<int[]> _sliderUpdate = new();
        private Subject<List<MixerButton>> _buttonsUpdate = new();

        public IObservable<int[]> SliderObservable => _sliderUpdate;
        public IObservable<List<MixerButton>> ButtonsObservable => _buttonsUpdate;

        public bool ConnectDevice(int vendorId, int devId)
        {
            var enumerator = new HidEnumerator();
            IEnumerable<IHidDevice> hidDevices = enumerator.Enumerate(vendorId, [devId]);

            _device = hidDevices.FirstOrDefault();

            if (_device is { IsConnected: true })
            {
                _device.OpenDevice();
                _device.MonitorDeviceEvents = true;

                _device.ReadReport(OnReport);
                _device.Removed += DeviceOnRemoved;
                _device.Inserted += DeviceOnInserted;
            }

            return _device?.IsConnected ?? false;
        }

        private void DeviceOnInserted()
        {
            _sliderUpdate = new Subject<int[]>();
            _buttonsUpdate = new Subject<List<MixerButton>>();
        }

        private void DeviceOnRemoved()
        {
            _sliderUpdate.OnCompleted();
            _buttonsUpdate.OnCompleted();
        }

        private void ReadSliders(byte[] reportSliderBytes)
        {
            var reportChunk = new byte[4];
            var sliderValues = new int[5];
            for (int i = 0; i < 5; i++)
            {
                Array.Copy(reportSliderBytes, i * 2, reportChunk, 0, 2);
                sliderValues[i] = BitConverter.ToInt32(reportChunk);
            }
            _sliderUpdate.OnNext(sliderValues);
        }

        private void ReadButtons(byte[] reportButtonBytes)
        {
            if (reportButtonBytes.Length != 8)
            {
                throw new ArgumentException("incoming report data was not padded to 8 bytes");
            }
            var buttonLong = BitConverter.ToInt64(reportButtonBytes);
            _buttonsUpdate.OnNext(MixerButtonHelper.GetSetFlags(buttonLong));
        }

        private void ReadReport(byte[] reportBytes)
        {
            var buttons = new byte[8];
            var sliders = new byte[10];
            Array.Copy(reportBytes, 0, buttons, 0, 6);
            Array.Copy(reportBytes, 6, sliders, 0, 10);
            ReadSliders(sliders);
            ReadButtons(buttons);
        }

        private void OnReport(HidReport report)
        {
            if (!_device!.IsConnected) return;

            ReadReport(report.Data);
            _device!.ReadReport(OnReport);
        }
    }
}
