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(); } }