using DECompanion.Windows.Services; namespace DECompanion.Windows; public sealed class AppController : IDisposable { private readonly HttpClient http; private readonly CacheStore cache; private readonly ProgramCatalogService programs; private readonly TimetableService timetable; private readonly MensaService mensa; private readonly CampusPulseService campus; private readonly UserPreferences preferences; public DataSnapshot Snapshot { get; private set; } = DataSnapshot.Empty; public event EventHandler? SnapshotChanged; public AppController() { http = new HttpClient { Timeout = TimeSpan.FromSeconds(25) }; http.DefaultRequestHeaders.UserAgent.ParseAdd("DE Companion Windows/1.0"); cache = new CacheStore(); programs = new ProgramCatalogService(http, cache); timetable = new TimetableService(http, cache); mensa = new MensaService(http, cache); campus = new CampusPulseService(http, cache); preferences = new UserPreferences(cache.BaseDirectory); } public async Task RefreshAllAsync(CancellationToken cancellationToken) { var selectedId = preferences.SelectedProgramId; var (programList, programState) = await programs.LoadAsync(cancellationToken); var selected = programList.FirstOrDefault(p => p.Id == selectedId) ?? programList.FirstOrDefault(p => string.Equals(p.CohortLabel, selectedId, StringComparison.OrdinalIgnoreCase)) ?? programList.FirstOrDefault(); if (selected is not null && selected.Id != selectedId) { preferences.SelectedProgramId = selected.Id; preferences.Save(); } var timetableTask = selected is null ? Task.FromResult(new(Array.Empty(), new FeedState("Kein Studiengang", "Bitte Studiengang auswählen.", FeedStateStyle.Error))) : LoadTimetable(selected, cancellationToken); var mensaTask = mensa.LoadAsync(cancellationToken); var campusTask = campus.LoadAsync(cancellationToken); await Task.WhenAll(timetableTask, mensaTask, campusTask); var timetableResult = timetableTask.Result; var mensaResult = mensaTask.Result; var campusResult = campusTask.Result; Snapshot = new DataSnapshot( DateTimeOffset.Now, selected, programList, timetableResult.Events, mensaResult.Menus, campusResult.Entries, programState, timetableResult.State, mensaResult.State, campusResult.State); SnapshotChanged?.Invoke(this, Snapshot); } public async Task SelectProgramAsync(StudyProgram program, CancellationToken cancellationToken) { preferences.SelectedProgramId = program.Id; preferences.Save(); await RefreshAllAsync(cancellationToken); } public void Dispose() { http.Dispose(); } private async Task LoadTimetable(StudyProgram selected, CancellationToken cancellationToken) { var result = await timetable.LoadAsync(selected, cancellationToken); return new LoadTimetableResult(result.Events, result.State); } private sealed record LoadTimetableResult(IReadOnlyList Events, FeedState State); }