//
// MainWindowViewModel.cs
//
// Copyright (c) František Boháček. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
using System.Collections;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Reactive;
using System.Reactive.Concurrency;
using System.Reactive.Linq;
using System.Reflection;
using System.Text.Json;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes;
using Dock.Model.Controls;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using NosSmooth.Comms.Local.Extensions;
using NosSmooth.Core.Extensions;
using NosSmooth.PacketSerializer.Abstractions.Attributes;
using NosSmooth.PacketSerializer.Extensions;
using NosSmooth.PacketSerializer.Packets;
using NosSmooth.Pcap;
using PacketLogger.Models;
using PacketLogger.Models.Filters;
using PacketLogger.Models.Packets;
using PacketLogger.ViewModels.Log;
using ReactiveUI;
namespace PacketLogger.ViewModels;
///
public class MainWindowViewModel : ViewModelBase
{
private readonly DockFactory _factory;
private readonly NostaleProcesses _processes;
///
/// Initializes a new instance of the class.
///
public MainWindowViewModel()
{
var filterProfiles = new FilterProfiles();
if (Path.Exists("settings.json"))
{
using var file = File.OpenRead("settings.json");
var deserialized = JsonSerializer.Deserialize(file);
if (deserialized is not null)
{
filterProfiles = deserialized;
}
}
var services = new ServiceCollection()
.AddLogging(b => b.ClearProviders().AddConsole())
.AddSingleton(_ => filterProfiles)
.AddSingleton()
.AddSingleton()
.AddSingleton>(_ => Providers)
.AddSingleton()
.AddSingleton()
.AddNostaleCore()
.AddStatefulInjector()
.AddStatefulEntity()
.AddLocalComms()
.AddPacketResponder(typeof(PacketResponder))
.BuildServiceProvider();
_processes = services.GetRequiredService();
_factory = services.GetRequiredService();
Layout = _factory.CreateLayout();
if (Layout is { })
{
_factory.InitLayout(Layout);
if (Layout is { } root)
{
root.Navigate.Execute("Home");
}
}
_factory.DocumentLoaded += doc =>
{
if (doc.Provider is not null)
{
RxApp.MainThreadScheduler.Schedule(() => Providers.Add(doc.Provider));
}
};
_factory.DocumentClosed += doc =>
{
if (doc.Provider is not null)
{
RxApp.MainThreadScheduler.Schedule(() => Providers.Remove(doc.Provider));
}
};
SaveAll = ReactiveCommand.CreateFromTask
(
async () =>
{
if (Layout?.FocusedDockable is DocumentViewModel activeDocument && activeDocument.Loaded
&& activeDocument.NestedViewModel is not null)
{
var mainWindow = (App.Current!.ApplicationLifetime as IClassicDesktopStyleApplicationLifetime)
?.MainWindow;
var result = await new SaveFileDialog()
{
DefaultExtension = ".log",
InitialFileName = Assembly.GetEntryAssembly()?.GetModules().FirstOrDefault()?.FullyQualifiedName
}.ShowAsync(mainWindow!);
if (result is null)
{
return;
}
if (activeDocument.Provider is null)
{
return;
}
using var file = File.OpenWrite(result);
using var streamWriter = new StreamWriter(file);
foreach (var packet in activeDocument.Provider.Packets.Items)
{
await streamWriter.WriteLineAsync
(
$"[{packet.Date:HH:mm:ss}]\t[{(packet.Source == PacketSource.Server ? "Recv" : "Send")}]\t{packet.PacketString.Trim()}"
);
}
}
}
);
SaveFiltered = ReactiveCommand.CreateFromTask
(
async () =>
{
if (Layout?.FocusedDockable is DocumentViewModel activeDocument && activeDocument.Loaded
&& activeDocument.NestedViewModel is not null)
{
var mainWindow = (App.Current!.ApplicationLifetime as IClassicDesktopStyleApplicationLifetime)
?.MainWindow;
var result = await new SaveFileDialog()
{
DefaultExtension = ".log",
}.ShowAsync(mainWindow!);
if (result is null)
{
return;
}
if (activeDocument.NestedViewModel is not PacketLogViewModel packetLogVM)
{
return;
}
using var file = File.OpenWrite(result);
using var streamWriter = new StreamWriter(file);
foreach (var packet in packetLogVM.FilteredPackets)
{
await streamWriter.WriteLineAsync
(
$"[{packet.Date:HH:mm:ss}]\t[{(packet.Source == PacketSource.Server ? "Recv" : "Send")}]\t{packet.PacketString.Trim()}"
);
}
}
}
);
SaveSettings = ReactiveCommand.CreateFromTask
(
async () =>
{
using var file = File.Open("settings.json", FileMode.Create);
await JsonSerializer.SerializeAsync(file, filterProfiles);
}
);
OpenFile = ReactiveCommand.Create
(() => _factory.CreateLoadedDocument(doc => doc.OpenFile.Execute(Unit.Default)));
OpenEmpty = ReactiveCommand.Create
(() => _factory.CreateLoadedDocument(doc => doc.OpenDummy.Execute(Unit.Default)));
OpenSettings = ReactiveCommand.Create
(() => _factory.CreateLoadedDocument(doc => doc.OpenSettings.Execute(Unit.Default)));
Connect = ReactiveCommand.Create
(process => _factory.CreateLoadedDocument(doc => doc.OpenProcess.Execute((NostaleProcess)process[0]!)));
OpenSender = ReactiveCommand.Create
(provider => _factory.CreateLoadedDocument(doc => doc.OpenSender.Execute((IPacketProvider)provider[0]!)));
NewTab = ReactiveCommand.Create
(() => _factory.DocumentDock.CreateDocument?.Execute(null));
QuitApplication = ReactiveCommand.Create
(() => (Application.Current?.ApplicationLifetime as IControlledApplicationLifetime)?.Shutdown());
}
///
/// Gets the nostale processes.
///
public ObservableCollection Processes => _processes.Processes;
///
/// Gets the packet provider.
///
public ObservableCollection Providers { get; } = new ObservableCollection();
///
/// Gets or sets the layout.
///
public IRootDock? Layout { get; set; }
///
/// Gets a command that quits the application.
///
public ReactiveCommand QuitApplication { get; }
///
/// Gets a command that saves all packets.
///
public ReactiveCommand SaveAll { get; }
///
/// Gets a command that saves filtered packets.
///
public ReactiveCommand SaveFiltered { get; }
///
/// Gets the comamnd that opens a file.
///
public ReactiveCommand OpenFile { get; }
///
/// Gets the command that opens empty logger.
///
public ReactiveCommand OpenEmpty { get; }
///
/// Gets the command that opens empty logger.
///
public ReactiveCommand OpenSender { get; }
///
/// Gets the command that opens empty logger.
///
public ReactiveCommand Connect { get; }
///
/// Gets the command that opens a new tab.
///
public ReactiveCommand NewTab { get; }
///
/// Gets the command used for opening settings.
///
public ReactiveCommand OpenSettings { get; }
///
/// Gets the command used for saving settings.
///
public ReactiveCommand SaveSettings { get; }
}