using System.Runtime;
using VisionBuilder.UI.Common;
using VisionBuilder.UI.Common.Commands;
using VisionBuilder.UI.Common.Processing;
using VisionBuilder.UI.Common.ViewModel;
using VisionBuilder.UI.IOCommander.Settings;

namespace VisionBuilder.UI.IOCommander.Modules;

public class IOCommanderCameraModule: IVisionBuilderModule
{
    private readonly IOCommanderModule _mainModule;
    private readonly IOCommanderSettings _settings;
    private readonly IOCommanderCameraSettings _cameraSettings;
    private readonly IRecognitionControl _recognitionControl;
    private readonly MainWindowVM _mainWindowVm;
    private readonly SingleCameraVM _cameraVm;

    public IOCommanderCameraModule(IOCommanderModule mainModule, IOCommanderSettings settings, IOCommanderCameraSettings cameraSettings, IRecognitionControl recognitionControl, MainWindowVM mainWindowVm, SingleCameraVM cameraVm)
    {
        _mainModule = mainModule;
        _settings = settings;
        _cameraSettings = cameraSettings;
        _recognitionControl = recognitionControl;
        _mainWindowVm = mainWindowVm;
        _cameraVm = cameraVm;

        _recognitionControl.ImageProcessed += _recognitionControl_ImageProcessed;
        _recognitionControl.SessionStarted += _recognitionControl_SessionStarted;
        _recognitionControl.SessionEnded += _recognitionControl_SessionEnded;
        _recognitionControl.ErrorsInSequenceAlarm += _recognitionControl_ErrorsInSequenceAlarm;



    }

    private void _recognitionControl_ErrorsInSequenceAlarm(ErrorsInSequenceEvent obj)
    {
        EmitEvent(EProcessingEvent.ErrorsInSequenceAlarm);
    }

    private void ProcessCommand(EGPIOCommand command)
    {
        switch (command)
        {
            case EGPIOCommand.Start:
                if(_cameraVm.StartCommand.CanExecute(null))
                {
                    _cameraVm.StartCommand.Execute(null);
                }
                break;
            case EGPIOCommand.Stop:
                if(_cameraVm.StopCommand.CanExecute(null))
                {
                    _cameraVm.StopCommand.Execute(null);
                }
                break;

            case EGPIOCommand.Pause:
                if(_cameraVm.PauseCommand.CanExecute(null))
                {
                    _cameraVm.PauseCommand.Execute(null);
                }
                break;
            case EGPIOCommand.Resume:
                if(_cameraVm.ResumeCommand.CanExecute(null))
                {
                    _cameraVm.ResumeCommand.Execute(null);
                }
                break;
            default:
                throw new ArgumentOutOfRangeException(nameof(command), command, null);
        }
    }

    private void MainModule_OnPinChangedEvent(int arg1, bool arg2)
    {
        var @event = _cameraSettings.GPIOCommands.FirstOrDefault(x => x.Pin == arg1 && x.OnHigh == arg2);
        if (@event != null)
        {
            ProcessCommand(@event.Command);
        }
    }

    private void EmitEvent(EProcessingEvent @event)
    {
        
        var @eventActions = _cameraSettings.ProcessingEvents.First(x => x.Event == @event);
        var actions = @eventActions.Actions;
        if (_mainWindowVm.TestMode)
        {
            var testModeActions = _cameraSettings.TestModeOverride;
            var testModePins = testModeActions.Select(x => x.Pin).ToList();
            actions = actions.Where(x => !testModePins.Contains(x.Pin)).ToList();
            actions.AddRange(testModeActions);
        }
        

        foreach (var action in actions)
        {
            _mainModule.SetOutput(action.Pin, !action.Low);
            if (action.Pulse)
            {
                Task.Run(async () =>
                {
                    await Task.Delay(_settings.PulseLength);
                    _mainModule.SetOutput(action.Pin, action.Low);
                });
            }
        }
    }

    public void InitializeModule()
    {
        _mainModule.OnPinChangedEvent += MainModule_OnPinChangedEvent;
    }

    private void _recognitionControl_SessionEnded(SessionEndedEvent obj)
    {
        EmitEvent(EProcessingEvent.SessionEnded);

    }

    private void _recognitionControl_SessionStarted(SessionStartedEvent obj)
    {
        EmitEvent(EProcessingEvent.SessionStarted);
    }

    private void _recognitionControl_ImageProcessed(Common.Commands.ImageProcessedEvent obj)
    {

        if (obj.HasError)
        {
            EmitEvent(EProcessingEvent.ErrorOccurred);
        }
        else
        {
            EmitEvent(EProcessingEvent.GoodOccured);
        }

    }
}