using System; using System.Collections.Generic; using System.IO; using System.Threading.Tasks; using MyInfoMate.Vr.Net; using UnityEngine; namespace MyInfoMate.Vr.Menu { /// /// Remplit une à partir d'une section — item E8 du /// lot XR-4. C'est ici, et nulle part ailleurs, que vit ce qui distingue un Slider /// d'une Map ou d'un Event. /// /// Trois types sur les quatre retenus au §8. Le quatrième, la Video 360, attend /// la ressource 360° côté backend. Le Parcours est volontairement rendu comme une Map : /// « chaque étape = un lieu autour de soi » demande de la 3D, donc `SectionScene3D`. /// public static class SectionPages { /// Les types qu'on sait ouvrir aujourd'hui. public static bool CanRender(ConfigurationExport.SectionType type) => type == ConfigurationExport.SectionType.Slider || type == ConfigurationExport.SectionType.Map || type == ConfigurationExport.SectionType.Parcours || type == ConfigurationExport.SectionType.Event; public static async Task FillAsync(PagedView view, ConfigurationExport.SectionSummary section, ApiClient client, string language) { switch (section.Type) { case ConfigurationExport.SectionType.Slider: await FillSlider(view, section, client, language); break; case ConfigurationExport.SectionType.Map: case ConfigurationExport.SectionType.Parcours: await FillMap(view, section, client, language); break; case ConfigurationExport.SectionType.Event: FillEvent(view, section, language); break; } if (view != null && view.PageCount == 0) view.ShowEmpty("Ce contenu est vide."); } static async Task FillSlider(PagedView view, ConfigurationExport.SectionSummary section, ApiClient client, string language) { foreach (var content in section.OrderedContents()) { var texture = await LoadImage(client, content.Resource?.Url); if (view == null) return; if (texture == null) continue; view.AddPage(new PagedView.Page { Heading = ConfigurationExport.Translate(content.Title, language) ?? "", Body = ConfigurationExport.Translate(content.Description, language), Image = texture }); } } /// /// Une Map devient une liste de points d'intérêt, un par page. /// /// ⚠️ Ce n'est pas la Map que le plan promet. Sa valeur en VR, c'est « le plan posé /// devant soi comme une maquette » — et ça demande un modèle 3D, donc /// `SectionScene3D` (§9). Une carte Google affichée sur un panneau flottant serait /// moins bonne qu'un téléphone : autant montrer les lieux eux-mêmes, avec /// leur photo et leurs horaires, ce que le `GeoPoint` porte déjà. /// static async Task FillMap(PagedView view, ConfigurationExport.SectionSummary section, ApiClient client, string language) { foreach (var point in section.Points) { var texture = await LoadImage(client, point.ImageUrl); if (view == null) return; var parts = new List(); var description = ConfigurationExport.Translate(point.Description, language); if (!string.IsNullOrEmpty(description)) parts.Add(description); var schedules = ConfigurationExport.Translate(point.Schedules, language); if (!string.IsNullOrEmpty(schedules)) parts.Add(schedules); view.AddPage(new PagedView.Page { Heading = ConfigurationExport.Translate(point.Title, language) ?? "", Body = string.Join("\n", parts), Image = texture }); } } /// /// Un Event devient le programme, une page par bloc. /// /// « Ce qui se passe aujourd'hui » est l'usage visé (§8) : un casque en /// écran d'attente dans un office de tourisme. On ne montre donc que la journée en /// cours — et seulement s'il s'y passe quelque chose : un programme vide le jour /// même vaut mieux rempli par les dates suivantes que pas rempli du tout. /// static void FillEvent(PagedView view, ConfigurationExport.SectionSummary section, string language) { var blocks = new List(section.Programme); blocks.Sort((a, b) => Nullable.Compare(a.StartTime, b.StartTime)); var today = new List(); var upcoming = new List(); foreach (var block in blocks) { if (block.StartTime == null) continue; var start = block.StartTime.Value.ToLocalTime(); if (start.Date == DateTime.Now.Date) today.Add(block); else if (start > DateTime.Now) upcoming.Add(block); } var shown = today.Count > 0 ? today : upcoming; foreach (var block in shown) { var start = block.StartTime?.ToLocalTime(); var end = block.EndTime?.ToLocalTime(); var when = start == null ? "" : today.Count > 0 ? (end == null ? $"{start:HH:mm}" : $"{start:HH:mm} – {end:HH:mm}") : $"{start:dd/MM} · {start:HH:mm}"; var description = ConfigurationExport.Translate(block.Description, language); view.AddPage(new PagedView.Page { Heading = ConfigurationExport.Translate(block.Title, language) ?? "", Body = string.IsNullOrEmpty(description) ? when : $"{when}\n{description}" }); } if (view.PageCount == 0) view.ShowEmpty("Rien au programme aujourd'hui."); } static async Task LoadImage(ApiClient client, string url) { if (string.IsNullOrEmpty(url)) return null; var path = await ContentCache.MediaPathAsync(client, url); if (path == null) return null; var texture = new Texture2D(2, 2); return texture.LoadImage(File.ReadAllBytes(path)) ? texture : null; } } }