import MilCon
import typing
import math


class _HPos:
    x: str
    y: str
    z: str

class RelativePos:
    def __init__(self, x: str | int | float = "~", y: str | int | float = "~", z: str | int | float = "~"):
        self.x: str = str(x) if isinstance(x, (int, float)) else x
        self.y: str = str(y) if isinstance(y, (int, float)) else y
        self.z: str = str(z) if isinstance(z, (int, float)) else z
    def __str__(self):
        return f"{self.x} {self.y} {self.z}"
    @classmethod
    def FromString(cls, s: str):
        parts = s.split()
        if len(parts) != 3 or not all(p.startswith('~') for p in parts): raise ValueError("Catch A Error As RelativePos ~x ~y ~z")
        return cls(*parts)
    def ToAbsolute(self, reference: _HPos=None):
        def ParseCoord(coord: str, ref):
            if coord.startswith('~'): return ref + (float(coord[1:]) if len(coord) > 1 else 0.0)
            else: return float(coord)
        ref_x = reference.x if reference and reference.x.startswith('~') else 0.0
        ref_y = reference.y if reference and reference.y.startswith('~') else 0.0
        ref_z = reference.z if reference and reference.z.startswith('~') else 0.0
        abs_x = ParseCoord(self.x, ref_x)
        abs_y = ParseCoord(self.y, ref_y)
        abs_z = ParseCoord(self.z, ref_z)
        return RelativePos(abs_x, abs_y, abs_z)

class LocalCoordinatesPos:
    def __init__(self, x: str | int | float = "^", y: str | int | float = "^", z: str | int | float = "^"):
        self.x: str = str(x) if isinstance(x, (int, float)) else x
        self.y: str = str(y) if isinstance(y, (int, float)) else y
        self.z: str = str(z) if isinstance(z, (int, float)) else z
    def __str__(self):
        return f"{self.x} {self.y} {self.z}"
    @classmethod
    def FromString(cls, s: str):
        parts = s.split()
        if len(parts) != 3 or not all(p.startswith('^') for p in parts): raise ValueError("Catch A Error As LocalCoordinatesPos ^x ^y ^z")
        return cls(*parts)
    def ToAbsolute(self, reference: _HPos=None):
        def ParseCoord(coord: str, ref):
            if coord.startswith('^'): return ref + (float(coord[1:]) if len(coord) > 1 else 0.0)
            else: return float(coord)
        ref_x = reference.x if reference and reference.x.startswith('^') else 0.0
        ref_y = reference.y if reference and reference.y.startswith('^') else 0.0
        ref_z = reference.z if reference and reference.z.startswith('^') else 0.0
        abs_x = ParseCoord(self.x, ref_x)
        abs_y = ParseCoord(self.y, ref_y)
        abs_z = ParseCoord(self.z, ref_z)
        return LocalCoordinatesPos(abs_x, abs_y, abs_z)

class Delta:
    def __init__(self, x: int = 0, y: int = 0, z: int = 0):
        self.x, self.y, self.z = x, y, z
    def __str__(self):
        return f"{self.x} {self.y} {self.z}"
class Command():
    def __init__(self, command):
        self.command = command
    def __str__(self):
        return self.command
class ExecuteSubCommandRule(typing.TypedDict):
    mode: MilCon._execute_sub_commands
    text: Command

def _Bool2Str(self, value: bool):
    return "true" if value else "false"
def _WithValue(self, value: str | int):
    return f" {value}" if value else ""

def _Pos1ToPos2(pos1: RelativePos, pos2: RelativePos, radios: int):
    abs_pos1 = pos1.ToAbsolute()
    abs_pos2 = pos2.ToAbsolute()
    x1, y1, z1 = abs_pos1.x, abs_pos1.y, abs_pos1.z
    x2, y2, z2 = abs_pos2.x, abs_pos2.y, abs_pos2.z
    delta_x = (x2 - x1) / (radios + 1)
    delta_y = (y2 - y1) / (radios + 1)
    delta_z = (z2 - z1) / (radios + 1)
    points = []
    for i in range(1, radios + 1):
        x = x1 + delta_x * i
        y = y1 + delta_y * i
        z = z1 + delta_z * i
        points.append(RelativePos(x, y, z))
    return points

