using BlazorApp.Models;
using BlazorApp.Services.Interfaces;
using Peak.Can.Basic.BackwardCompatibility;

namespace BlazorApp.Services.Windows
{
    public class PcanCanService : ICanService, IDisposable
    {
        private const ushort PCAN_USBBUS1 = 0x51;
        private CancellationTokenSource? _cts;
        private bool _initialized;
        public event Action<CanMessage>? OnMessageReceived;

        public Task InitAsync()
        {
            if (_initialized) return Task.CompletedTask;

            var status = PCANBasic.Initialize(
                PCAN_USBBUS1,
                TPCANBaudrate.PCAN_BAUD_250K
            );

            if (status != TPCANStatus.PCAN_ERROR_OK)
                throw new Exception($"PCAN init failed: {status}");

            _initialized = true;
            return Task.CompletedTask;
        }

        public Task<List<CanMessage>> ReadAsync()
        {
            var list = new List<CanMessage>();
            TPCANMsg msg;
            TPCANTimestamp ts;

            if (PCANBasic.Read(PCAN_USBBUS1, out msg, out ts)
                == TPCANStatus.PCAN_ERROR_OK)
            {
                list.Add(Map(msg));
            }

            return Task.FromResult(list);
        }

        public void StartStream()
        {
            if (_cts != null) return;

            _cts = new CancellationTokenSource();

            Task.Run(() =>
            {
                while (!_cts.IsCancellationRequested)
                {
                    TPCANMsg msg;
                    TPCANTimestamp ts;

                    var result = PCANBasic.Read(
                        PCAN_USBBUS1,
                        out msg,
                        out ts
                    );

                    if (result == TPCANStatus.PCAN_ERROR_OK)
                        OnMessageReceived?.Invoke(Map(msg));
                    else
                        Thread.Sleep(1);
                }
            });
        }

        public void StopStream()
        {
            _cts?.Cancel();
            _cts = null;

            if (_initialized)
            {
                PCANBasic.Uninitialize(PCAN_USBBUS1);
                _initialized = false;
            }
        }

        private static CanMessage Map(TPCANMsg msg) => new()
        {
            Id = msg.ID,
            Length = msg.LEN,
            Data = msg.DATA,
            Timestamp = DateTime.Now
        };

        public void Dispose() => StopStream();
    }
}
