using System.Collections.Concurrent;
using Serilog;
using VisionBuilder.UI.Common;
using VisionBuilder.UI.Common.Commands;
using VisionBuilder.UI.Common.ViewModel;
using VisionBuilder.UI.IOCommander.Interfaces;
using VisionBuilder.UI.IOCommander.Settings;

namespace VisionBuilder.UI.IOCommander.Modules;


public class IOCommanderModule:IVisionBuilderModule
{
    private readonly IOCommanderSettings _settings;
    private readonly IIOCommanderFactory _factory;
    private readonly IIOCommanderDebugViewService _debugViewService;

    private IIOCommander? _ioCommander;
    public IOCommanderModule(IOCommanderSettings settings, IIOCommanderFactory factory, IIOCommanderDebugViewService debugViewService)
    {
        _settings = settings;
        _factory = factory;
        _debugViewService = debugViewService;
    }

    public void InitializeModule()
    {
        if(!_settings.Enabled)return;
        try
        {
            _ioCommander = _factory.Create();
            _ioCommander.OnPinChanged += OnPinChanged;
            BroadcastInitialPinStates();
        }
        catch (Exception e)
        {
            Log.Warning("IOCommander did non load: {e}",e);
            _ioCommander= null;
        }


        if (_settings.ShowDebugView)_debugViewService.ShowDebugView();
        

    }
    private void BroadcastInitialPinStates()
    {
        if (_ioCommander == null) return;
        Task.Run(async () =>
        {
            // Wait for the first poll cycle to populate PinState
            for (int attempt = 0; attempt < 10 && _ioCommander?.PinState == null; attempt++)
            {
                await Task.Delay(200);
            }
            if (_ioCommander?.PinState == null) return;
            for (int i = 0; i < _ioCommander.PinState.Length; i++)
            {
                OnPinChanged(i + 1, _ioCommander.PinState[i]);
            }
        });
    }

    ConcurrentDictionary<int, bool> _pinStates = new ConcurrentDictionary<int, bool>();
    public event Action<int, bool>? OnPinChangedEvent = delegate { };
    private void OnPinChanged(int arg1, bool arg2)
    {
        _pinStates[arg1] = arg2;
        _debugViewService.SetInputPinStatus(arg1, arg2);
        OnPinChangedEvent?.Invoke(arg1, arg2);
    }

    public void SetOutput(int pin, bool value)
    {
        if (_ioCommander != null)
        {
            _ioCommander.SetPins(pin, value);
        }
        _debugViewService.SetOutputPinStatus(pin,value);
    }

    public bool GetInput(int pin)
    {
        if (_ioCommander == null) return false;
        var found=_pinStates.TryGetValue(pin, out var value);
        if (found)
        {
            return value;
        }
        return false;
    }
}