import cv2
import numpy as np
from PIL import Image, ImageDraw, ImageFont
from typing import List, Dict, Any, Tuple, Optional
import os
import math
import logging

class TextRenderingModule:
    def __init__(self, logger, font_path: Optional[str] = None):
        self.logger = logger
        self.font_path = font_path
        self.default_font_size = 20
        self.font_cache = {}

    def get_font(self, size: int, font_path: Optional[str] = None) -> ImageFont.FreeTypeFont:
        font_path = font_path or self.font_path
        cache_key = f"{font_path}_{size}"
        if cache_key not in self.font_cache:
            try:
                if font_path and os.path.exists(font_path):
                    font = ImageFont.truetype(font_path, size)
                else:
                    font = ImageFont.load_default()
                self.font_cache[cache_key] = font
            except Exception as e:
                print(f"폰트 로드 오류: {e}")
                font = ImageFont.load_default()
                self.font_cache[cache_key] = font
        return self.font_cache[cache_key]
    
    def estimate_text_size(self, text: str, font_size: int, font_path: Optional[str] = None) -> Tuple[int, int]:
        font = self.get_font(font_size, font_path)
        try:
            bbox = font.getbbox(text)
            width = bbox[2] - bbox[0]
            height = bbox[3] - bbox[1]
        except AttributeError:
            width, height = font.getsize(text)
        return width, height
    
    def calculate_optimal_font_size(self, text: str, target_width: int, target_height: int, min_size: int = 8, max_size: int = 100, font_path: Optional[str] = None) -> int:
        best_size = min_size
        for size in range(min_size, max_size + 1):
            width, height = self.estimate_text_size(text, size, font_path)
            if width <= target_width and height <= target_height:
                best_size = size
            else:
                break
        return best_size
    
    def _estimate_background_color(self, image: np.ndarray, x1: int, y1: int, x2: int, y2: int) -> Tuple[int, int, int]:
        margin = 5
        y1_exp = max(0, y1 - margin)
        y2_exp = min(image.shape[0], y2 + margin)
        x1_exp = max(0, x1 - margin)
        x2_exp = min(image.shape[1], x2 + margin)
        region = image[y1_exp:y2_exp, x1_exp:x2_exp]
        mean_color = np.mean(region, axis=(0, 1))
        return (int(mean_color[2]), int(mean_color[1]), int(mean_color[0]))
    
    def _get_contrasting_color(self, bg_color: Tuple[int, int, int]) -> Tuple[int, int, int]:
        brightness = (bg_color[0] * 0.299 + bg_color[1] * 0.587 + bg_color[2] * 0.114)
        if brightness > 128:
            return (0, 0, 0)
        else:
            return (255, 255, 255)
        
    def render_text(self, image: np.ndarray, ocr_results: List[Dict], translated_texts: List[str], font_path: Optional[str] = None) -> np.ndarray:
        result_image = image.copy()
        for i, (ocr_result, translated_text) in enumerate(zip(ocr_results, translated_texts)):
            polygon = ocr_result['polygon']
            polygon_array = np.array(polygon)
            x_coords = polygon_array[:, 0]
            y_coords = polygon_array[:, 1]
            x_min, x_max = int(np.min(x_coords)), int(np.max(x_coords))
            y_min, y_max = int(np.min(y_coords)), int(np.max(y_coords))
            width = x_max - x_min
            height = y_max - y_min
            optimal_font_size = self.calculate_optimal_font_size(translated_text, width, height, font_path=font_path)
            text_width, text_height = self.estimate_text_size(translated_text, optimal_font_size, font_path)
            center_x = (x_min + x_max) // 2
            center_y = (y_min + y_max) // 2
            text_x = center_x - text_width // 2
            text_y = center_y - text_height // 2
            angle = 0
            if len(polygon_array) >= 2:
                dx = polygon_array[1][0] - polygon_array[0][0]
                dy = polygon_array[1][1] - polygon_array[0][1]
                angle = math.degrees(math.atan2(dy, dx))
            bg_color = self._estimate_background_color(image, x_min, y_min, x_max, y_max)
            text_color = self._get_contrasting_color(bg_color)
            result_image = self.render_text_on_image(
                result_image, translated_text, (text_x, text_y),
                font_size=optimal_font_size,
                font_path=font_path,
                text_color=text_color,
                background_color=None,
                angle=angle
            )
        return result_image
    
    def render_text_on_image(self, image: np.ndarray, text: str, position: Tuple[int, int], font_size: Optional[int] = None, font_path: Optional[str] = None, text_color: Tuple[int, int, int] = (0, 0, 0), background_color: Optional[Tuple[int, int, int]] = None, angle: float = 0) -> np.ndarray:
        if font_size is None:
            font_size = self.default_font_size
        pil_image = Image.fromarray(cv2.cvtColor(image, cv2.COLOR_BGR2RGB))
        draw = ImageDraw.Draw(pil_image)
        font = self.get_font(font_size, font_path)
        print(f"render_text_on_image font: {font}")
        text_width, text_height = self.estimate_text_size(text, font_size, font_path)
        if background_color is not None:
            bg_x1 = position[0] - 2
            bg_y1 = position[1] - 2
            bg_x2 = position[0] + text_width + 2
            bg_y2 = position[1] + text_height + 2
            draw.rectangle([bg_x1, bg_y1, bg_x2, bg_y2], fill=background_color)
        if angle != 0:
            text_image = Image.new('RGBA', (text_width + 10, text_height + 10), (255, 255, 255, 0))
            text_draw = ImageDraw.Draw(text_image)
            text_draw.text((5, 5), text, font=font, fill=text_color + (255,))
            rotated_text = text_image.rotate(angle, expand=True)
            pil_image.paste(rotated_text, position, rotated_text)
        else:
            draw.text(position, text, font=font, fill=text_color)
        result_image = cv2.cvtColor(np.array(pil_image), cv2.COLOR_RGB2BGR)
        return result_image
    
    def create_text_styles(self) -> Dict[str, Dict[str, Any]]:
        """다양한 텍스트 스타일 정의"""
        styles = {
            'default': {
                'color': (0, 0, 0),
                'bg_color': None,
                'outline': True,
                'outline_color': (255, 255, 255),
                'outline_width': 1
            },
            'bold': {
                'color': (0, 0, 0),
                'bg_color': (255, 255, 255),
                'outline': True,
                'outline_color': (128, 128, 128),
                'outline_width': 2
            },
            'highlight': {
                'color': (255, 255, 255),
                'bg_color': (255, 0, 0),
                'outline': False,
                'outline_color': None,
                'outline_width': 0
            },
            'subtle': {
                'color': (128, 128, 128),
                'bg_color': None,
                'outline': True,
                'outline_color': (255, 255, 255),
                'outline_width': 1
            }
        }
        
        return styles
    
    def render_with_style(self, image: np.ndarray, ocr_results: List[Dict], 
                         translated_texts: List[str], style_name: str = 'default') -> np.ndarray:
        """스타일을 적용한 텍스트 렌더링"""
        styles = self.create_text_styles()
        
        if style_name not in styles:
            print(f"알 수 없는 스타일: {style_name}")
            style_name = 'default'
        
        style = styles[style_name]
        
        # 기본 렌더링 후 스타일 적용
        result = self.render_text(image, ocr_results, translated_texts)
        
        # 추가 스타일 처리는 여기서 구현
        # (예: 그림자, 글로우 효과 등)
        
        return result
    
    def adjust_text_for_space(self, text: str, max_width: int, max_height: int, 
                            font_size: int) -> Tuple[str, int]:
        """
        공간에 맞게 텍스트 조정
        
        Args:
            text (str): 원본 텍스트
            max_width (int): 최대 너비
            max_height (int): 최대 높이
            font_size (int): 폰트 크기
            
        Returns:
            Tuple[str, int]: 조정된 텍스트와 폰트 크기
        """
        # 텍스트가 너무 길면 줄바꿈 또는 생략
        if len(text) > 20:
            # 긴 텍스트는 줄바꿈
            words = text.split(' ')
            if len(words) > 1:
                mid = len(words) // 2
                text = ' '.join(words[:mid]) + '\n' + ' '.join(words[mid:])
            else:
                # 단어가 하나면 생략
                text = text[:15] + '...'
        
        # 폰트 크기 조정
        adjusted_font_size = font_size
        while adjusted_font_size > 8:
            # 실제로는 텍스트 크기를 측정해서 비교
            estimated_width = len(text) * adjusted_font_size * 0.6
            if estimated_width <= max_width:
                break
            adjusted_font_size -= 2
        
        return text, adjusted_font_size
    
    def _create_style_comparison(self, images: List[np.ndarray], style_names: List[str]):
        """스타일 비교 이미지 생성"""
        if not images:
            return
        
        # 이미지 크기 조정
        target_width = 200
        target_height = int(images[0].shape[0] * target_width / images[0].shape[1])
        
        resized_images = []
        for img in images:
            resized = cv2.resize(img, (target_width, target_height))
            resized_images.append(resized)
        
        # 비교 이미지 생성
        num_images = len(resized_images)
        comparison_width = target_width * num_images
        comparison_height = target_height + 30
        
        comparison = np.ones((comparison_height, comparison_width, 3), dtype=np.uint8) * 255
        
        # 원본 이미지
        comparison[30:30+target_height, 0:target_width] = resized_images[0]
        cv2.putText(comparison, "Original", (10, 20), 
                   cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 0), 1)
        
        # 스타일 이미지들
        for i, (img, style_name) in enumerate(zip(resized_images[1:], style_names)):
            x_offset = target_width * (i + 1)
            comparison[30:30+target_height, x_offset:x_offset+target_width] = img
            cv2.putText(comparison, style_name, (x_offset + 10, 20), 
                       cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 0), 1)
        
        cv2.imwrite("test_output/text_style_comparison.jpg", comparison)
        self.logger.log("스타일 비교 이미지 저장 완료", level=logging.INFO) 