using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using StardewModdingAPI.Toolkit.Utilities;
namespace StardewModdingAPI.Internal.ConsoleWriting
{
/// Writes color-coded text to the console.
internal class ColorfulConsoleWriter : IConsoleWriter
{
/*********
** Fields
*********/
/// The console text color for each log level.
private readonly IDictionary? Colors;
/// Whether the current console supports color formatting.
[MemberNotNullWhen(true, nameof(ColorfulConsoleWriter.Colors))]
private bool SupportsColor { get; }
/*********
** Public methods
*********/
/// Construct an instance.
/// The target platform.
public ColorfulConsoleWriter(Platform platform)
: this(platform, ColorfulConsoleWriter.GetDefaultColorSchemeConfig(MonitorColorScheme.AutoDetect)) { }
/// Construct an instance.
/// The target platform.
/// The colors to use for text written to the SMAPI console.
public ColorfulConsoleWriter(Platform platform, ColorSchemeConfig colorConfig)
{
if (colorConfig.UseScheme == MonitorColorScheme.None)
{
this.SupportsColor = false;
this.Colors = null;
}
else
{
this.SupportsColor = this.TestColorSupport();
this.Colors = this.GetConsoleColorScheme(platform, colorConfig);
}
}
/// Write a message line to the log.
/// The message to log.
/// The log level.
public void WriteLine(string message, ConsoleLogLevel level)
{
if (this.SupportsColor)
{
if (level == ConsoleLogLevel.Critical)
{
Console.BackgroundColor = ConsoleColor.Red;
Console.ForegroundColor = ConsoleColor.White;
Console.WriteLine(message);
Console.ResetColor();
}
else
{
Console.ForegroundColor = this.Colors[level];
Console.WriteLine(message);
Console.ResetColor();
}
}
else
Console.WriteLine(message);
}
/// Get the default color scheme config for cases where it's not configurable (e.g. the installer).
/// The default color scheme ID to use, or to select one automatically.
/// The colors here should be kept in sync with the SMAPI config file.
public static ColorSchemeConfig GetDefaultColorSchemeConfig(MonitorColorScheme useScheme)
{
return new ColorSchemeConfig(
useScheme: useScheme,
schemes: new Dictionary>
{
[MonitorColorScheme.DarkBackground] = new Dictionary
{
[ConsoleLogLevel.Trace] = ConsoleColor.DarkGray,
[ConsoleLogLevel.Debug] = ConsoleColor.DarkGray,
[ConsoleLogLevel.Info] = ConsoleColor.White,
[ConsoleLogLevel.Warn] = ConsoleColor.Yellow,
[ConsoleLogLevel.Error] = ConsoleColor.Red,
[ConsoleLogLevel.Alert] = ConsoleColor.Magenta,
[ConsoleLogLevel.Success] = ConsoleColor.DarkGreen
},
[MonitorColorScheme.LightBackground] = new Dictionary
{
[ConsoleLogLevel.Trace] = ConsoleColor.DarkGray,
[ConsoleLogLevel.Debug] = ConsoleColor.DarkGray,
[ConsoleLogLevel.Info] = ConsoleColor.Black,
[ConsoleLogLevel.Warn] = ConsoleColor.DarkYellow,
[ConsoleLogLevel.Error] = ConsoleColor.Red,
[ConsoleLogLevel.Alert] = ConsoleColor.DarkMagenta,
[ConsoleLogLevel.Success] = ConsoleColor.DarkGreen
}
}
);
}
/*********
** Private methods
*********/
/// Test whether the current console supports color formatting.
private bool TestColorSupport()
{
try
{
Console.ForegroundColor = Console.ForegroundColor;
return true;
}
catch (Exception)
{
return false; // Mono bug
}
}
/// Get the color scheme to use for the current console.
/// The target platform.
/// The colors to use for text written to the SMAPI console.
private IDictionary GetConsoleColorScheme(Platform platform, ColorSchemeConfig colorConfig)
{
// get color scheme ID
MonitorColorScheme schemeID = colorConfig.UseScheme;
if (schemeID == MonitorColorScheme.AutoDetect)
{
schemeID = platform == Platform.Mac
? MonitorColorScheme.LightBackground // macOS doesn't provide console background color info, but it's usually white.
: ColorfulConsoleWriter.IsDark(Console.BackgroundColor) ? MonitorColorScheme.DarkBackground : MonitorColorScheme.LightBackground;
}
// get colors for scheme
return colorConfig.Schemes.TryGetValue(schemeID, out IDictionary? scheme)
? scheme
: throw new NotSupportedException($"Unknown color scheme '{schemeID}'.");
}
/// Get whether a console color should be considered dark, which is subjectively defined as 'white looks better than black on this text'.
/// The color to check.
private static bool IsDark(ConsoleColor color)
{
switch (color)
{
case ConsoleColor.Black:
case ConsoleColor.Blue:
case ConsoleColor.DarkBlue:
case ConsoleColor.DarkMagenta: // PowerShell
case ConsoleColor.DarkRed:
case ConsoleColor.Red:
return true;
default:
return false;
}
}
}
}