using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Threading.Tasks;
using CommunityToolkit.Diagnostics;
using CommunityToolkit.Mvvm.DependencyInjection;
using Microsoft.Extensions.DependencyInjection;
using OwlCore;
using OwlCore.AbstractStorage;
using OwlCore.Extensions;
using StrixMusic.Sdk.Services;
using StrixMusic.Sdk.ViewModels;
using StrixMusic.Sdk.WinUI.Services.NotificationService;
using StrixMusic.Sdk.WinUI.Services.ShellManagement;
using StrixMusic.Services;
using StrixMusic.Services.CoreManagement;
using Windows.Media.Playback;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
namespace StrixMusic.Shared
{
///
/// Displays the main content of the app (the user's preferred shell).
///
public sealed partial class MainPage : UserControl
{
private readonly AppSettings _settings;
private readonly ICoreManagementService _coreManagementService;
private readonly List _mediaPlayerElements = new();
///
/// Initializes a new instance of the class.
///
public MainPage(StrixDataRootViewModel dataRoot, AppSettings settings, ICoreManagementService coreManagementService)
{
_settings = settings;
_coreManagementService = coreManagementService;
DataRoot = dataRoot;
InitializeComponent();
Loaded += MainPage_Loaded;
}
///
/// The currently loaded shell, if any.
///
internal ShellMetadata? ActiveShell { get; private set; }
///
/// The user's preferred shell.
///
internal ShellMetadata? PreferredShell { get; private set; }
///
/// The user's preferred shell.
///
internal ShellMetadata? FallbackShell { get; private set; }
///
/// The data root the page was initialized with.
///
public StrixDataRootViewModel DataRoot { get; }
private async void MainPage_Loaded(object? sender, RoutedEventArgs e)
{
Loaded -= MainPage_Loaded;
Unloaded += MainPage_Unloaded;
LoadRegisteredMediaPlayerElements();
SetupShellsFromSettings();
Guard.IsNotNull(PreferredShell, nameof(PreferredShell));
await SetupShell(PreferredShell);
AttachEvents();
// Events must be attached before initializing if you want them to fire correctly.
await Ioc.Default.GetRequiredService().InitAsync();
}
private void MainPage_Unloaded(object? sender, RoutedEventArgs e)
{
DetachEvents();
}
///
/// Fires when the Super buttons is clicked. Temporary until a proper trigger mechanism is found for touch devices.
///
public void Button_Click(object? sender, RoutedEventArgs e) => Window.Current.GetAppFrame().DisplaySuperShell(_settings, DataRoot.Sources, _coreManagementService);
private void AttachEvents()
{
Ioc.Default.GetRequiredService().PropertyChanged += OnSettingChanged;
#warning Remove me when live core editing is stable.
var coreManagementService = Ioc.Default.GetRequiredService();
coreManagementService.CoreInstanceRegistered += ShowEditCoresWarning;
coreManagementService.CoreInstanceUnregistered += ShowEditCoresWarning;
}
private void DetachEvents()
{
Unloaded -= MainPage_Unloaded;
Ioc.Default.GetRequiredService().PropertyChanged -= OnSettingChanged;
var coreManagementService = Ioc.Default.GetRequiredService();
coreManagementService.CoreInstanceRegistered -= ShowEditCoresWarning;
coreManagementService.CoreInstanceUnregistered -= ShowEditCoresWarning;
}
private void ShowEditCoresWarning(object? sender, CoreInstanceEventArgs args)
{
Ioc.Default.GetRequiredService().RaiseNotification("Restart recommended",
"Editing cores while the app is running is not stable yet");
}
private async void OnSettingChanged(object? sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == nameof(AppSettings.FallbackShell) || e.PropertyName == nameof(AppSettings.PreferredShell))
{
SetupShellsFromSettings();
if (PreferredShell is null)
return;
if (FallbackShell is null)
return;
// Store the shell that is actually going to be created according to the conditions.
ShellMetadata? shellToCreate;
if (CheckShellModelSupport(PreferredShell))
shellToCreate = PreferredShell;
else
shellToCreate = FallbackShell;
// Don't setup the fallback shell if its same as the preferred shell.
if (e.PropertyName == nameof(AppSettings.FallbackShell) && shellToCreate != PreferredShell)
{
await SetupShell(shellToCreate);
}
if (e.PropertyName == nameof(AppSettings.PreferredShell))
{
await SetupShell(shellToCreate);
}
}
}
private void LoadRegisteredMediaPlayerElements()
{
foreach (var item in _mediaPlayerElements.Where(item => !PART_MediaPlayerElements.Children.Contains(item)))
{
PART_MediaPlayerElements.Children.Add(item);
}
}
private void SetupShellsFromSettings()
{
// Gets the preferred shell from settings.
var preferredShellId = _settings.PreferredShell;
PreferredShell = ShellRegistry.MetadataRegistry.FirstOrDefault(x => x.Id == preferredShellId);
if (PreferredShell == default)
{
CurrentWindow.NavigationService.NavigateTo(typeof(SuperShell), true);
return;
}
// Gets the preferred shell from settings.
var fallbackShellId = _settings.FallbackShell;
FallbackShell = ShellRegistry.MetadataRegistry.FirstOrDefault(x => x.Id == fallbackShellId);
}
private Task SetupShell(ShellMetadata shellMetadata)
{
using (Threading.PrimaryContext)
{
if (ActiveShell is not null && ActiveShell.Id == shellMetadata.Id)
return Task.CompletedTask;
var notificationService = Ioc.Default.GetRequiredService().Cast();
notificationService.ChangeNotificationMargins(new Thickness(25, 35, 25, 35));
notificationService.ChangeNotificationAlignment(HorizontalAlignment.Right, VerticalAlignment.Top);
// Removes the current shell.
ShellDisplay.Content = null;
var shell = ShellRegistry.CreateShell(shellMetadata, DataRoot);
shell.DataRoot = DataRoot;
shell.Notifications = Window.Current.GetAppFrame().Notifications;
shell.Notifications.IsHandled = false;
shell.InitServices(new ServiceCollection());
ShellDisplay.Content = shell;
ActiveShell = shellMetadata;
return Task.CompletedTask;
}
return Task.CompletedTask;
}
private bool CheckShellModelSupport(ShellMetadata shell)
{
bool heightIsInRange = ActualHeight < shell.MaxWindowSize.Height &&
ActualHeight > shell.MinWindowSize.Height;
bool widthIsInRange = ActualWidth > shell.MinWindowSize.Width &&
ActualWidth > shell.MinWindowSize.Width;
return widthIsInRange && heightIsInRange;
}
private async void MainPage_SizeChanged(object? sender, SizeChangedEventArgs e)
{
if (ActiveShell is null || PreferredShell is null || FallbackShell is null)
{
// Ignore this during initialization
return;
}
if (ActiveShell != PreferredShell)
{
if (CheckShellModelSupport(PreferredShell))
{
if (FallbackShell != PreferredShell)
await SetupShell(PreferredShell);
}
}
else if (!CheckShellModelSupport(ActiveShell))
{
if (FallbackShell != PreferredShell)
await SetupShell(FallbackShell);
}
}
///
/// Creates a and inserts it into the UI.
///
/// The created
public MediaPlayerElement CreateMediaPlayerElement()
{
var mediaSource = new MediaPlayer();
var mediaPlayerElement = new MediaPlayerElement();
mediaPlayerElement.SetMediaPlayer(mediaSource);
#if !__WASM__
mediaSource.CommandManager.IsEnabled = false;
#endif
_mediaPlayerElements.Add(mediaPlayerElement);
// If loaded, add it to the visual tree.
// If not loaded, we'll handle it in MainPage's Loaded event handler.
if (IsLoaded)
{
PART_MediaPlayerElements.Children.Add(mediaPlayerElement);
}
return mediaPlayerElement;
}
}
}