// Copyright (c) 2025-2026 GeWuYou // SPDX-License-Identifier: Apache-2.0 using System; using System.Collections.Generic; using GFramework.Core.Abstractions.Logging; namespace GFramework.Godot.Logging; /// /// Godot logger formatting and routing options. /// public sealed class GodotLoggerOptions { private static readonly IReadOnlyDictionary DefaultColors = new Dictionary { [LogLevel.Trace] = "gray", [LogLevel.Debug] = "cyan", [LogLevel.Info] = "white", [LogLevel.Warning] = "orange", [LogLevel.Error] = "red", [LogLevel.Fatal] = "deep_pink" }; /// /// Gets or sets the output mode. /// public GodotLoggerMode Mode { get; set; } = GodotLoggerMode.Debug; /// /// Gets or sets the minimum level used by . /// public LogLevel DebugMinLevel { get; set; } = LogLevel.Debug; /// /// Gets or sets the minimum level used by . /// public LogLevel ReleaseMinLevel { get; set; } = LogLevel.Info; /// /// Gets or sets the BBCode-capable template used by . /// #pragma warning disable MA0016 // Keep configuration mutable for object initializer and serializer scenarios. public string DebugOutputTemplate { get; set; } = "[{timestamp:yyyy-MM-dd HH:mm:ss.fff}] [color={color}][{level:u3}][/color] [{category:l16}] {message}{properties}"; #pragma warning restore MA0016 /// /// Gets or sets the plain text template used by . /// #pragma warning disable MA0016 // Keep configuration mutable for object initializer and serializer scenarios. public string ReleaseOutputTemplate { get; set; } = "[{timestamp:yyyy-MM-dd HH:mm:ss.fff}] [{level:u3}] [{category:l16}] {message}{properties}"; #pragma warning restore MA0016 /// /// Gets or sets Godot named colors by log level. /// #pragma warning disable MA0016 // Keep configuration mutable for object initializer and serializer scenarios. public Dictionary Colors { get; set; } = new(DefaultColors); #pragma warning restore MA0016 /// /// Creates options that preserve the previous Godot logger defaults for a fixed minimum level. /// /// The minimum enabled level. /// Options equivalent to the previous fixed-format logger behavior. public static GodotLoggerOptions ForMinimumLevel(LogLevel minLevel) { return new GodotLoggerOptions { Mode = GodotLoggerMode.Debug, DebugMinLevel = minLevel, ReleaseMinLevel = minLevel, DebugOutputTemplate = "[{timestamp:yyyy-MM-dd HH:mm:ss.fff}] {level:padded} [{category}] {message}{properties}", ReleaseOutputTemplate = "[{timestamp:yyyy-MM-dd HH:mm:ss.fff}] {level:padded} [{category}] {message}{properties}", Colors = new Dictionary(DefaultColors) }; } /// /// Returns the configured color for the specified level. /// /// The level. /// The Godot named color. public string GetColor(LogLevel level) { if (Colors is { } colors && colors.TryGetValue(level, out var color) && !string.IsNullOrWhiteSpace(color)) { return color; } return DefaultColors.TryGetValue(level, out var fallback) ? fallback : "white"; } /// /// Gets the active minimum level for the current . /// /// /// when is ; otherwise /// . /// /// /// Factories use this value as the option-level floor before category-specific settings are applied. /// internal LogLevel GetEffectiveMinLevel() { return Mode == GodotLoggerMode.Debug ? DebugMinLevel : ReleaseMinLevel; } /// /// Creates a copy whose debug and release floors are at least . /// /// The minimum level that both mode-specific floors must satisfy. /// A normalized copy with stricter or equal mode-specific minimum levels. /// /// The operation can raise and through /// , but it never lowers them. , /// , and are preserved through a defensive copy. /// internal GodotLoggerOptions WithMinimumLevelFloor(LogLevel minLevel) { return new GodotLoggerOptions { Mode = Mode, DebugMinLevel = Max(DebugMinLevel, minLevel), ReleaseMinLevel = Max(ReleaseMinLevel, minLevel), DebugOutputTemplate = DebugOutputTemplate, ReleaseOutputTemplate = ReleaseOutputTemplate, Colors = CopyColorsWithDefaults(Colors) }; } /// /// Creates a copy that replaces missing templates or color mappings with safe defaults. /// /// A normalized copy suitable for runtime rendering. /// /// JSON input can set , , or /// to null even though the public API treats them as non-null. This method keeps /// deserialization and imperative configuration from publishing values that would fail during rendering. /// internal GodotLoggerOptions CreateNormalizedCopy() { var defaults = new GodotLoggerOptions(); return new GodotLoggerOptions { Mode = Mode, DebugMinLevel = DebugMinLevel, ReleaseMinLevel = ReleaseMinLevel, DebugOutputTemplate = string.IsNullOrWhiteSpace(DebugOutputTemplate) ? defaults.DebugOutputTemplate : DebugOutputTemplate, ReleaseOutputTemplate = string.IsNullOrWhiteSpace(ReleaseOutputTemplate) ? defaults.ReleaseOutputTemplate : ReleaseOutputTemplate, Colors = CopyColorsWithDefaults(Colors) }; } private static LogLevel Max(LogLevel left, LogLevel right) { return left > right ? left : right; } private static Dictionary CopyColorsWithDefaults(Dictionary? colors) { var merged = new Dictionary(DefaultColors); if (colors == null) { return merged; } foreach (var pair in colors) { if (!string.IsNullOrWhiteSpace(pair.Value)) { merged[pair.Key] = pair.Value; } } return merged; } }