import time from engine.effects.performance import PerformanceMonitor, get_monitor from engine.effects.registry import EffectRegistry from engine.effects.types import EffectContext class EffectChain: def __init__( self, registry: EffectRegistry, monitor: PerformanceMonitor | None = None ): self._registry = registry self._order: list[str] = [] self._monitor = monitor def _get_monitor(self) -> PerformanceMonitor: if self._monitor is not None: return self._monitor return get_monitor() def set_order(self, names: list[str]) -> None: self._order = list(names) def get_order(self) -> list[str]: return self._order.copy() def add_effect(self, name: str, position: int | None = None) -> bool: if name not in self._registry.list_all(): return False if position is None: self._order.append(name) else: self._order.insert(position, name) return True def remove_effect(self, name: str) -> bool: if name in self._order: self._order.remove(name) return True return False def reorder(self, new_order: list[str]) -> bool: all_plugins = set(self._registry.list_all().keys()) if not all(name in all_plugins for name in new_order): return False self._order = list(new_order) return True def process(self, buf: list[str], ctx: EffectContext) -> list[str]: monitor = self._get_monitor() frame_number = ctx.frame_number monitor.start_frame(frame_number) frame_start = time.perf_counter() result = list(buf) for name in self._order: plugin = self._registry.get(name) if plugin and plugin.config.enabled: chars_in = sum(len(line) for line in result) effect_start = time.perf_counter() try: result = plugin.process(result, ctx) except Exception: plugin.config.enabled = False elapsed = time.perf_counter() - effect_start chars_out = sum(len(line) for line in result) monitor.record_effect(name, elapsed * 1000, chars_in, chars_out) total_elapsed = time.perf_counter() - frame_start monitor.end_frame(frame_number, total_elapsed * 1000) return result