def _Pos1ToPos2WithPoints(radios: int, radian: int, pos1: RelativePos, pos2: RelativePos, *poses: RelativePos):
    # 参数验证
    if radios <= len(poses) + 2:
        raise ValueError("radios must be greater than len(poses) + 2")
    if radian < 0:
        raise ValueError("radian must be non-negative")
    
    # 转换为绝对坐标
    abs_pos1 = pos1.ToAbsolute()
    abs_poses = [p.ToAbsolute() for p in poses]
    abs_pos2 = pos2.ToAbsolute()
    
    # 所有点按顺序排列
    all_points = [abs_pos1] + abs_poses + [abs_pos2]
    num_segments = len(all_points) - 1
    
    # 计算每段的点数
    points_per_segment = (radios - num_segments + 1) // num_segments
    remainder = (radios - num_segments + 1) % num_segments
    
    # 生成点
    points = []
    for i in range(num_segments):
        start = all_points[i]
        end = all_points[i + 1]
        
        # 当前段的点数
        current_points = points_per_segment + (1 if i < remainder else 0)
        
        # 计算增量
        delta_x = (float(end.x) - float(start.x)) / (current_points + 1)
        delta_y = (float(end.y) - float(start.y)) / (current_points + 1)
        delta_z = (float(end.z) - float(start.z)) / (current_points + 1)
        
        # 生成当前段的点
        for j in range(1, current_points + 1):
            x = float(start.x) + delta_x * j
            y = float(start.y) + delta_y * j
            z = float(start.z) + delta_z * j
            
            # 如果 radian 不为 0 且 poses 存在，则在中间点附近向外偏移
            if radian != 0 and len(poses) > 0 and i < len(poses):
                # 计算当前点与中间点的距离比例
                t = j / (current_points + 1)
                # 使用正弦函数生成弧形偏移
                offset = radian * math.sin(t * math.pi)
                
                # 计算垂直于路径的方向（简单实现，可以改进为真正的 3D 垂直）
                # 这里假设偏移方向为 (1, 1, 1) 的某个分量，实际应根据路径方向计算
                x += offset * 0.577  # 0.577 ≈ 1 / sqrt(3)
                y += offset * 0.577
                z += offset * 0.577
            
            points.append(RelativePos(x, y, z))
    
    return points

'''def _Pos1ToPos2WithPoints(radios: int, pos1: RelativePos, pos2: RelativePos, *poses: RelativePos):
    if radios <= len(poses) + 2:
        raise ValueError("radios must be greater than len(poses) + 2")
    abs_pos1 = pos1.ToAbsolute()
    abs_poses = [p.ToAbsolute() for p in poses]
    abs_pos2 = pos2.ToAbsolute()
    all_points = [abs_pos1] + abs_poses + [abs_pos2]
    num_segments = len(all_points) - 1
    points_per_segment = (radios - num_segments + 1) // num_segments
    remainder = (radios - num_segments + 1) % num_segments
    points = []
    for i in range(num_segments):
        start = all_points[i]
        end = all_points[i + 1]
        current_points = points_per_segment + (1 if i < remainder else 0)
        delta_x = (float(end.x) - float(start.x)) / (current_points + 1)
        delta_y = (float(end.y) - float(start.y)) / (current_points + 1)
        delta_z = (float(end.z) - float(start.z)) / (current_points + 1)
        for j in range(1, current_points + 1):
            x = float(start.x) + delta_x * j
            y = float(start.y) + delta_y * j
            z = float(start.z) + delta_z * j
            points.append(RelativePos(x, y, z))
    return points'''