import cv2
from nonebot import logger
import numpy as np

from shapely.geometry import Polygon
from shapely.ops import unary_union

def fix_with_shapely(contours: list) -> np.ndarray:
    """
    使用Shapely库处理复杂自相交
    """
    fixed_polygons = []
    for contour in contours:
        # 转换输入为正确的格式
        contour_array = contour.reshape(-1, 2)
        # 转换为Shapely多边形
        polygon = Polygon(contour_array)
        if not polygon.is_valid:
            polygon = polygon.buffer(0)
        fixed_polygons.append(polygon)
    # 接下来把所有轮廓合并为一个
    if len(fixed_polygons) >= 1:
        merged_polygon = unary_union(fixed_polygons)
        if merged_polygon.geom_type == 'Polygon':
            merged_points = np.array(merged_polygon.exterior.coords, dtype=np.int32)
        elif merged_polygon.geom_type == 'MultiPolygon':
            largest = max(merged_polygon.geoms, key=lambda p: p.area)
            merged_points = np.array(largest.exterior.coords, dtype=np.int32)
        return [merged_points.reshape(-1, 1, 2)]
    else:
        logger.warning("No valid contours found after fixing with Shapely.")
        return [np.array([], dtype=np.int32).reshape(0, 1, 2)]

def expand_contours(contours, stroke_width):
    """
    将轮廓向外扩展指定宽度
    
    参数:
        contours: OpenCV轮廓列表
        stroke_width: 扩展宽度（像素）
        
    返回:
        扩展后的轮廓列表
    """
    expanded_contours = []
    
    for cnt in contours:
        # 将轮廓转换为点列表
        points = cnt.reshape(-1, 2).astype(np.float32)
        n = len(points)
        
        if n < 3:
            continue  # 至少需要3个点才能形成多边形
            
        expanded_points = []
        
        for i in range(n):
            # 获取当前点、前一个点和后一个点
            p_curr = points[i]
            p_prev = points[(i - 1) % n]
            p_next = points[(i + 1) % n]
            
            # 计算两条边的向量
            v1 = p_curr - p_prev  # 前一条边（从prev到curr）
            v2 = p_next - p_curr  # 后一条边（从curr到next）
            
            # 归一化
            norm1 = np.linalg.norm(v1)
            norm2 = np.linalg.norm(v2)
            
            if norm1 == 0 or norm2 == 0:
                # 如果有零向量，直接沿着法线方向扩展
                edge_dir = np.array([0, 0])
                if norm1 > 0:
                    edge_dir = v1 / norm1
                elif norm2 > 0:
                    edge_dir = v2 / norm2
                normal = np.array([-edge_dir[1], edge_dir[0]])
                expanded_point = p_curr + normal * stroke_width
            else:
                # 归一化向量
                v1_norm = v1 / norm1
                v2_norm = v2 / norm2
                
                # 计算两条边的单位法向量（指向多边形外部）
                n1 = np.array([-v1_norm[1], v1_norm[0]])
                n2 = np.array([-v2_norm[1], v2_norm[0]])
                
                # 计算角平分线方向（两个法向量的和）
                bisector = n1 + n2
                
                # 计算平分线的长度
                bisector_norm = np.linalg.norm(bisector)
                
                if bisector_norm == 0:
                    # 如果两条边平行（同向或反向），取任一法线方向
                    expanded_point = p_curr + n1 * stroke_width
                else:
                    # 归一化平分线
                    bisector_normalized = bisector / bisector_norm
                    
                    # 计算偏移距离（考虑夹角）
                    # 使用余弦定理计算正确的偏移距离
                    cos_angle = np.dot(v1_norm, v2_norm)
                    angle = np.arccos(np.clip(cos_angle, -1.0, 1.0))
                    
                    if abs(np.pi - angle) < 1e-6:  # 近似平角
                        # 接近直线的情况
                        offset_distance = stroke_width
                    else:
                        # 计算正确的偏移距离
                        offset_distance = stroke_width / np.sin(angle / 2)
                    
                    # 计算扩展点
                    expanded_point = p_curr + bisector_normalized * offset_distance
            
            expanded_points.append(expanded_point)
        
        # 将扩展后的点转换为整数坐标
        expanded_cnt = np.array(expanded_points, dtype=np.float32).reshape(-1, 1, 2)
        expanded_contours.append(expanded_cnt.astype(np.int32))

    expanded_contours = fix_with_shapely(expanded_contours)

    return expanded_contours