using Firebird2D.EventPipelines.EventArguments; using Firebird2D.EventPipelines; using SFML.Window; using System.Runtime.CompilerServices; using System.Text.Json; namespace Firebird2D.KeyMapping { /// /// Maps input events (keyboard, mouse, joystick) to custom Firebird events and manages their pipelines. /// public class KeyMapper { private EventBus eventBus; // Stores registered input events by name. private Dictionary inputEvents = []; // Stores the expected argument type for each input event. private Dictionary eventTypes = []; // Maps event names to FirebirdEvent identifiers. private Dictionary inputMapping = []; // Caches active pipelines for performance. private Dictionary cachedPipelines = []; public KeyMapper(EventBus eventBus) { if (eventBus == null) throw new NullReferenceException(nameof(eventBus)); this.eventBus = eventBus; } /// /// Registers an input event with its handler and links it to a FirebirdEvent. /// public void RegisterEvent(string EventName, EventHandler handler, FirebirdEvent eventNumber) where T : EventArgs { if (inputEvents.ContainsKey(EventName)) throw new ArgumentException($"Event {EventName} already exists."); inputEvents.Add(EventName, handler); eventTypes.Add(EventName, typeof(T)); Map(EventName, eventNumber); } /// /// Maps an existing input event to a new FirebirdEvent. /// public void Map(string EventName, FirebirdEvent eventNumber) { if (inputMapping.ContainsKey(EventName)) { RemoveMapping(EventName); inputMapping[EventName] = eventNumber; } else { inputMapping.Add(EventName, eventNumber); } AjustPipeline(EventName, eventNumber); } /// /// Removes the current mapping and unsubscribes from the pipeline. /// private void RemoveMapping(string EventName) { if (!inputMapping.TryGetValue(EventName, out var firebirdEvent)) return; if (!eventTypes.TryGetValue(EventName, out var type)) return; I_Pipeline? pipeline = eventBus.GetPipeline(firebirdEvent, type); if (pipeline != null) { pipeline.Unsubscribe(inputEvents[EventName]); if (pipeline.IsEventEmpty) { eventBus.DestroyPipeline(firebirdEvent, this); cachedPipelines.Remove(firebirdEvent); } } inputMapping.Remove(EventName); } /// /// Creates or adjusts a pipeline to reflect current mappings. /// private void AjustPipeline(string EventName, FirebirdEvent eventNumber) { if (!eventTypes.TryGetValue(EventName, out var type)) return; I_Pipeline pipeline = eventBus.GetOrBuildPipeline(eventNumber, type, this); pipeline.Subscribe(inputEvents[EventName]); } /// /// Serializes and saves the current mapping to a file. /// public void SaveMapping(string Path) { string json = JsonSerializer.Serialize(inputMapping); File.WriteAllText(Path, json); } /// /// Loads and applies an input mapping from a JSON file. /// public void LoadMapping(string Path) { string json = File.ReadAllText(Path); Dictionary? keyValuePairs = JsonSerializer.Deserialize>(json); if (keyValuePairs == null) throw new FileLoadException("Unable to deserialize input mapping."); cachedPipelines.Clear(); foreach (var kvp in inputMapping) { RemoveMapping(kvp.Key); } inputMapping = keyValuePairs; foreach (var kvp in inputMapping) { if (inputEvents.ContainsKey(kvp.Key)) { AjustPipeline(kvp.Key, kvp.Value); } } } /// /// Removes a pipeline from the internal cache. /// public void InvalidatePipelineCache(FirebirdEvent fbEvent) { cachedPipelines.Remove(fbEvent); } /// /// Retrieves a cached pipeline or fetches and caches it. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] private Pipeline? GetOrCachePipeline(FirebirdEvent fbEvent) where T : EventArgs { if (cachedPipelines.TryGetValue(fbEvent, out var pipelineObj) && pipelineObj is Pipeline cached) return cached; var pipeline = eventBus?.GetPipeline(fbEvent, typeof(T)) as Pipeline; if (pipeline != null) cachedPipelines[fbEvent] = pipeline; return pipeline; } /// /// Handles a keyboard input and dispatches the event. /// public void HandleKeyboardInput(KeyEvent key, InputEventType type) { var pipeline = GetOrCachePipeline((FirebirdEvent)key.Code); pipeline?.SendEvent(this, new FirebirdKeyEventArgs(key, type)); } /// /// Handles a mouse button input and dispatches the event. /// public void HandleMouseInput(MouseButtonEvent button, InputEventType type) { var pipeline = GetOrCachePipeline((FirebirdEvent)(button.Button + 101)); pipeline?.SendEvent(this, new FirebirdMouseButtonEventArgs(button, type)); } /// /// Handles a joystick button press and dispatches the event. /// public void HandleJoystickButtonInput(JoystickButtonEvent joystickButton, InputEventType type) { var pipeline = GetOrCachePipeline(FirebirdEvent.JoystickButton); pipeline?.SendEvent(this, new FirebirdJoystickButtonEventArgs(joystickButton, type)); } /// /// Handles mouse movement and dispatches the event. /// public void HandleMouseMovement(MouseMoveEvent mouseMove) { var pipeline = GetOrCachePipeline(FirebirdEvent.MouseMove); pipeline?.SendEvent(this, new MouseMoveEventArgs(mouseMove)); } /// /// Handles joystick movement and dispatches the event. /// public void HandleJoytickMove(JoystickMoveEvent joystickMove) { var pipeline = GetOrCachePipeline(FirebirdEvent.JoystickMove); pipeline?.SendEvent(this, new JoystickMoveEventArgs(joystickMove)); } } }