using System; using System.Linq; using UnityEditor; using UnityEngine; using LLM.Editor.Data; using System.Reflection; using LLM.Editor.Commands; using System.Collections.Generic; namespace LLM.Editor.Core { /// /// Responsible for finding and executing commands from the session queue. /// [InitializeOnLoad] public static class CommandExecutor { private static List _commandQueue; private static CommandContext _currentContext; public static Action OnQueueUpdated; static CommandExecutor() { // This will run when the editor loads, including after a recompile. EditorApplication.delayCall += Initialize; } private static void Initialize() { Debug.Log("[CommandExecutor] Initializing..."); if (SessionManager.HasActiveSession()) { _commandQueue = SessionManager.LoadCommandQueue(); if (!_commandQueue.Any()) { SessionManager.EndSession(); return; } Debug.Log($"[CommandExecutor] Resuming session with {_commandQueue.Count} commands in queue."); _currentContext = new CommandContext(); OnQueueUpdated?.Invoke(); } else { _commandQueue = new List(); } } public static void SetQueue(List commands) { _commandQueue = commands; _currentContext = new CommandContext(); SessionManager.SaveCommandQueue(_commandQueue); OnQueueUpdated?.Invoke(); } private static void ClearQueue() { _commandQueue.Clear(); SessionManager.SaveCommandQueue(_commandQueue); OnQueueUpdated?.Invoke(); } public static bool HasPendingCommands() => _commandQueue != null && _commandQueue.Any(); public static CommandData GetNextCommand() => HasPendingCommands() ? _commandQueue.First() : null; public static void ExecuteNextCommand() { if (!HasPendingCommands()) { Debug.LogWarning("[CommandExecutor] No commands to execute."); return; } var commandData = _commandQueue.First(); _commandQueue.RemoveAt(0); try { var commandInstance = CreateCommandInstance(commandData); if (commandInstance != null) { Debug.Log($"[CommandExecutor] Executing: {commandData.commandName}"); var outcome = commandInstance.Execute(_currentContext); var message = outcome == CommandOutcome.Success ? commandData.messages?.onSuccess : commandData.messages?.onError; new DisplayMessageCommand(new DisplayMessageParams { message = message, outcome = outcome }).Execute(_currentContext); if (outcome == CommandOutcome.Error) { Debug.LogError($"[CommandExecutor] Command '{commandData.commandName}' failed. Clearing remaining command queue."); ClearQueue(); } } else { Debug.LogError($"[CommandExecutor] Could not create instance for command: {commandData.commandName}"); } } catch(Exception e) { Debug.LogError($"[CommandExecutor] Failed to execute command '{commandData.commandName}'. Error: {e.Message}"); ClearQueue(); } // Save the modified queue SessionManager.SaveCommandQueue(_commandQueue); OnQueueUpdated?.Invoke(); } private static ICommand CreateCommandInstance(CommandData data) { // Use reflection to find the command class in the Commands namespace // This makes the system extensible without needing a giant switch statement. var commandClassName = $"{data.commandName}Command"; var type = Assembly.GetExecutingAssembly().GetTypes() .FirstOrDefault(t => t.Namespace == "LLM.Editor.Commands" && t.Name == commandClassName); if (type != null) { // Assumes commands have a constructor that takes a single string (the JSON parameters) return (ICommand)Activator.CreateInstance(type, data.jsonData); } Debug.LogError($"[CommandExecutor] Command type '{commandClassName}' not found."); return null; } } }