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

"""
文本渲染组件 - 新架构版本
完全保持与原版demo_refactored_templates.py中TextRenderer的视觉效果一致
"""

import os
import logging
from typing import Tuple, List, Dict, Any, Optional, Union
from pathlib import Path
from PIL import Image, ImageDraw, ImageFont
from abc import ABC, abstractmethod

logger = logging.getLogger(__name__)


class PosterComponent(ABC):
    """海报组件基类"""
    
    @abstractmethod
    def initialize(self, config: Dict[str, Any]) -> None:
        """初始化组件"""
        pass
    
    @abstractmethod
    def process(self, input_data: Any, **kwargs) -> Any:
        """处理数据"""
        pass
    
    @abstractmethod
    def cleanup(self) -> None:
        """清理资源"""
        pass


class TextRenderer(PosterComponent):
    """
    文本渲染器 - 完全保持原版效果
    提供专业级文本渲染功能，包括字体管理、多行文本、特效等
    """
    
    def __init__(self, font_dir: str = "/root/autodl-tmp/posterGenerator/assets/fonts"):
        self.font_dir = font_dir
        self.logger = logging.getLogger(__name__)
        self._font_cache = {}  # 字体缓存
        self._available_fonts = None
        
    def initialize(self, config: Dict[str, Any]) -> None:
        """初始化文本渲染器"""
        self.font_dir = config.get('font_dir', self.font_dir)
        
        # 确保字体目录存在
        if not os.path.exists(self.font_dir):
            self.logger.warning(f"字体目录不存在: {self.font_dir}")
            self.font_dir = None
        
        # 预加载常用字体
        self._preload_fonts(config.get('preload_fonts', []))
        
    def process(self, input_data: Any, **kwargs) -> Any:
        """处理文本渲染请求"""
        if isinstance(input_data, dict):
            operation = input_data.get('operation')
            if operation == 'render_text':
                return self.render_text_with_background(**input_data.get('params', {}))
            elif operation == 'calculate_size':
                return self.calculate_optimal_font_size(**input_data.get('params', {}))
        return None
    
    def cleanup(self) -> None:
        """清理资源"""
        self._font_cache.clear()
        
    def get_available_fonts(self) -> List[str]:
        """获取可用字体列表 - 与原版完全一致"""
        if self._available_fonts is not None:
            return self._available_fonts
            
        fonts = []
        if self.font_dir and os.path.exists(self.font_dir):
            for file in os.listdir(self.font_dir):
                if file.lower().endswith(('.ttf', '.otf', '.ttc')):
                    fonts.append(file)
        
        # 系统默认字体
        system_fonts = [
            "Arial.ttf", "Times.ttf", "Helvetica.ttf",
            "STHeiti.ttc", "PingFang.ttc", "msyh.ttc"
        ]
        fonts.extend(system_fonts)
        
        self._available_fonts = list(set(fonts))
        return self._available_fonts
    
    def get_font_path(self, font_name: Optional[str] = None) -> str:
        """获取字体路径 - 与原版逻辑完全一致"""
        if not font_name:
            # 默认字体优先级
            default_fonts = [
                "SourceHanSansCN-Bold.otf",
                "SourceHanSansCN-Regular.otf", 
                "STHeiti.ttc",
                "PingFang.ttc",
                "msyh.ttc",
                "Arial.ttf"
            ]
            
            for font in default_fonts:
                font_path = self._find_font_file(font)
                if font_path:
                    return font_path
                    
            # 如果都找不到，返回系统默认
            return ""
        
        # 查找指定字体
        font_path = self._find_font_file(font_name)
        if font_path:
            return font_path
            
        # 找不到指定字体，使用默认
        self.logger.warning(f"字体 {font_name} 未找到，使用默认字体")
        return self.get_font_path()
    
    def _find_font_file(self, font_name: str) -> Optional[str]:
        """查找字体文件"""
        # 先在指定目录查找
        if self.font_dir and os.path.exists(self.font_dir):
            font_path = os.path.join(self.font_dir, font_name)
            if os.path.exists(font_path):
                return font_path
        
        # 在系统字体目录查找
        system_paths = [
            "/usr/share/fonts/",
            "/System/Library/Fonts/",
            "C:\\Windows\\Fonts\\",
            "/usr/local/share/fonts/"
        ]
        
        for sys_path in system_paths:
            if os.path.exists(sys_path):
                for root, dirs, files in os.walk(sys_path):
                    if font_name in files:
                        return os.path.join(root, font_name)
        
        return None
    
    def load_font(self, size: int, font_name: Optional[str] = None) -> ImageFont.FreeTypeFont:
        """加载字体 - 与原版逻辑完全一致"""
        font_path = self.get_font_path(font_name)
        cache_key = f"{font_path}_{size}"
        
        # 检查缓存
        if cache_key in self._font_cache:
            return self._font_cache[cache_key]
        
        try:
            if font_path and os.path.exists(font_path):
                font = ImageFont.truetype(font_path, size)
            else:
                # 使用PIL默认字体
                font = ImageFont.load_default()
                self.logger.warning(f"使用默认字体，大小: {size}")
            
            self._font_cache[cache_key] = font
            return font
            
        except Exception as e:
            self.logger.error(f"加载字体失败: {e}")
            font = ImageFont.load_default()
            self._font_cache[cache_key] = font
            return font
    
    def calculate_optimal_font_size(self, text: str, 
                                  target_width: int, 
                                  font_name: Optional[str] = None,
                                  max_size: int = 120, 
                                  min_size: int = 10) -> int:
        """计算最适合的字体大小 - 与原版算法完全一致"""
        if not text.strip():
            return min_size
        
        # 二分查找最优字体大小
        left, right = min_size, max_size
        best_size = min_size
        
        while left <= right:
            mid = (left + right) // 2
            font = self.load_font(mid, font_name)
            
            # 计算文本宽度
            text_width = self.get_text_size(text, font)[0]
            
            if text_width <= target_width:
                best_size = mid
                left = mid + 1
            else:
                right = mid - 1
        
        return best_size
    
    def get_text_size(self, text: str, font: ImageFont.FreeTypeFont) -> Tuple[int, int]:
        """获取文本尺寸 - 与原版方法完全一致"""
        try:
            # 使用textbbox方法（PIL新版本）
            if hasattr(font, 'getbbox'):
                bbox = font.getbbox(text)
                return (bbox[2] - bbox[0], bbox[3] - bbox[1])
            else:
                # 兼容旧版本PIL
                return font.getsize(text)
        except:
            # 创建临时图像来测量
            temp_img = Image.new('RGB', (1, 1))
            temp_draw = ImageDraw.Draw(temp_img)
            try:
                bbox = temp_draw.textbbox((0, 0), text, font=font)
                return (bbox[2] - bbox[0], bbox[3] - bbox[1])
            except:
                # 最后的回退方案
                return (len(text) * font.size // 2, font.size)
    
    def draw_text_with_outline(self, draw: ImageDraw.Draw, 
                             position: Tuple[int, int],
                             text: str, 
                             font: ImageFont.FreeTypeFont,
                             text_color: Tuple[int, int, int, int] = (255, 255, 255, 255),
                             outline_color: Tuple[int, int, int, int] = (0, 0, 0, 255),
                             outline_width: int = 2):
        """绘制带轮廓的文本 - 与原版效果完全一致"""
        x, y = position
        
        # 绘制轮廓 - 使用8方向偏移确保轮廓完整
        offsets = [
            (-outline_width, -outline_width), (0, -outline_width), (outline_width, -outline_width),
            (-outline_width, 0), (outline_width, 0),
            (-outline_width, outline_width), (0, outline_width), (outline_width, outline_width)
        ]
        
        for dx, dy in offsets:
            draw.text((x + dx, y + dy), text, font=font, fill=outline_color)
        
        # 绘制主文本
        draw.text((x, y), text, font=font, fill=text_color)
    
    def draw_text_with_shadow(self, draw: ImageDraw.Draw,
                            position: Tuple[int, int],
                            text: str,
                            font: ImageFont.FreeTypeFont,
                            text_color: Tuple[int, int, int, int] = (255, 255, 255, 255),
                            shadow_color: Tuple[int, int, int, int] = (0, 0, 0, 128),
                            shadow_offset: Tuple[int, int] = (2, 2)):
        """绘制带阴影的文本 - 与原版效果完全一致"""
        x, y = position
        shadow_x, shadow_y = shadow_offset
        
        # 先绘制阴影
        draw.text((x + shadow_x, y + shadow_y), text, font=font, fill=shadow_color)
        
        # 再绘制主文本
        draw.text((x, y), text, font=font, fill=text_color)
    
    def wrap_text(self, text: str, font: ImageFont.FreeTypeFont, max_width: int) -> List[str]:
        """文本换行 - 与原版逻辑完全一致"""
        if not text.strip():
            return []
        
        # 首先尝试按句子分割
        sentences = []
        current_sentence = ""
        
        for char in text:
            current_sentence += char
            if char in ['。', '！', '？', '.', '!', '?', '\n']:
                sentences.append(current_sentence.strip())
                current_sentence = ""
        
        if current_sentence.strip():
            sentences.append(current_sentence.strip())
        
        # 对每个句子进行换行处理
        lines = []
        for sentence in sentences:
            if not sentence:
                continue
                
            # 检查整句是否超出宽度
            if self.get_text_size(sentence, font)[0] <= max_width:
                lines.append(sentence)
            else:
                # 需要分割句子
                words = []
                current_word = ""
                
                for char in sentence:
                    current_word += char
                    # 中文每个字符都可以作为断点，英文以空格分割
                    if char == ' ' or ord(char) > 127:  # 中文字符
                        words.append(current_word)
                        current_word = ""
                
                if current_word:
                    words.append(current_word)
                
                # 按单词组装行
                current_line = ""
                for word in words:
                    test_line = current_line + word
                    if self.get_text_size(test_line, font)[0] <= max_width:
                        current_line = test_line
                    else:
                        if current_line:
                            lines.append(current_line.strip())
                        current_line = word
                
                if current_line:
                    lines.append(current_line.strip())
        
        return lines
    
    def draw_multiline_text(self, draw: ImageDraw.Draw,
                          position: Tuple[int, int],
                          text: str,
                          font: ImageFont.FreeTypeFont,
                          max_width: int,
                          line_spacing: int = 5,
                          align: str = "left",
                          text_color: Tuple[int, int, int, int] = (255, 255, 255, 255),
                          outline_color: Optional[Tuple[int, int, int, int]] = None,
                          outline_width: int = 1):
        """绘制多行文本 - 与原版效果完全一致"""
        lines = self.wrap_text(text, font, max_width)
        if not lines:
            return
        
        x, y = position
        line_height = self.get_text_size("测试Ag", font)[1]
        
        for i, line in enumerate(lines):
            line_y = y + i * (line_height + line_spacing)
            
            # 计算对齐位置
            if align == "center":
                line_width = self.get_text_size(line, font)[0]
                line_x = x + (max_width - line_width) // 2
            elif align == "right":
                line_width = self.get_text_size(line, font)[0]
                line_x = x + max_width - line_width
            else:  # left
                line_x = x
            
            # 绘制文本
            if outline_color:
                self.draw_text_with_outline(
                    draw, (line_x, line_y), line, font,
                    text_color, outline_color, outline_width
                )
            else:
                draw.text((line_x, line_y), line, font=font, fill=text_color)
    
    def create_text_background(self, size: Tuple[int, int],
                             color: Tuple[int, int, int, int] = (0, 0, 0, 128),
                             radius: int = 10) -> Image.Image:
        """创建文本背景 - 与原版效果完全一致"""
        bg = Image.new('RGBA', size, (0, 0, 0, 0))
        draw = ImageDraw.Draw(bg)
        
        # 绘制圆角矩形背景
        self.draw_rounded_rectangle(
            draw, (0, 0), size, radius, color
        )
        
        return bg
    
    def draw_rounded_rectangle(self, draw: ImageDraw.Draw,
                             position: Tuple[int, int],
                             size: Tuple[int, int],
                             radius: int,
                             fill_color: Tuple[int, int, int, int],
                             outline_color: Optional[Tuple[int, int, int, int]] = None,
                             outline_width: int = 0):
        """绘制圆角矩形 - 与原版算法完全一致"""
        x, y = position
        width, height = size
        
        # 限制圆角半径
        radius = min(radius, width // 2, height // 2)
        
        # 绘制圆角矩形的各个部分
        # 主矩形
        draw.rectangle([x + radius, y, x + width - radius, y + height], fill=fill_color)
        draw.rectangle([x, y + radius, x + width, y + height - radius], fill=fill_color)
        
        # 四个圆角
        draw.ellipse([x, y, x + 2 * radius, y + 2 * radius], fill=fill_color)
        draw.ellipse([x + width - 2 * radius, y, x + width, y + 2 * radius], fill=fill_color)
        draw.ellipse([x, y + height - 2 * radius, x + 2 * radius, y + height], fill=fill_color)
        draw.ellipse([x + width - 2 * radius, y + height - 2 * radius, x + width, y + height], fill=fill_color)
        
        # 绘制边框（如果需要）
        if outline_color and outline_width > 0:
            # 简化版边框，只绘制矩形边框
            for i in range(outline_width):
                draw.rectangle([x + i, y + i, x + width - i - 1, y + height - i - 1], 
                             outline=outline_color, fill=None)
    
    def render_text_with_background(self, canvas: Image.Image,
                                  text: str,
                                  position: Tuple[int, int],
                                  font_size: int,
                                  max_width: int,
                                  text_color: Tuple[int, int, int, int] = (255, 255, 255, 255),
                                  bg_color: Tuple[int, int, int, int] = (0, 0, 0, 128),
                                  padding: int = 10,
                                  align: str = "center",
                                  font_name: Optional[str] = None) -> Image.Image:
        """渲染带背景的文本 - 与原版效果完全一致"""
        if not text.strip():
            return canvas
        
        font = self.load_font(font_size, font_name)
        lines = self.wrap_text(text, font, max_width - 2 * padding)
        
        if not lines:
            return canvas
        
        # 计算文本区域尺寸
        line_height = self.get_text_size("测试Ag", font)[1]
        text_height = len(lines) * line_height + (len(lines) - 1) * 5  # 5px行间距
        
        # 计算最大行宽
        max_line_width = 0
        for line in lines:
            line_width = self.get_text_size(line, font)[0]
            max_line_width = max(max_line_width, line_width)
        
        # 创建背景
        bg_width = max_line_width + 2 * padding
        bg_height = text_height + 2 * padding
        text_bg = self.create_text_background((bg_width, bg_height), bg_color, 10)
        
        # 将背景贴到画布上
        bg_x, bg_y = position
        if canvas.mode != 'RGBA':
            canvas = canvas.convert('RGBA')
        canvas.paste(text_bg, (bg_x, bg_y), text_bg)
        
        # 绘制文本
        draw = ImageDraw.Draw(canvas)
        text_x = bg_x + padding
        text_y = bg_y + padding
        
        self.draw_multiline_text(
            draw, (text_x, text_y), text, font, max_width - 2 * padding,
            line_spacing=5, align=align, text_color=text_color,
            outline_color=(0, 0, 0, 255), outline_width=1
        )
        
        return canvas
    
    def _preload_fonts(self, fonts: List[str]):
        """预加载常用字体"""
        common_sizes = [12, 16, 20, 24, 32, 48, 64]
        for font_name in fonts:
            for size in common_sizes:
                try:
                    self.load_font(size, font_name)
                except Exception as e:
                    self.logger.warning(f"预加载字体失败 {font_name}@{size}: {e}") 