//
// NumberedTitleGenerator.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.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices.JavaScript;
using System.Threading;
namespace PacketLogger.Models.Titles;
///
/// A generator and manager of document titles.
///
public class NumberedTitleGenerator
{
private readonly SemaphoreSlim _semaphore;
private readonly ConcurrentDictionary> _titles;
///
/// Initializes a new instance of the class.
///
public NumberedTitleGenerator()
{
_semaphore = new SemaphoreSlim(1, 1);
_titles = new ConcurrentDictionary>();
}
///
/// Add title with given information.
///
/// The function used for setting new document title.
/// The observable observing changes to the default, unnumbered title.
/// The current initial title.
/// The handle, title will be removed upon disposal.
public TitleHandle AddTitle
(
Action setDocumentTitle,
IObservable titleChanged,
string initialTitle
)
{
var title = new Title
(
setDocumentTitle,
initialTitle
);
title.TitleChanged = titleChanged.Subscribe
(
newTitle => HandleTitleChange(title, newTitle)
);
HandleTitleChange(title, title.CurrentTitle);
return new TitleHandle(this, title);
}
private void RemoveTitle(Title title)
{
_titles.AddOrUpdate
(
title.CurrentTitle,
_ => new List(),
(_, u) =>
{
u.Remove(title);
return u;
}
);
UpdateNumbers(title.CurrentTitle);
}
private void HandleTitleChange(Title title, string newTitle)
{
_semaphore.Wait();
_titles.AddOrUpdate
(
title.CurrentTitle,
_ => new List(),
(_, u) =>
{
u.Remove(title);
return u;
}
);
UpdateNumbers(title.CurrentTitle);
title.CurrentTitle = newTitle;
_titles.TryAdd(newTitle, new List());
_titles.AddOrUpdate
(
newTitle,
_ => new List(),
(_, u) =>
{
u.Add(title);
return u;
}
);
UpdateNumbers(title.CurrentTitle);
_semaphore.Release();
}
private void UpdateNumbers(string title)
{
if (_titles.TryGetValue(title, out var titles))
{
if (titles.Count == 1)
{
titles[0].CurrentNumber = 0;
titles[0].SetDocumentTitle(titles[0].CurrentTitle);
}
else if (titles.Count > 1)
{
titles[0].CurrentNumber = null;
titles[0].SetDocumentTitle(titles[0].CurrentTitle);
for (int i = 1; i < titles.Count; i++)
{
titles[i].CurrentNumber = i;
titles[i].SetDocumentTitle($"{titles[i].CurrentTitle} ({i})");
}
}
}
}
///
/// A store of a title.
///
public class TitleHandle : IDisposable
{
private readonly NumberedTitleGenerator _titleGenerator;
private readonly Title _title;
///
/// Initializes a new instance of the class.
///
/// The title generator.
/// The title.
public TitleHandle(NumberedTitleGenerator titleGenerator, Title title)
{
_titleGenerator = titleGenerator;
_title = title;
}
///
public void Dispose()
{
_title.TitleChanged.Dispose();
_titleGenerator.RemoveTitle(_title);
}
}
///
/// A title.
///
/// The function used for setting the title of document.
/// The current title.
public record Title(Action SetDocumentTitle, string CurrentTitle)
{
///
/// Gets or sets the current suffix.
///
public int? CurrentNumber { get; set; }
///
/// Gets or sets the disposable title changed observer.
///
public IDisposable TitleChanged { get; set; } = null!;
///
/// Gets or sets the current title.
///
public string CurrentTitle { get; set; } = CurrentTitle;
}
}