using Engine.Graphics;
using Engine.Input;
using Serilog;
using Serilog.Core;
using Serilog.Events;
using Serilog.Sinks.SystemConsole.Themes;
namespace Engine;
///
/// Provides a builder for creating and configuring an instance of the class.
///
public sealed class EngineBuilder
{
///
/// The title of the application window.
///
private string _title = "";
///
/// Indicates whether the engine should run in headless mode.
///
private bool _headless;
///
/// The width of the rendering window.
///
private int _width = 1;
///
/// The height of the rendering window.
///
private int _height = 1;
///
/// The path to the asset folder.
///
private string _assetFolder = "./asset";
///
/// The path to the data folder.
///
private string _dataFolder = "./data";
///
/// The input handler factory.
///
private Func? _inputHandlerFunc;
///
/// The presenter factory.
///
private Func? _presenterFunc;
// Logging
///
/// Indicates whether to log to the console.
///
private bool _logToConsole;
///
/// Indicates whether to log to a file.
///
private bool _logToFile;
///
/// The path to the log file.
///
private string? _logFilePath;
///
/// The log level.
///
private LogEventLevel _logLevel = LogEventLevel.Information;
///
/// Sets the title of the engine window.
///
/// The title to use for the engine window.
/// The current instance of for chaining.
public EngineBuilder Title(string parTitle)
{
_title = parTitle;
return this;
}
///
/// Configures the engine to run in headless mode.
///
/// Indicates whether to enable headless mode. Defaults to true.
/// The current instance of for chaining.
public EngineBuilder Headless(bool parHeadless = true)
{
_headless = parHeadless;
return this;
}
///
/// Sets the width of the engine window.
///
/// The width in pixels. Must be greater than zero.
/// The current instance of for chaining.
public EngineBuilder Width(int parWidth)
{
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(parWidth);
_width = parWidth;
return this;
}
///
/// Sets the height of the engine window.
///
/// The height in pixels. Must be greater than zero.
/// The current instance of for chaining.
public EngineBuilder Height(int parHeight)
{
ArgumentOutOfRangeException.ThrowIfNegativeOrZero(parHeight);
_height = parHeight;
return this;
}
///
/// Sets the folder path for assets used by the engine.
///
/// The folder path containing asset files.
/// The current instance of for chaining.
public EngineBuilder AssetFolder(string parAssetFolder)
{
_assetFolder = parAssetFolder;
return this;
}
///
/// Sets the folder path for data files used by the engine.
///
/// The folder path containing data files.
/// The current instance of for chaining.
public EngineBuilder DataFolder(string parDataFolder)
{
_dataFolder = parDataFolder;
return this;
}
///
/// Specifies the input handler to be used by the engine.
///
/// A function that creates an input handler for the engine.
/// The current instance of for chaining.
public EngineBuilder InputHandler(Func parInputHandlerFunc)
{
_inputHandlerFunc = parInputHandlerFunc;
return this;
}
///
/// Specifies the presenter to be used by the engine.
///
/// A function that creates a presenter for the engine.
/// The current instance of for chaining.
public EngineBuilder Presenter(Func parPresenterFunc)
{
_presenterFunc = parPresenterFunc;
return this;
}
///
/// Configures logging to output to the console.
///
/// Indicates whether to enable console logging. Defaults to true.
/// The current instance of for chaining.
public EngineBuilder LogToConsole(bool parLogToConsole = true)
{
_logToConsole = parLogToConsole;
return this;
}
///
/// Configures logging to output to a file.
///
/// Indicates whether to enable file logging. Defaults to true.
/// The path of the log file. Cannot be null if file logging is enabled.
/// The current instance of for chaining.
/// Thrown if is null when is true.
public EngineBuilder LogToFile(bool parLogToFile = true, string? parLogFilePath = null)
{
if (parLogToFile && parLogFilePath == null)
{
throw new ArgumentNullException(nameof(parLogFilePath));
}
_logToFile = parLogToFile;
_logFilePath = parLogFilePath;
return this;
}
///
/// Sets the minimum log level for logging.
///
/// The minimum level of log events to capture.
/// The current instance of for chaining.
public EngineBuilder LogLevel(LogEventLevel parLogLevel)
{
_logLevel = parLogLevel;
return this;
}
///
/// Builds and returns a new instance of the class based on the configured settings.
///
/// A fully configured instance of .
public Engine Build()
{
var logger = BuildLogger();
var engine = new Engine(_width, _height, _headless, _title, _assetFolder, _dataFolder, logger);
var presenter = _presenterFunc?.Invoke(engine);
if (presenter != null)
{
engine.Presenter = presenter;
}
var inputHandler = _inputHandlerFunc?.Invoke(engine);
if (inputHandler != null)
{
engine.InputHandler = inputHandler;
}
return engine;
}
///
/// Configures and builds a logger based on the current logging settings.
///
/// A configured instance of .
private Logger BuildLogger()
{
const string template =
"[{Timestamp:yyyy-MM-dd HH:mm:ss.fff}] [{Level:u3}] [{ThreadName,-15:l}:{ThreadId,-4:d4}] [{SourceContext:l}] {Message:lj}{NewLine}{Exception}";
var loggerConfiguration = new LoggerConfiguration()
.MinimumLevel.Is(_logLevel)
.Enrich.WithThreadName()
.Enrich.WithThreadId()
.Enrich.FromLogContext();
if (_logToConsole)
{
loggerConfiguration.WriteTo.Console(
outputTemplate: template,
theme: AnsiConsoleTheme.Literate
);
}
if (_logToFile)
{
loggerConfiguration.WriteTo.File(
_logFilePath!,
outputTemplate: template
);
}
return loggerConfiguration.CreateLogger();
}
}