using MainShell.EventArgsFolder;
using MainShell.Log;
using Stylet;
using System;
using System.Threading;
using MwFramework.Device;

namespace MainShell.Hardware
{
    public class DiastimeterPollingService : IDisposable
    {
        private const int DefaultIntervalMilliseconds = 200;
        private readonly HardwareManager _hardwareManager;
        private readonly IEventAggregator _eventAggregator;
        private readonly object _syncRoot = new object();
        private Timer _timer;
        private bool _isRunning;
        private bool _isReading;
        private int _intervalMilliseconds;

        public DiastimeterPollingService(HardwareManager hardwareManager, IEventAggregator eventAggregator)
        {
            _hardwareManager = hardwareManager ?? throw new ArgumentNullException(nameof(hardwareManager));
            _eventAggregator = eventAggregator ?? throw new ArgumentNullException(nameof(eventAggregator));
            _intervalMilliseconds = DefaultIntervalMilliseconds;
        }

        public bool IsRunning
        {
            get
            {
                lock (_syncRoot)
                {
                    return _isRunning;
                }
            }
        }

        public int IntervalMilliseconds
        {
            get
            {
                lock (_syncRoot)
                {
                    return _intervalMilliseconds;
                }
            }
        }

        public void Start()
        {
            Start(DefaultIntervalMilliseconds);
        }

        public void Start(int intervalMilliseconds)
        {
            if (intervalMilliseconds <= 0)
            {
                throw new ArgumentOutOfRangeException(nameof(intervalMilliseconds));
            }

            lock (_syncRoot)
            {
                _intervalMilliseconds = intervalMilliseconds;
                if (_timer == null)
                {
                    _timer = new Timer(ReadSample, null, Timeout.Infinite, Timeout.Infinite);
                }

                _timer.Change(0, _intervalMilliseconds);
                _isRunning = true;
            }

            $"激光测距仪轮询启动，周期={intervalMilliseconds}ms".LogInfo();
        }

        public void Stop()
        {
            lock (_syncRoot)
            {
                if (_timer != null)
                {
                    _timer.Change(Timeout.Infinite, Timeout.Infinite);
                }

                _isRunning = false;
            }

            "激光测距仪轮询停止。".LogInfo();
        }

        private void ReadSample(object state)
        {
            lock (_syncRoot)
            {
                if (!_isRunning || _isReading)
                {
                    return;
                }

                _isReading = true;
            }

            try
            {
                IDiastimeter diastimeter = _hardwareManager.Diastimeter;
                if (diastimeter == null)
                {
                    PublishInvalidSample("激光测距仪未初始化。");
                    return;
                }

                if (!diastimeter.IsConnected)
                {
                    PublishInvalidSample("激光测距仪未连接。");
                    return;
                }

                double distance;
                diastimeter.GetLastSample(out distance);
                if (double.IsNaN(distance) || double.IsInfinity(distance))
                {
                    PublishInvalidSample("激光测距仪反馈数据无效。");
                    return;
                }

                _eventAggregator.PublishOnUIThread(new DiastimeterSampleChangedEventArgs(distance, DateTime.Now, true, string.Empty));
            }
            catch (Exception ex)
            {
                $"激光测距仪轮询读取失败：{ex.Message}".LogSysError();
                PublishInvalidSample(ex.Message);
            }
            finally
            {
                lock (_syncRoot)
                {
                    _isReading = false;
                }
            }
        }

        private void PublishInvalidSample(string message)
        {
            _eventAggregator.PublishOnUIThread(new DiastimeterSampleChangedEventArgs(0d, DateTime.Now, false, message));
        }

        public void Dispose()
        {
            Stop();

            lock (_syncRoot)
            {
                if (_timer != null)
                {
                    _timer.Dispose();
                    _timer = null;
                }
            }
        }
    }
}
