"""
Fog weather engine.

Creates a density overlay effect simulating fog, mist, or haze.
"""

import math
import random
from typing import List

from soracli.engines.base import BaseWeatherEngine, Particle, EngineConfig


class FogEngine(BaseWeatherEngine):
    """
    Fog simulation engine.
    
    Creates a dynamic fog/mist effect with varying density layers.
    """
    
    # Fog density characters (from sparse to dense)
    FOG_CHARS_SPARSE = [' ', ' ', ' ', '·', '.']
    FOG_CHARS_LIGHT = ['·', '.', '∘', '°', ' ', ' ']
    FOG_CHARS_MEDIUM = ['░', '·', '.', '∘', '°', '·']
    FOG_CHARS_DENSE = ['░', '▒', '·', '∘', '░']
    FOG_CHARS_THICK = ['▒', '░', '▓', '░', '▒']
    
    def __init__(self, config: EngineConfig = None):
        super().__init__(config)
        self.time = 0.0
        self.density_map: List[List[float]] = []
        self.wave_offsets = [random.uniform(0, math.pi * 2) for _ in range(5)]
        
    def get_particle_chars(self) -> List[str]:
        """Return fog character set based on intensity."""
        if self.config.intensity > 1.5:
            return self.FOG_CHARS_THICK
        elif self.config.intensity > 1.2:
            return self.FOG_CHARS_DENSE
        elif self.config.intensity > 0.8:
            return self.FOG_CHARS_MEDIUM
        elif self.config.intensity > 0.4:
            return self.FOG_CHARS_LIGHT
        return self.FOG_CHARS_SPARSE
    
    def initialize_particles(self) -> None:
        """Initialize fog density map."""
        self._generate_density_map()
        self.particles = []  # Fog doesn't use traditional particles
    
    def _generate_density_map(self) -> None:
        """Generate initial fog density map using Perlin-like noise."""
        self.density_map = []
        
        for y in range(self.config.height):
            row = []
            for x in range(self.config.width):
                # Layer multiple sine waves for organic fog pattern
                density = self._calculate_density(x, y)
                row.append(density)
            self.density_map.append(row)
    
    def _calculate_density(self, x: int, y: int) -> float:
        """Calculate fog density at a specific point."""
        # Combine multiple waves at different frequencies
        density = 0.0
        
        # Large slow-moving waves
        density += math.sin(x * 0.05 + self.time * 0.3 + self.wave_offsets[0]) * 0.3
        density += math.sin(y * 0.08 + self.time * 0.2 + self.wave_offsets[1]) * 0.2
        
        # Medium waves
        density += math.sin(x * 0.1 + y * 0.05 + self.time * 0.5 + self.wave_offsets[2]) * 0.25
        
        # Small detail waves
        density += math.sin(x * 0.2 + self.time * 0.8 + self.wave_offsets[3]) * 0.15
        density += math.sin(y * 0.15 + self.time * 0.6 + self.wave_offsets[4]) * 0.1
        
        # Normalize to 0-1 range
        density = (density + 1.0) / 2.0
        
        # Apply intensity modifier
        density *= self.config.intensity
        
        # Fog tends to be denser at the bottom
        height_factor = 1.0 - (y / self.config.height) * 0.3
        density *= height_factor
        
        return max(0.0, min(1.0, density))
    
    def _density_to_char(self, density: float) -> str:
        """Convert density value to appropriate character."""
        if density > 0.8:
            chars = self.FOG_CHARS_THICK
        elif density > 0.6:
            chars = self.FOG_CHARS_DENSE
        elif density > 0.4:
            chars = self.FOG_CHARS_MEDIUM
        elif density > 0.2:
            chars = self.FOG_CHARS_LIGHT
        else:
            chars = self.FOG_CHARS_SPARSE
        
        return random.choice(chars)
    
    def update_particles(self) -> None:
        """Update fog density patterns."""
        self.time += 0.1
        
        # Regenerate density map with new time offset
        self._generate_density_map()
    
    def render_frame(self) -> str:
        """Render the fog frame."""
        # Create buffer from density map
        buffer = []
        
        for y in range(self.config.height):
            row = []
            for x in range(self.config.width):
                if y < len(self.density_map) and x < len(self.density_map[y]):
                    density = self.density_map[y][x]
                    char = self._density_to_char(density)
                else:
                    char = ' '
                row.append(char)
            buffer.append(row)
        
        # Apply fog coloring
        return self._render_fog_buffer(buffer)
    
    def _render_fog_buffer(self, buffer: List[List[str]]) -> str:
        """Render fog buffer with appropriate colors."""
        output = []
        
        # Get fog color from theme or use default
        fog_colors = self.config.theme.get('fog_colors', ['gray', 'white'])
        
        for y, row in enumerate(buffer):
            line = ""
            for x, char in enumerate(row):
                if char != ' ':
                    # Apply color based on density
                    density = self.density_map[y][x] if y < len(self.density_map) else 0
                    color_code = self._get_color_for_density(density)
                    line += f"{color_code}{char}\033[0m"
                else:
                    line += char
            output.append(line)
        
        return '\n'.join(output)
    
    def _get_color_for_density(self, density: float) -> str:
        """Get ANSI color code based on fog density."""
        # Grayscale based on density
        if density > 0.7:
            return "\033[38;5;250m"  # Bright gray
        elif density > 0.5:
            return "\033[38;5;245m"  # Medium gray
        elif density > 0.3:
            return "\033[38;5;240m"  # Dark gray
        else:
            return "\033[38;5;235m"  # Very dark gray
    
    def set_fog_thickness(self, thickness: float) -> None:
        """Set fog thickness (0.0 to 2.0)."""
        self.set_intensity(thickness)
