using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.IO.Ports;
using System.Runtime.CompilerServices;
using System.Windows.Input;
using Avalonia.Threading;
using OMControl.Services;

namespace OMControl.ViewModels;

public class MainWindowViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler? PropertyChanged;
    void Raise([CallerMemberName] string? p = null) => PropertyChanged?.Invoke(this, new(p));

    private readonly SerialPortService _serial = new();

    public ObservableCollection<string> AvailablePorts { get; } = new();

    private string? _selectedPort;
    public string? SelectedPort { get => _selectedPort; set { _selectedPort = value; Raise(); } }

    private string _receivedData = "";
    public string ReceivedData { get => _receivedData; set { _receivedData = value; Raise(); } }

    private string _sendData = "";
    public string SendData { get => _sendData; set { _sendData = value; Raise(); } }

    string _field01 = ""; public string Field01 { get => _field01; set { _field01 = value; Raise(); } }
    string _field02 = ""; public string Field02 { get => _field02; set { _field02 = value; Raise(); } }
    string _field03 = ""; public string Field03 { get => _field03; set { _field03 = value; Raise(); } }

    public ICommand RefreshPortsCommand { get; }
    public ICommand ConnectCommand { get; }
    public ICommand DisconnectCommand { get; }
    public ICommand SendCommand { get; }
    public ICommand ClearCommand { get; }

    public MainWindowViewModel()
    {
        RefreshPortsCommand = new RelayCommand(RefreshPorts);
        ConnectCommand = new RelayCommand(Connect);
        DisconnectCommand = new RelayCommand(Disconnect);
        SendCommand = new RelayCommand(Send);
        ClearCommand = new RelayCommand(ClearAll);

        _serial.DataReceived += data =>
        {
            Dispatcher.UIThread.Post(() => ReceivedData += data + Environment.NewLine);
        };

        RefreshPorts();
    }

    private void RefreshPorts()
    {
        AvailablePorts.Clear();
        foreach (var p in SerialPort.GetPortNames())
            AvailablePorts.Add(p);

        if (SelectedPort == null && AvailablePorts.Count > 0)
            SelectedPort = AvailablePorts[0];
    }

    private void Connect()
    {
        if (SelectedPort is null) return;
        _serial.Open(SelectedPort, 9600);
    }

    private void Disconnect() => _serial.Close();

    private void Send()
    {
        if (!string.IsNullOrWhiteSpace(SendData))
            _serial.Send(SendData);
    }

    private void ClearAll()
    {
        Field01 = Field02 = Field03 = "";
        ReceivedData = "";
        SendData = "";
    }
}

public sealed class RelayCommand : ICommand
{
    private readonly Action _execute;
    private readonly Func<bool>? _canExecute;

    public RelayCommand(Action execute, Func<bool>? canExecute = null)
    {
        _execute = execute;
        _canExecute = canExecute;
    }

    public event EventHandler? CanExecuteChanged;
    public bool CanExecute(object? parameter) => _canExecute?.Invoke() ?? true;
    public void Execute(object? parameter) => _execute();
}
