// 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;
}
}