""" Strategy factory for creating subject-specific formatting strategies. This module provides centralized strategy creation with auto-detection support for determining the most appropriate strategy based on content. """ from typing import Union, List from ..core.interfaces import Strategy from .math_strategy import MathStrategy from .chemistry_strategy import ChemistryStrategy from .physics_strategy import PhysicsStrategy from .statistics_strategy import StatisticsStrategy class StrategyFactory: """ Factory for creating subject-specific conversion strategies. This factory provides centralized strategy creation with support for: - Explicit strategy selection (math, chemistry, physics, statistics) - Auto-detection based on equation content - Fallback to math strategy as default """ # Singleton instances for strategy reuse _math_strategy: Union[MathStrategy, None] = None _chemistry_strategy: Union[ChemistryStrategy, None] = None _physics_strategy: Union[PhysicsStrategy, None] = None _statistics_strategy: Union[StatisticsStrategy, None] = None @classmethod def create_strategy(cls, subject_mode: str = "math") -> Strategy: """ Create a formatting strategy for the specified subject mode. Args: subject_mode: The subject area for formatting ("math", "chemistry", "physics", "statistics", or "auto") Returns: Strategy instance for the specified subject mode Examples: >>> factory = StrategyFactory() >>> strategy = factory.create_strategy("chemistry") >>> isinstance(strategy, ChemistryStrategy) True >>> strategy = factory.create_strategy("math") >>> isinstance(strategy, MathStrategy) True >>> strategy = factory.create_strategy("invalid") >>> isinstance(strategy, MathStrategy) # Falls back to math True """ subject_mode = subject_mode.lower() if subject_mode == "chemistry": if cls._chemistry_strategy is None: cls._chemistry_strategy = ChemistryStrategy() return cls._chemistry_strategy elif subject_mode == "physics": if cls._physics_strategy is None: cls._physics_strategy = PhysicsStrategy() return cls._physics_strategy elif subject_mode == "statistics": if cls._statistics_strategy is None: cls._statistics_strategy = StatisticsStrategy() return cls._statistics_strategy elif subject_mode == "auto": # Auto-detection handled by auto_detect_strategy() # For now, return math as default (will be enhanced later) if cls._math_strategy is None: cls._math_strategy = MathStrategy() return cls._math_strategy else: # Default: "math" or any unrecognized mode if cls._math_strategy is None: cls._math_strategy = MathStrategy() return cls._math_strategy @classmethod def auto_detect_strategy(cls, equation_text: str) -> Strategy: """ Auto-detect the most appropriate strategy based on equation content. Args: equation_text: The equation text to analyze Returns: Strategy instance best suited for the content Detection priority: 1. Chemistry (chemical elements, reaction arrows) 2. Physics (SI units, physical constants) 3. Statistics (statistical operators, distributions) 4. Math (default fallback) Examples: >>> factory = StrategyFactory() >>> strategy = factory.auto_detect_strategy("Fe + H2SO4 → FeSO4 + H2") >>> isinstance(strategy, ChemistryStrategy) True >>> strategy = factory.auto_detect_strategy("F = ma, where a = 9.8 m/s²") >>> isinstance(strategy, PhysicsStrategy) True >>> strategy = factory.auto_detect_strategy("X ~ Normal(μ, σ²)") >>> isinstance(strategy, StatisticsStrategy) True >>> strategy = factory.auto_detect_strategy("sin(x) + cos(x)") >>> isinstance(strategy, MathStrategy) True """ # Create strategy instances if not already created if cls._chemistry_strategy is None: cls._chemistry_strategy = ChemistryStrategy() if cls._physics_strategy is None: cls._physics_strategy = PhysicsStrategy() if cls._statistics_strategy is None: cls._statistics_strategy = StatisticsStrategy() if cls._math_strategy is None: cls._math_strategy = MathStrategy() # Priority order: Chemistry > Physics > Statistics > Math # (Chemistry has most specific indicators) if cls._chemistry_strategy.detect_context(equation_text): return cls._chemistry_strategy if cls._physics_strategy.detect_context(equation_text): return cls._physics_strategy if cls._statistics_strategy.detect_context(equation_text): return cls._statistics_strategy # Default: math strategy return cls._math_strategy @classmethod def reset_cache(cls): """ Reset the cached strategy instances. This is useful for testing or when strategy implementations change. """ cls._math_strategy = None cls._chemistry_strategy = None cls._physics_strategy = None cls._statistics_strategy = None @classmethod def get_available_strategies(cls) -> List[str]: """ Get list of available strategy names. Returns: List of strategy names that can be used with create_strategy() """ return ["math", "chemistry", "physics", "statistics", "auto"] def __repr__(self) -> str: """String representation for debugging.""" return "StrategyFactory(math, chemistry, physics, statistics, auto)"