using System.Net;
using System.Net.NetworkInformation;
namespace SddpViewer;
using System;
using System.Threading.Tasks;
using CommunityToolkit.Mvvm.ComponentModel;
using Rssdp;
public partial class DiscoveredDeviceViewModel : ObservableObject, IDisposable
{
  private readonly DiscoveredSsdpDevice _device;
  private SsdpDevice? _ssdpDevice;
  private readonly CancellationTokenSource _cancellationTokenSource = new();
  public DiscoveredDeviceViewModel(DiscoveredSsdpDevice device)
  {
    _device = device;
    ResponseHeader = GetResponseHeader();
    DiscoveredAt = DateTime.Now;
    IpAddress = EvaluateIpAddress(device);
    MacAddress = EvaluateMacAddress();
    HostName = EvaluateHostName();
    RunPing(_cancellationTokenSource.Token);
  }
  private async void RunPing(CancellationToken token)
  {
    try
    {
      using Ping ping = new();
      while (!token.IsCancellationRequested)
      {
        var result = await ping.SendPingAsync(IpAddress, TimeSpan.FromSeconds(1), cancellationToken: token).ConfigureAwait(true);
        Online = result.Status == IPStatus.Success;
        await Task.Delay(1000, token).ConfigureAwait(true);
      }
    }
    catch (TaskCanceledException e)
    {
      Console.WriteLine(e);
    }
    catch (OperationCanceledException e)
    {
      Console.WriteLine(e);
    }
  }
  public string HostName { get; set; }
  public string MacAddress { get; set; }
  public IPAddress IpAddress { get; set; }
  public DateTime DiscoveredAt { get; }
  [ObservableProperty]
  public partial string ResponseHeader { get; set; }
  [ObservableProperty]
  public partial bool Online { get; set; }
  /// 
  /// Sets or returns the type of notification, being either a uuid, device type, service type or upnp:rootdevice.
  /// 
  public string NotificationType => _device.NotificationType;
  /// 
  /// Sets or returns the universal service name (USN) of the device.
  /// 
  public string Usn => _device.Usn;
  /// 
  /// Sets or returns a URL pointing to the device description document for this device.
  /// 
  public Uri DescriptionLocation => _device.DescriptionLocation;
  /// 
  /// Sets or returns the length of time this information is valid for (from the  time).
  /// 
  public TimeSpan CacheLifetime => _device.CacheLifetime;
  /// 
  /// Sets or returns the date and time this information was received.
  /// 
  public DateTimeOffset AsAt => _device.AsAt;
  [ObservableProperty]
  public partial string FriendlyName { get; set; } = "";
  [ObservableProperty]
  public partial SsdpDeviceIcon? Icon { get; set; }
  [ObservableProperty]
  public partial Uri? PresentationUrl { get; set; }
  [ObservableProperty]
  public partial string ModelNumber { get; set; } = "";
  [ObservableProperty]
  public partial string Version { get; set; } = "";
  public void Cancel()
  {
    _cancellationTokenSource.Cancel();
  }
  public async Task GetFurtherInformationAsync()
  {
    _ssdpDevice = await _device.GetDeviceInfo().ConfigureAwait(false);
    FriendlyName = _ssdpDevice.FriendlyName;
    Icon = _ssdpDevice.Icons.MinBy(x => x.Height);
    PresentationUrl = _ssdpDevice.PresentationUrl;
    ModelNumber = _ssdpDevice.ModelNumber;
    Version = _ssdpDevice.SerialNumber?.Split(',').Last() ?? new Version().ToString();
  }
  private string EvaluateHostName() => Dns.GetHostEntry(IpAddress).HostName;
  private string GetResponseHeader()
  {
    return _device.ResponseHeaders is null
      ? ""
      : string.Join("," + Environment.NewLine, _device.ResponseHeaders.Select(x => $"{{{x.Key} : {string.Join(";", x.Value)}}}"));
  }
  private IPAddress EvaluateIpAddress(DiscoveredSsdpDevice device) =>
    IPAddress.TryParse(device.DescriptionLocation.Host, out var value) ? value : IPAddress.Any;
  private string EvaluateMacAddress()
  {
    if (!ArpLookup.Arp.IsSupported)
      return "not supported";
    var lookupResult = ArpLookup.Arp.Lookup(IpAddress);
    return lookupResult is null ? "Unknown" : string.Join(":", lookupResult.GetAddressBytes().Select(b => $"{b:x2}"));
  }
  public override bool Equals(object? obj)
  {
    return obj is DiscoveredDeviceViewModel viewModel && Equals(viewModel.Usn, Usn);
  }
  public void Dispose()
  {
    _cancellationTokenSource.Cancel();
    _cancellationTokenSource.Dispose();
  }
}