import math

class Line:
    def __init__(self, x1, y1, x2, y2, thickness=5):
        self.x1 = x1
        self.y1 = y1
        self.x2 = x2
        self.y2 = y2
        self.thickness = thickness
        self._half_thickness = thickness / 2
    
    def update(self, x1=None, y1=None, x2=None, y2=None):
        if x1 is not None:
            self.x1 = x1
        if y1 is not None:
            self.y1 = y1
        if x2 is not None:
            self.x2 = x2
        if y2 is not None:
            self.y2 = y2
    
    def set_thickness(self, thickness):
        self.thickness = thickness
        self._half_thickness = thickness / 2
    
    def get_start_point(self):
        return (self.x1, self.y1)
    
    def get_end_point(self):
        return (self.x2, self.y2)
    
    def get_center_point(self):
        return ((self.x1 + self.x2) / 2, (self.y1 + self.y2) / 2)
    
    def get_length(self):
        return math.hypot(self.x2 - self.x1, self.y2 - self.y1)
    
    def get_direction(self):
        dx = self.x2 - self.x1
        dy = self.y2 - self.y1
        length = self.get_length()
        if length == 0:
            return (0, 0)
        return (dx / length, dy / length)
    
    def get_normal(self):
        dx, dy = self.get_direction()
        return (-dy, dx)  # 顺时针旋转90度
    
    def _point_to_segment_distance(self, px, py):
        dx = self.x2 - self.x1
        dy = self.y2 - self.y1
        
        # 如果线段是一个点
        if dx == 0 and dy == 0:
            return math.hypot(px - self.x1, py - self.y1)
        
        # 计算投影参数 t
        t = ((px - self.x1) * dx + (py - self.y1) * dy) / (dx * dx + dy * dy)
        
        # 限制 t 在 [0, 1] 范围内
        t = max(0, min(1, t))
        
        # 计算最近点的坐标
        closest_x = self.x1 + t * dx
        closest_y = self.y1 + t * dy
        
        # 计算距离
        return math.hypot(px - closest_x, py - closest_y)
    
    def collisions_with_point(self, px, py, extra_tolerance=0):
        distance = self._point_to_segment_distance(px, py)
        return distance <= self._half_thickness + extra_tolerance
    
    def collisions_with_line(self, other_line):
        half_t1 = self._half_thickness
        half_t2 = other_line._half_thickness
        total_half = half_t1 + half_t2
        
        # 方法1：检查端点圆是否与另一条线段相交
        if other_line.collisions_with_point(self.x1, self.y1, total_half):
            return True
        if other_line.collisions_with_point(self.x2, self.y2, total_half):
            return True
        if self.collisions_with_point(other_line.x1, other_line.y1, total_half):
            return True
        if self.collisions_with_point(other_line.x2, other_line.y2, total_half):
            return True
        
        # 方法2：检查两条无限细的线段是否相交
        if self._line_line_collision(other_line):
            return True
        
        # 方法3：检查两条线段是否平行且距离很近
        min_distance = min(
            other_line._point_to_segment_distance(self.x1, self.y1),
            other_line._point_to_segment_distance(self.x2, self.y2),
            self._point_to_segment_distance(other_line.x1, other_line.y1),
            self._point_to_segment_distance(other_line.x2, other_line.y2)
        )
        
        return min_distance <= total_half
    
    def _line_line_collision(self, other_line):
        def ccw(ax, ay, bx, by, cx, cy):
            return (cy - ay) * (bx - ax) > (by - ay) * (cx - ax)
        
        return (ccw(self.x1, self.y1, other_line.x1, other_line.y1, other_line.x2, other_line.y2) != 
                ccw(self.x2, self.y2, other_line.x1, other_line.y1, other_line.x2, other_line.y2) and 
                ccw(self.x1, self.y1, self.x2, self.y2, other_line.x1, other_line.y1) != 
                ccw(self.x1, self.y1, self.x2, self.y2, other_line.x2, other_line.y2))
    
    def collisions_with_rect(self, rect_x, rect_y, rect_w, rect_h):
        # 1. 检查矩形的四个角点是否在线段的厚度范围内
        corners = [
            (rect_x, rect_y),
            (rect_x + rect_w, rect_y),
            (rect_x, rect_y + rect_h),
            (rect_x + rect_w, rect_y + rect_h)
        ]
        
        for corner_x, corner_y in corners:
            if self.collisions_with_point(corner_x, corner_y):
                return True
        
        # 2. 检查矩形的四条边是否与线段相交
        edges = [
            Line(rect_x, rect_y, rect_x + rect_w, rect_y, thickness=0),  # 上边
            Line(rect_x, rect_y + rect_h, rect_x + rect_w, rect_y + rect_h, thickness=0),  # 下边
            Line(rect_x, rect_y, rect_x, rect_y + rect_h, thickness=0),  # 左边
            Line(rect_x + rect_w, rect_y, rect_x + rect_w, rect_y + rect_h, thickness=0)  # 右边
        ]
        
        for edge in edges:
            if self.collisions_with_line(edge):
                return True
        
        # 3. 检查线段的端点圆是否与矩形相交
        if self._circle_rect_collision(self.x1, self.y1, self._half_thickness, rect_x, rect_y, rect_w, rect_h):
            return True
        if self._circle_rect_collision(self.x2, self.y2, self._half_thickness, rect_x, rect_y, rect_w, rect_h):
            return True
        
        return False
    
    @staticmethod
    def _circle_rect_collision(circle_x, circle_y, radius, rect_x, rect_y, rect_w, rect_h):
        closest_x = max(rect_x, min(circle_x, rect_x + rect_w))
        closest_y = max(rect_y, min(circle_y, rect_y + rect_h))
        distance = math.hypot(circle_x - closest_x, circle_y - closest_y)
        return distance <= radius
    
    def collisions_with_circle(self, circle_x, circle_y, radius):
        total_radius = self._half_thickness + radius
        
        # 检查端点圆是否与目标圆相交
        if math.hypot(self.x1 - circle_x, self.y1 - circle_y) <= total_radius:
            return True
        if math.hypot(self.x2 - circle_x, self.y2 - circle_y) <= total_radius:
            return True
        
        # 检查线段到圆心的距离
        distance = self._point_to_segment_distance(circle_x, circle_y)
        return distance <= total_radius
    
    def get_bounding_rect(self):
        min_x = min(self.x1, self.x2) - self._half_thickness
        min_y = min(self.y1, self.y2) - self._half_thickness
        max_x = max(self.x1, self.x2) + self._half_thickness
        max_y = max(self.y1, self.y2) + self._half_thickness
        
        return (min_x, min_y, max_x - min_x, max_y - min_y)
    
    def draw(self, screen, color, width=None):
        import pygame
        draw_width = width if width is not None else self.thickness
        pygame.draw.line(screen, color, (self.x1, self.y1), (self.x2, self.y2), draw_width)
    
    def draw_control_points(self, screen, color, radius=8):
        import pygame
        pygame.draw.circle(screen, color, (int(self.x1), int(self.y1)), radius)
        pygame.draw.circle(screen, color, (int(self.x2), int(self.y2)), radius)
    
    def __repr__(self):
        return f"Line(({self.x1}, {self.y1}) -> ({self.x2}, {self.y2}), thickness={self.thickness})"
    