"""Core ASCII art conversion functionality."""

import numpy as np
from typing import List, Tuple, Optional
import cv2
from .config import CharacterSet, ColorTheme


class ASCIIConverter:
    """Converts image frames to ASCII art."""
    
    def __init__(
        self,
        charset: CharacterSet = CharacterSet.SIMPLE,
        color_theme: Optional[ColorTheme] = None,
        invert: bool = False
    ):
        """
        Initialize the ASCII converter.
        
        Args:
            charset: Character set to use for conversion
            color_theme: Optional color theme for colored output
            invert: Whether to invert brightness mapping
        """
        self.charset = charset
        self.color_theme = color_theme
        self.invert = invert
        self.chars = self._get_charset_chars()
        
    def _get_charset_chars(self) -> str:
        """Get character string for the selected charset."""
        if self.charset == CharacterSet.SIMPLE:
            return " .:-=+*#%@"
        elif self.charset == CharacterSet.DETAILED:
            return " .,'~!;<>+*xvXGM#8@"
        elif self.charset == CharacterSet.BLOCK:
            return " ░▒▓█"
        else:
            return " .:-=+*#%@"  # Default fallback
    
    def frame_to_ascii(
        self, 
        frame: np.ndarray, 
        width: int, 
        height: int
    ) -> str:
        """
        Convert a video frame to ASCII art.
        
        Args:
            frame: Input video frame (BGR format from OpenCV)
            width: Desired ASCII art width
            height: Desired ASCII art height
            
        Returns:
            ASCII art string representation of the frame
        """
        # Convert BGR to grayscale
        gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
        
        # Resize to target dimensions
        resized = cv2.resize(gray, (width, height), interpolation=cv2.INTER_AREA)
        
        # Convert to ASCII
        ascii_art = self._pixels_to_ascii(resized)
        
        # Apply colors if theme is set
        if self.color_theme:
            ascii_art = self._apply_colors(ascii_art, resized)
            
        return ascii_art
    
    def _pixels_to_ascii(self, image: np.ndarray) -> str:
        """Convert grayscale image pixels to ASCII characters."""
        # Normalize pixel values to character index range
        char_count = len(self.chars)
        
        if self.invert:
            # Invert for dark backgrounds (bright pixels = dense chars)
            char_indices = ((255 - image) * (char_count - 1) // 255).astype(int)
        else:
            # Normal mapping (dark pixels = sparse chars)
            char_indices = (image * (char_count - 1) // 255).astype(int)
        
        # Ensure indices are within bounds
        char_indices = np.clip(char_indices, 0, char_count - 1)
        
        # Convert to ASCII
        rows = []
        for row in char_indices:
            ascii_row = ''.join(self.chars[idx] for idx in row)
            rows.append(ascii_row)
            
        return '\n'.join(rows)
    
    def _apply_colors(self, ascii_art: str, image: np.ndarray) -> str:
        """Apply color theme to ASCII art."""
        if not self.color_theme:
            return ascii_art
            
        lines = ascii_art.split('\n')
        colored_lines = []
        
        for i, line in enumerate(lines):
            if i >= image.shape[0]:
                break
                
            colored_line = ""
            for j, char in enumerate(line):
                if j >= image.shape[1]:
                    break
                    
                # Get pixel brightness for color selection
                brightness = image[i, j]
                color_code = self._get_color_code(brightness)
                
                # Apply color to character
                colored_line += f"{color_code}{char}"
                
            # Reset color at end of line
            colored_line += "\033[0m"
            colored_lines.append(colored_line)
            
        return '\n'.join(colored_lines)
    
    def _get_color_code(self, brightness: int) -> str:
        """Get ANSI color code based on brightness and theme."""
        if self.color_theme == ColorTheme.MATRIX:
            # Green gradient
            if brightness < 64:
                return "\033[32m"      # Dark green
            elif brightness < 128:
                return "\033[92m"      # Light green  
            else:
                return "\033[97m"      # White
                
        elif self.color_theme == ColorTheme.AMBER:
            # Amber gradient
            if brightness < 64:
                return "\033[33m"      # Dark yellow
            elif brightness < 128:
                return "\033[93m"      # Light yellow
            else:
                return "\033[97m"      # White
                
        elif self.color_theme == ColorTheme.CYAN:
            # Cyan gradient
            if brightness < 64:
                return "\033[36m"      # Dark cyan
            elif brightness < 128:
                return "\033[96m"      # Light cyan
            else:
                return "\033[97m"      # White
                
        elif self.color_theme == ColorTheme.RAINBOW:
            # Rainbow mapping
            hue = brightness * 6 // 256  # 0-5 range
            colors = [
                "\033[31m",    # Red
                "\033[33m",    # Yellow  
                "\033[32m",    # Green
                "\033[36m",    # Cyan
                "\033[34m",    # Blue
                "\033[35m",    # Magenta
            ]
            return colors[min(hue, 5)]
            
        return ""  # No color
