From 6f4330ef123b41d6f6e30b5c7bb6ee2a432d501e Mon Sep 17 00:00:00 2001
From: Ben Parsons <9parsonsb@gmail.com>
Date: Thu, 18 Apr 2024 23:45:29 +1000
Subject: [PATCH] Added Basic File Watched Started structure of Status Service
 Not Yet Tested

---
 Pulsar/Features/EventsHub.cs                 |  1 -
 Pulsar/Features/FileHandlerService.cs        | 68 ++++++++++++++++++++
 Pulsar/Features/FileWatcherService.cs        | 40 ++++++++++++
 Pulsar/Features/Journal/JournalController.cs |  8 +++
 Pulsar/Features/Status/StatusController.cs   |  2 -
 Pulsar/Features/Status/StatusService.cs      | 18 ++++++
 Pulsar/Global.Usings.cs                      |  2 +
 Pulsar/LoggingUtils.cs                       | 29 ---------
 Pulsar/Program.cs                            |  1 +
 9 files changed, 137 insertions(+), 32 deletions(-)
 create mode 100644 Pulsar/Features/FileHandlerService.cs
 create mode 100644 Pulsar/Features/FileWatcherService.cs
 create mode 100644 Pulsar/Features/Journal/JournalController.cs
 create mode 100644 Pulsar/Features/Status/StatusService.cs
 delete mode 100644 Pulsar/LoggingUtils.cs

diff --git a/Pulsar/Features/EventsHub.cs b/Pulsar/Features/EventsHub.cs
index 3c4813a..10fa4cb 100644
--- a/Pulsar/Features/EventsHub.cs
+++ b/Pulsar/Features/EventsHub.cs
@@ -2,7 +2,6 @@ namespace Pulsar.Features;
 
 using Observatory.Framework.Files;
 using Observatory.Framework.Files.Journal;
