using System.Diagnostics;
using System.IO;
using System.Text.Json;
using System.Windows.Threading;
namespace DeskCore;
public class UsageEntry
{
public int Count { get; set; }
public DateTime Last { get; set; }
}
///
/// Merkt sich, was wie oft und wann zuletzt über DeskCore gestartet wurde (für „Meistgenutzt“, „Zuletzt benutzt“
/// und die Schnellstart-Suche). Liegt getrennt von den Einstellungen in usage.json, weil es sich ständig ändert.
///
public static class Usage
{
/// Schlüssel-Präfix für Start-Gruppen (die keinen Dateipfad haben).
public const string GroupPrefix = "gruppe:";
private static readonly string FilePath = Path.Combine(AppSettings.Folder, "usage.json");
private static readonly Dictionary _data = Load();
private static DispatcherTimer? _saveTimer;
/// Etwas wurde gestartet (z. B. damit „Zuletzt benutzt“-Boxen sich aktualisieren).
public static event Action? Changed;
private static Dictionary Load()
{
try
{
if (File.Exists(FilePath) &&
JsonSerializer.Deserialize>(File.ReadAllText(FilePath)) is { } d)
return new(d, StringComparer.OrdinalIgnoreCase);
}
catch { }
return new(StringComparer.OrdinalIgnoreCase);
}
/// Programm/Datei starten und den Start mitzählen. Wirft bei Fehlern wie Process.Start.
public static void Start(string path)
{
Process.Start(new ProcessStartInfo(path) { UseShellExecute = true });
Record(path);
}
public static void Record(string key)
{
if (!_data.TryGetValue(key, out var e)) _data[key] = e = new UsageEntry();
e.Count++;
e.Last = DateTime.Now;
ScheduleSave();
Changed?.Invoke();
}
public static int Count(string key) => _data.TryGetValue(key, out var e) ? e.Count : 0;
///
/// Wie „beliebt“ etwas ist: Anzahl der Starts, mit der Zeit abnehmend gewichtet –
/// was du früher oft, aber seit Wochen nicht mehr benutzt hast, rutscht langsam nach hinten.
///
public static double Score(string key)
{
if (!_data.TryGetValue(key, out var e)) return 0;
double days = Math.Max(0, (DateTime.Now - e.Last).TotalDays);
return e.Count / (1 + days / 30);
}
/// Zuletzt gestartete Dateien, die es noch gibt (neueste zuerst).
public static List Recent(int max) => _data
.Where(kv => !kv.Key.StartsWith(GroupPrefix) && (File.Exists(kv.Key) || Directory.Exists(kv.Key)))
.OrderByDescending(kv => kv.Value.Last)
.Take(max)
.Select(kv => kv.Key)
.ToList();
/// Datei wurde umbenannt/verschoben → Zählung mitnehmen.
public static void Rename(string oldKey, string newKey)
{
if (!_data.Remove(oldKey, out var e)) return;
if (_data.TryGetValue(newKey, out var existing))
{
existing.Count += e.Count;
if (e.Last > existing.Last) existing.Last = e.Last;
}
else _data[newKey] = e;
ScheduleSave();
}
public static void Forget(string key)
{
if (_data.Remove(key)) { ScheduleSave(); Changed?.Invoke(); }
}
private static void ScheduleSave()
{
_saveTimer ??= new DispatcherTimer(TimeSpan.FromSeconds(2), DispatcherPriority.Background, (_, _) => Flush(),
Dispatcher.CurrentDispatcher);
_saveTimer.Stop();
_saveTimer.Start();
}
/// Sofort speichern (z. B. beim Beenden).
public static void Flush()
{
_saveTimer?.Stop();
try
{
// Nicht mehr vorhandene Einträge, die lange nicht benutzt wurden, aufräumen
foreach (var key in _data.Where(kv => kv.Value.Last < DateTime.Now.AddDays(-180) && !kv.Key.StartsWith(GroupPrefix)
&& !File.Exists(kv.Key) && !Directory.Exists(kv.Key)).Select(kv => kv.Key).ToList())
_data.Remove(key);
Directory.CreateDirectory(AppSettings.Folder);
File.WriteAllText(FilePath, JsonSerializer.Serialize(_data));
}
catch { }
}
}