using BotPages.Core.Abstractions;
using BotPages.Core.Routing;
using System.Collections.Concurrent;
namespace BotPages.Core;
///
/// Сервис навигации между страницами.
/// Позволяет выполнять переходы, замену страниц и передачу аргументов.
///
public sealed class NavigationService
{
private readonly RoutesRegistry _routes;
private readonly Dictionary _singletonPages = new();
private readonly ConcurrentDictionary _sessionPages = new();
private Type? _defaultPage;
///
/// Создать сервис навигации.
///
internal NavigationService(RoutesRegistry routes)
{
_routes = routes;
}
internal void AddDefaultPage() where TPage : SingletonPage
{
_defaultPage = typeof(TPage);
}
///
/// Перейти по маршруту без аргументов.
///
public Task GoToHomeAsync(PageContext ctx, CancellationToken ct)
{
return NavigateAsync(_defaultPage!, ctx, null, ct);
}
///
/// Перейти по маршруту без аргументов.
///
public Task GoToAsync(string route, PageContext ctx, CancellationToken ct)
{
var pageType = _routes.Resolve(route);
return NavigateAsync(pageType!, ctx, null, ct);
}
///
/// Перейти по маршруту с аргументами.
///
public Task GoToAsync(string route, TArgs args, PageContext ctx, CancellationToken ct)
{
var pageType = _routes.Resolve(route);
return NavigateAsync(pageType!, ctx, args!, ct);
}
///
/// Перейти на страницу без аргументов.
///
public Task GoToAsync(PageContext ctx, CancellationToken ct) where TPage : Page
=> NavigateAsync(typeof(TPage), ctx, null, ct);
///
/// Перейти на страницу с аргументами.
///
public Task GoToAsync(PageContext ctx, TArgs args, CancellationToken ct) where TPage : StatefullPage
=> NavigateAsync(typeof(TPage), ctx, args!, ct);
///
/// Заменить текущую страницу.
///
public Task ReplaceWithAsync(PageContext ctx, CancellationToken ct) where TPage : Page
=> NavigateAsync(typeof(TPage), ctx, null, ct, replace: true);
internal async Task NavigateAsync(Type pageType, PageContext ctx, object? args, CancellationToken ct, bool replace = false)
{
Page? page;
if (typeof(SingletonPage).IsAssignableFrom(pageType))
{
// Singleton: один объект на всё приложение
if (!_singletonPages.TryGetValue(pageType, out page))
{
page = (Page)Activator.CreateInstance(pageType)!;
_singletonPages[pageType] = page;
}
}
else
{
// Stateful: новый объект на пользователя
page = (Page)Activator.CreateInstance(pageType)!;
}
if (_sessionPages.TryGetValue(ctx.SessionKey, out var currentPage))
{
if (currentPage.GetType() != pageType || replace)
{
await currentPage.OnLeave(ctx, ct);
}
}
_sessionPages[ctx.SessionKey] = page;
if (args is null)
await page.OnEnter(ctx, ct);
else
await (page as dynamic).OnEnter(ctx, (dynamic)args, ct);
}
///
/// Восстановить текущую страницу из StateStorage.
///
public Page? ResolveCurrentPage(PageContext ctx)
=> _sessionPages.TryGetValue(ctx.SessionKey, out var page) ? page : null;
}