#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""
Text Renderer
文本渲染器 - 重构版本，提供配置化的文本渲染功能
"""

import logging
from typing import Tuple, Dict, Any, Optional, List
from pathlib import Path
from PIL import Image, ImageDraw, ImageFont

logger = logging.getLogger(__name__)


class TextRenderer:
    """文本渲染工具类 - 重构版本"""
    
    def __init__(self, font_dir: Optional[str] = None, font_configs: Optional[Dict[str, Any]] = None):
        """
        初始化文本渲染器

        Args:
            font_dir: 字体目录路径
            font_configs: 字体配置字典
        """
        self.font_dir = Path(font_dir) if font_dir else None
        self.font_configs = font_configs or {}
        self._font_cache: Dict[str, ImageFont.FreeTypeFont] = {}
        self.logger = logging.getLogger(__name__)
        
        # 默认字体配置
        self.default_font_config = {
            "family": "Arial",
            "fallback": ["Times New Roman", "SimHei"]
        }
        
        logger.debug(f"文本渲染器初始化完成，字体目录: {self.font_dir}")

    def get_font(self, size: int, font_config: Optional[Dict[str, Any]] = None) -> ImageFont.FreeTypeFont:
        """
        获取字体对象

        Args:
            size: 字体大小
            font_config: 字体配置

        Returns:
            字体对象
        """
        if not font_config:
            font_config = self.font_configs.get("default", self.default_font_config)
        
        # 构建缓存键
        cache_key = f"{font_config.get('family', 'default')}_{size}"
        
        if cache_key in self._font_cache:
            return self._font_cache[cache_key]
        
        font = self._load_font(size, font_config)
        self._font_cache[cache_key] = font
        
        return font

    def _load_font(self, size: int, font_config: Dict[str, Any]) -> ImageFont.FreeTypeFont:
        """加载字体"""
        font_family = font_config.get("family", "Arial")
        fallback_fonts = font_config.get("fallback", [])
        
        # 尝试加载主字体
        font = self._try_load_font(font_family, size)
        if font:
            return font
        
        # 尝试回退字体
        for fallback_font in fallback_fonts:
            font = self._try_load_font(fallback_font, size)
            if font:
                logger.debug(f"使用回退字体: {fallback_font}")
                return font
        
        # 使用默认字体
        logger.warning(f"无法加载指定字体，使用默认字体")
        return self._load_default_font(size)

    def _try_load_font(self, font_name: str, size: int) -> Optional[ImageFont.FreeTypeFont]:
        """尝试加载指定字体"""
        try:
            # 如果提供了字体目录，优先从目录加载
            if self.font_dir and self.font_dir.exists():
                font_patterns = [
                    f"{font_name}.ttf",
                    f"{font_name}.TTF",
                    f"{font_name}.otf",
                    f"{font_name}.OTF"
                ]
                
                for pattern in font_patterns:
                    font_path = self.font_dir / pattern
                    if font_path.exists():
                        return ImageFont.truetype(str(font_path), size)
            
            # 尝试系统字体
            return ImageFont.truetype(font_name, size)
            
        except Exception as e:
            logger.debug(f"加载字体失败 {font_name}: {e}")
            return None

    def _load_default_font(self, size: int) -> ImageFont.FreeTypeFont:
        """加载默认字体"""
        try:
            return ImageFont.load_default()
        except:
            # 如果连默认字体都加载失败，创建一个基础字体
            try:
                return ImageFont.truetype("arial.ttf", size)
            except:
                # 最后的回退
                return ImageFont.load_default()

    def render_text(
        self,
        image: Image.Image,
        text: str,
        position: Tuple[int, int],
        font_size: int = 24,
        color: Tuple[int, int, int] = (255, 255, 255),
        font_config: Optional[Dict[str, Any]] = None,
        anchor: str = "la"
    ) -> Image.Image:
        """
        渲染文本到图像

        Args:
            image: 目标图像
            text: 要渲染的文本
            position: 文本位置 (x, y)
            font_size: 字体大小
            color: 文本颜色 (R, G, B)
            font_config: 字体配置
            anchor: 文本对齐方式

        Returns:
            渲染后的图像
        """
        if not text:
            return image
        
        # 复制图像避免修改原图
        result = image.copy()
        draw = ImageDraw.Draw(result)
        
        # 获取字体
        font = self.get_font(font_size, font_config)
        
        try:
            # 渲染文本
            draw.text(position, text, fill=color, font=font, anchor=anchor)
        except Exception as e:
            logger.error(f"文本渲染失败: {e}")
            # 使用默认字体重试
            try:
                default_font = self._load_default_font(font_size)
                draw.text(position, text, fill=color, font=default_font, anchor=anchor)
            except Exception as e2:
                logger.error(f"默认字体渲染也失败: {e2}")
        
        return result

    def render_text_with_effects(
        self,
        image: Image.Image,
        text: str,
        position: Tuple[int, int],
        font_size: int = 24,
        color: Tuple[int, int, int] = (255, 255, 255),
        effects: Optional[Dict[str, Any]] = None
    ) -> Image.Image:
        """
        渲染带效果的文本

        Args:
            image: 目标图像
            text: 要渲染的文本
            position: 文本位置
            font_size: 字体大小
            color: 文本颜色
            effects: 效果配置

        Returns:
            渲染后的图像
        """
        if not effects:
            effects = {}
        
        result = image.copy()
        
        # 应用阴影效果
        if effects.get("shadow", False):
            result = self._apply_text_shadow(
                result, text, position, font_size, color,
                shadow_offset=effects.get("shadow_offset", [2, 2]),
                shadow_color=effects.get("shadow_color", [0, 0, 0, 128])
            )
        
        # 应用描边效果
        if effects.get("outline", False):
            result = self._apply_text_outline(
                result, text, position, font_size, color,
                outline_width=effects.get("outline_width", 1),
                outline_color=effects.get("outline_color", [255, 255, 255])
            )
        else:
            # 正常渲染文本
            result = self.render_text(result, text, position, font_size, color)
        
        return result

    def _apply_text_shadow(
        self,
        image: Image.Image,
        text: str,
        position: Tuple[int, int],
        font_size: int,
        text_color: Tuple[int, int, int],
        shadow_offset: List[int],
        shadow_color: List[int]
    ) -> Image.Image:
        """应用文本阴影效果"""
        result = image.copy()
        
        # 先渲染阴影
        shadow_pos = (position[0] + shadow_offset[0], position[1] + shadow_offset[1])
        shadow_rgba = tuple(shadow_color) if len(shadow_color) == 4 else tuple(shadow_color + [128])
        
        result = self.render_text(result, text, shadow_pos, font_size, shadow_rgba[:3])
        
        # 再渲染正常文本
        result = self.render_text(result, text, position, font_size, text_color)
        
        return result

    def _apply_text_outline(
        self,
        image: Image.Image,
        text: str,
        position: Tuple[int, int],
        font_size: int,
        text_color: Tuple[int, int, int],
        outline_width: int,
        outline_color: List[int]
    ) -> Image.Image:
        """应用文本描边效果"""
        result = image.copy()
        
        # 渲染描边（在多个位置渲染背景色）
        outline_rgb = tuple(outline_color[:3])
        for dx in range(-outline_width, outline_width + 1):
            for dy in range(-outline_width, outline_width + 1):
                if dx != 0 or dy != 0:
                    outline_pos = (position[0] + dx, position[1] + dy)
                    result = self.render_text(result, text, outline_pos, font_size, outline_rgb)
        
        # 渲染正常文本
        result = self.render_text(result, text, position, font_size, text_color)
        
        return result

    def get_text_size(self, text: str, font_size: int, font_config: Optional[Dict[str, Any]] = None) -> Tuple[int, int]:
        """
        获取文本尺寸

        Args:
            text: 文本内容
            font_size: 字体大小
            font_config: 字体配置

        Returns:
            文本尺寸 (宽, 高)
        """
        if not text:
            return (0, 0)
        
        font = self.get_font(font_size, font_config)
        
        try:
            # 使用textbbox获取更准确的尺寸
            bbox = font.getbbox(text)
            width = bbox[2] - bbox[0]
            height = bbox[3] - bbox[1]
            return (width, height)
        except:
            # 回退到旧方法
            try:
                return font.getsize(text)
            except:
                # 简单估算
                return (len(text) * font_size // 2, font_size)

    def wrap_text(self, text: str, max_width: int, font_size: int, font_config: Optional[Dict[str, Any]] = None) -> List[str]:
        """
        文本自动换行

        Args:
            text: 原始文本
            max_width: 最大宽度
            font_size: 字体大小
            font_config: 字体配置

        Returns:
            换行后的文本列表
        """
        if not text:
            return []
        
        words = text.split()
        lines = []
        current_line = ""
        
        for word in words:
            test_line = current_line + (" " if current_line else "") + word
            text_width, _ = self.get_text_size(test_line, font_size, font_config)
            
            if text_width <= max_width:
                current_line = test_line
            else:
                if current_line:
                    lines.append(current_line)
                current_line = word
        
        if current_line:
            lines.append(current_line)
        
        return lines

    def render_multiline_text(
        self,
        image: Image.Image,
        text_lines: List[str],
        start_position: Tuple[int, int],
        font_size: int = 24,
        line_spacing: int = 5,
        color: Tuple[int, int, int] = (255, 255, 255),
        font_config: Optional[Dict[str, Any]] = None,
        align: str = "left"
    ) -> Image.Image:
        """
        渲染多行文本

        Args:
            image: 目标图像
            text_lines: 文本行列表
            start_position: 起始位置
            font_size: 字体大小
            line_spacing: 行间距
            color: 文本颜色
            font_config: 字体配置
            align: 对齐方式 (left, center, right)

        Returns:
            渲染后的图像
        """
        result = image.copy()
        x, y = start_position
        
        for line in text_lines:
            if align == "center":
                text_width, _ = self.get_text_size(line, font_size, font_config)
                line_x = x - text_width // 2
            elif align == "right":
                text_width, _ = self.get_text_size(line, font_size, font_config)
                line_x = x - text_width
            else:  # left
                line_x = x
            
            result = self.render_text(result, line, (line_x, y), font_size, color, font_config)
            y += font_size + line_spacing
        
        return result

    def clear_font_cache(self) -> None:
        """清除字体缓存"""
        self._font_cache.clear()
        logger.debug("字体缓存已清除")

    def get_available_fonts(self) -> List[str]:
        """获取可用字体列表"""
        fonts = []
        
        if self.font_dir and self.font_dir.exists():
            for font_file in self.font_dir.glob("*.ttf"):
                fonts.append(font_file.stem)
            for font_file in self.font_dir.glob("*.TTF"):
                fonts.append(font_file.stem)
            for font_file in self.font_dir.glob("*.otf"):
                fonts.append(font_file.stem)
            for font_file in self.font_dir.glob("*.OTF"):
                fonts.append(font_file.stem)
        
        return sorted(set(fonts)) 