-using Microsoft.AspNetCore.SignalR;
 
 public class EventsHub : Hub<IEventsHub>
 {
diff --git a/Pulsar/Features/FileHandlerService.cs b/Pulsar/Features/FileHandlerService.cs
new file mode 100644
index 0000000..d17cfaa
--- /dev/null
+++ b/Pulsar/Features/FileHandlerService.cs
@@ -0,0 +1,68 @@
+namespace Pulsar.Features;
+
+public interface IFileHandlerService
+{
+    void HandleFile(string path);
+}
+
+public class FileHandlerService(ILogger<FileHandlerService> logger, IStatusService statusService) : IFileHandlerService
+{
+    public static readonly string MarketFileName = "Market.json";
+    public static readonly string StatusFileName = "Status.json";
+    public static readonly string OutfittingFileName = "Outfitting.json";
+    public static readonly string ShipyardFileName = "Shipyard.json";
+    public static readonly string ModulesFileName = "Modules.json";
+    public static readonly string JournalFileName = "Journal.";
+    public static readonly string RouteFileName = "Route.json";
+    public static readonly string CargoFileName = "Cargo.json";
+    public static readonly string BackpackFileName = "Backpack.json";
+    public static readonly string ModulesInfoFileName = "ModulesInfo.json";
+    public static readonly string ShipLockerFileName = "ShipLocker.json";
+    public static readonly string NavRouteFileName = "NavRoute.json";
+    
+    public static readonly string[] AllFileNames =
+    [
+        MarketFileName,
+        StatusFileName,
+        OutfittingFileName,
+        ShipyardFileName,
+        ModulesFileName,
+        JournalFileName,
+        RouteFileName,
+        CargoFileName,
+        BackpackFileName,
+        ModulesInfoFileName,
+        ShipLockerFileName,
+        NavRouteFileName
+    ];
+    
+    private readonly Dictionary<string, IJournalHandler> Handlers = new()
+    {
+        { StatusFileName, statusService },
+    };
+    
+    public void HandleFile(string path)
+    {
+        var fileInfo = new FileInfo(path);
+        var fileName = fileInfo.Name;
+        
+        // only scan the file if we recognize it
+        var match = AllFileNames.FirstOrDefault(
+            f => fileName.StartsWith(f, StringComparison.InvariantCultureIgnoreCase));
+        
+        if (string.IsNullOrWhiteSpace(match))
+        {
+            logger.LogWarning("File {FileName} is not recognized", fileName);
+            return;
+        }
+
+        if (Handlers.TryGetValue(match, out var handler))
+        {
+            logger.LogInformation("Handling file {FileName}", fileName);
+            handler.HandleFile(fileInfo.Name);
+            return;
+        }
+        
+        logger.LogInformation("File {FileName} was not handled", fileName);
+    }
+}
diff --git a/Pulsar/Features/FileWatcherService.cs b/Pulsar/Features/FileWatcherService.cs
new file mode 100644
index 0000000..518434a
--- /dev/null
+++ b/Pulsar/Features/FileWatcherService.cs
@@ -0,0 +1,40 @@
+namespace Pulsar.Features;
+
+public class FileWatcherService(IOptions<PulsarConfiguration> options, IFileHandlerService fileHandlerService) : IHostedService
+{
+    private FileSystemWatcher watcher = null!;
+    
+    public Task StartAsync(CancellationToken cancellationToken)
+    {
+        watcher = new FileSystemWatcher(options.Value.JournalDirectory)
+        {
+            EnableRaisingEvents = true,
+            IncludeSubdirectories = true
+        };
+        
+        watcher.BeginInit();
+        
+        watcher.Created += HandleFileChanged;
+        watcher.Changed += HandleFileChanged;
+        watcher.Renamed += HandleFileChanged; // ?
+        
+        watcher.IncludeSubdirectories = false;
+        watcher.EnableRaisingEvents = true;
+        watcher.NotifyFilter = NotifyFilters.LastWrite & NotifyFilters.Size & NotifyFilters.FileName;
+        
+        watcher.EndInit();
+        
+        return Task.CompletedTask;
+    }
+
+    private void HandleFileChanged(object sender, FileSystemEventArgs e)
+    {
+        fileHandlerService.HandleFile(e.FullPath);   
+    }
+
+    public Task StopAsync(CancellationToken cancellationToken)
+    {
+        watcher.Dispose();
+        return Task.CompletedTask;
+    }
+}
\ No newline at end of file
diff --git a/Pulsar/Features/Journal/JournalController.cs b/Pulsar/Features/Journal/JournalController.cs
new file mode 100644
index 0000000..85ec95e
--- /dev/null
+++ b/Pulsar/Features/Journal/JournalController.cs
@@ -0,0 +1,8 @@
+namespace Pulsar.Features.Journal;
+
+[ApiController]
+[Route("api/journal")]
+public class JournalController : ControllerBase
+{
+    
+}
\ No newline at end of file
diff --git a/Pulsar/Features/Status/StatusController.cs b/Pulsar/Features/Status/StatusController.cs
index f3c984c..fc70715 100644
--- a/Pulsar/Features/Status/StatusController.cs
+++ b/Pulsar/Features/Status/StatusController.cs
@@ -1,5 +1,3 @@
-using Microsoft.AspNetCore.SignalR;
-
 namespace Pulsar.Features.Status;
 
 [ApiController]
diff --git a/Pulsar/Features/Status/StatusService.cs b/Pulsar/Features/Status/StatusService.cs
new file mode 100644
index 0000000..c71943e
--- /dev/null
+++ b/Pulsar/Features/Status/StatusService.cs
@@ -0,0 +1,18 @@
+namespace Pulsar.Features.Status;
+
+public class StatusService : IStatusService
+{
+    public void HandleFile(string fileInfo)
+    {
+        throw new NotImplementedException();
+    }
+}
+
+public interface IStatusService : IJournalHandler
+{
+}
+
+public interface IJournalHandler
+{
+    void HandleFile(string fileInfo);
+}
\ No newline at end of file
diff --git a/Pulsar/Global.Usings.cs b/Pulsar/Global.Usings.cs
index c688c13..e72b6b0 100644
--- a/Pulsar/Global.Usings.cs
+++ b/Pulsar/Global.Usings.cs
@@ -1,9 +1,11 @@
 global using Pulsar;
 global using Pulsar.Utils;
+global using Pulsar.Features.Status;
 global using System.Text;
 global using System.Text.Json;
 global using System.Text.Json.Nodes;
 global using System.Text.Json.Serialization;
 global using Microsoft.AspNetCore.Mvc;
+global using Microsoft.AspNetCore.SignalR;
 global using Microsoft.Extensions.Options;
 
diff --git a/Pulsar/LoggingUtils.cs b/Pulsar/LoggingUtils.cs
deleted file mode 100644
index 6f74ebb..0000000
--- a/Pulsar/LoggingUtils.cs
+++ /dev/null
@@ -1,29 +0,0 @@
-namespace Pulsar;
-
-public static class LoggingUtils
-{
-    internal static void LogError(Exception ex, string context)
-    {
-        var docPath = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
-        var errorMessage = new StringBuilder();
-        var timestamp = DateTime.Now.ToString("G");
-        errorMessage
-            .AppendLine($"[{timestamp}] Error encountered in Elite Observatory {context}")
-            .AppendLine(FormatExceptionMessage(ex))
-            .AppendLine();
-        File.AppendAllText(docPath + Path.DirectorySeparatorChar + "ObservatoryCrashLog.txt",
-            errorMessage.ToString());
-    }
-
-    static string FormatExceptionMessage(Exception ex, bool inner = false)
-    {
-        var errorMessage = new StringBuilder();
-        errorMessage
-            .AppendLine($"{(inner ? "Inner e" : "E")}xception message: {ex.Message}")
-            .AppendLine("Stack trace:")
-            .AppendLine(ex.StackTrace);
-        if (ex.InnerException != null)
-            errorMessage.AppendLine(FormatExceptionMessage(ex.InnerException, true));
-        return errorMessage.ToString();
-    }
-}
\ No newline at end of file
diff --git a/Pulsar/Program.cs b/Pulsar/Program.cs
index 3208dfd..cc768c2 100644
--- a/Pulsar/Program.cs
+++ b/Pulsar/Program.cs
@@ -19,6 +19,7 @@ builder.Services.Configure<JsonOptions>(options =>
     options.JsonSerializerOptions.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull);
 builder.Services.AddReverseProxy().LoadFromConfig(builder.Configuration.GetSection("ReverseProxy"));
 builder.Services.AddSpaYarp();
+builder.Services.AddHostedService<FileWatcherService>();
 
 var app = builder.Build();