import pygame
import math
import random
import sys

pygame.init()

# 窗口设置
SCREEN_W, SCREEN_H = 1200, 750
PANEL_W = 300
SEA_W, SEA_H = SCREEN_W - PANEL_W, SCREEN_H
FPS = 60

# 颜色
C_DEEP = (8, 20, 60)
C_SEA1 = (15, 40, 90)
C_SEA2 = (25, 60, 120)
C_WHITE = (240, 240, 240)
C_BLACK = (0, 0, 0)
C_RED = (220, 50, 50)
C_GREEN = (50, 200, 50)
C_YELLOW = (255, 220, 50)
C_GRAY = (80, 90, 110)
C_LGRAY = (160, 180, 200)
C_ORANGE = (255, 150, 30)

# 船舶参数
MAX_SPEED = 8.0          # 像素/帧 (~480 px/s @60fps)
NOMOTO_K = 0.12          # 回转性指数
NOMOTO_T = 2.0           # 追随性指数 (时间常数)
RUDDER_MAX = 35          # 最大舵角 (度)
RUDDER_RATE = 2.0        # 舵角变化速率 (度/帧)
DRIFT_COEFF = 0.02       # 侧向漂移系数
ROLL_AMPLITUDE = 1.5     # 横摇幅度 (视觉用)

# 初始环境
WIND_SPEED = 2.0         # 节
WIND_DIR = 45            # 度 (气象方向，0=北)
CURRENT_SPEED = 0.8
CURRENT_DIR = 200

# 随机目标 (雷达用)
NUM_RADAR_TARGETS = 5

def draw_ship(surf, pos, heading, scale=1.2):
    """绘制较逼真的机动船"""
    length = 35 * scale
    width = 14 * scale
    # 船体多边形 (船头向右)
    points = [
        (length, 0),
        (length*0.5, -width*0.5),
        (-length*0.6, -width),
        (-length*0.8, -width*0.4),
        (-length*0.8, width*0.4),
        (-length*0.6, width),
        (length*0.5, width*0.5)
    ]
    rad = math.radians(heading)
    cos_a, sin_a = math.cos(rad), math.sin(rad)
    rot = []
    for x, y in points:
        rx = x * cos_a - y * sin_a
        ry = x * sin_a + y * cos_a
        rot.append((pos[0] + rx, pos[1] - ry))
    pygame.draw.polygon(surf, (200, 200, 210), rot)
    pygame.draw.polygon(surf, (100, 100, 120), rot, 2)
    # 上层建筑
    bridge_pts = [
        (length*0.3, -width*0.2),
        (-length*0.2, -width*0.2),
        (-length*0.2, width*0.2),
        (length*0.3, width*0.2)
    ]
    rot_b = []
    for x, y in bridge_pts:
        rx = x * cos_a - y * sin_a
        ry = x * sin_a + y * cos_a
        rot_b.append((pos[0] + rx, pos[1] - ry))
    pygame.draw.polygon(surf, (140, 140, 160), rot_b)

def draw_compass_rose(surf, center, heading, radius=55):
    """绘制航海罗经卡"""
    pygame.draw.circle(surf, (20, 20, 40), center, radius)
    pygame.draw.circle(surf, C_WHITE, center, radius, 3)
    for deg in range(0, 360, 5):
        rad = math.radians(deg)
        inner = radius - 6 if deg % 30 == 0 else radius - 3
        outer = radius - 1
        col = C_RED if deg % 90 == 0 else C_WHITE
        if deg % 10 == 0:
            start = (center[0] + inner * math.cos(rad), center[1] - inner * math.sin(rad))
            end = (center[0] + outer * math.cos(rad), center[1] - outer * math.sin(rad))
            pygame.draw.line(surf, col, start, end, 2)
    # 指北标记
    north_angle = math.radians(0)
    n_end = (center[0] + (radius-15) * math.cos(north_angle), center[1] - (radius-15) * math.sin(north_angle))
    pygame.draw.line(surf, C_RED, center, n_end, 4)
    font = pygame.font.Font(None, 20)
    n_text = font.render("N", True, C_RED)
    surf.blit(n_text, (n_end[0]-5, n_end[1]-10))
    # 船首线
    rad_h = math.radians(heading)
    line_end = (center[0] + (radius-15) * math.cos(rad_h), center[1] - (radius-15) * math.sin(rad_h))
    pygame.draw.line(surf, C_YELLOW, center, line_end, 4)

def draw_circular_gauge(surf, center, value, max_val, label, color, radius=35):
    """圆形仪表（速度、舵角等）"""
    rect = pygame.Rect(center[0]-radius, center[1]-radius, radius*2, radius*2)
    # 背景弧
    pygame.draw.arc(surf, C_GRAY, rect, math.radians(135), math.radians(45), 10)
    # 指示弧
    angle_range = 270
    start_angle = 135
    val_angle = start_angle + (value / max_val) * angle_range
    if val_angle > 360: val_angle -= 360
    pygame.draw.arc(surf, color, rect, math.radians(start_angle), math.radians(val_angle), 10)
    # 指针
    pointer_angle = math.radians(val_angle)
    pointer_len = radius - 8
    end_x = center[0] + pointer_len * math.cos(pointer_angle)
    end_y = center[1] - pointer_len * math.sin(pointer_angle)
    pygame.draw.line(surf, color, center, (end_x, end_y), 3)
    # 标签
    font = pygame.font.Font(None, 18)
    text = font.render(label, True, C_WHITE)
    surf.blit(text, (center[0]-text.get_width()//2, center[1]+radius-10))
    val_str = f"{value:.0f}" if isinstance(value, float) else str(value)
    val_surf = font.render(val_str, True, C_WHITE)
    surf.blit(val_surf, (center[0]-val_surf.get_width()//2, center[1]-radius-15))

def draw_radar(surf, center, radius, scan_angle, targets, ship_pos, ship_heading):
    """雷达PPI显示"""
    # 背景
    pygame.draw.circle(surf, (0, 30, 0), center, radius)
    pygame.draw.circle(surf, (0, 80, 0), center, radius, 2)
    # 刻度圆
    for r in range(radius//4, radius, radius//4):
        pygame.draw.circle(surf, (0, 120, 0), center, r, 1)
    # 扫描线
    end_x = center[0] + radius * math.cos(math.radians(scan_angle))
    end_y = center[1] - radius * math.sin(math.radians(scan_angle))
    pygame.draw.line(surf, (0, 255, 0), center, (end_x, end_y), 2)
    # 目标回波 (世界坐标转雷达坐标，范围缩放)
    scale = radius / 300  # 假设雷达范围300像素
    for tx, ty in targets:
        dx = tx - ship_pos[0]
        dy = ship_pos[1] - ty  # 注意Y轴反转
        dist = math.hypot(dx, dy)
        if dist > 300: continue
        bearing = math.degrees(math.atan2(dy, dx)) - ship_heading
        rad_b = math.radians(bearing)
        rx = center[0] + dist * scale * math.cos(rad_b)
        ry = center[1] - dist * scale * math.sin(rad_b)
        pygame.draw.circle(surf, (0, 255, 0), (int(rx), int(ry)), 3)

def main():
    screen = pygame.display.set_mode((SCREEN_W, SCREEN_H))
    pygame.display.set_caption("专业航海模拟器 - Marine Simulator Pro")
    clock = pygame.time.Clock()
    font_sm = pygame.font.Font(None, 20)
    font_md = pygame.font.Font(None, 24)

    # 船只状态 (世界坐标，海图区域左下角为原点，向右向上为正)
    ship_x = 400.0
    ship_y = 400.0
    heading = 0.0              # 度，0=东，90=北
    speed = 0.0                # 像素/帧
    rudder = 0.0               # 舵角
    throttle = 0               # 0-10
    yaw_rate = 0.0             # 转首角速度 (度/帧)
    roll_angle = 0.0           # 横摇视觉角度

    # 环境
    wind_speed = WIND_SPEED
    wind_dir = WIND_DIR
    curr_speed = CURRENT_SPEED
    curr_dir = CURRENT_DIR

    # 航路点与自动舵
    waypoint = None            # (x, y) 世界坐标
    auto_pilot = False
    radar_on = True
    radar_scan = 0.0

    # 雷达目标 (随机船只/浮标)
    targets = []
    for _ in range(NUM_RADAR_TARGETS):
        targets.append((random.uniform(100, 800), random.uniform(100, 700)))
    # 随机目标运动方向
    target_speeds = [(random.uniform(-0.3, 0.3), random.uniform(-0.3, 0.3)) for _ in targets]

    # 岛屿轮廓 (世界坐标多边形)
    islands = [
        [(200, 200), (250, 180), (300, 220), (280, 280), (220, 270)],
        [(600, 500), (650, 480), (680, 520), (640, 560), (600, 540)]
    ]
    # 浅滩区域 (简单矩形)
    shoals = [pygame.Rect(100, 500, 100, 80), pygame.Rect(700, 200, 120, 90)]

    wake = []
    running = True

    while running:
        dt = clock.tick(FPS) / 1000.0
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running = False
            elif event.type == pygame.MOUSEBUTTONDOWN:
                if event.button == 1:  # 左键设置航路点
                    mx, my = event.pos
                    if mx < SEA_W:      # 点击在海图区域
                        # 屏幕坐标转换为世界坐标（视口中心对齐船）
                        world_x = ship_x + (mx - SEA_W/2)
                        world_y = ship_y + (SEA_H/2 - my)
                        waypoint = (world_x, world_y)
            elif event.type == pygame.KEYDOWN:
                if event.key == pygame.K_a:
                    auto_pilot = not auto_pilot
                elif event.key == pygame.K_r:
                    radar_on = not radar_on

        # 键盘输入
        keys = pygame.key.get_pressed()
        if keys[pygame.K_UP]:
            throttle = min(10, throttle + 1)
        if keys[pygame.K_DOWN]:
            throttle = max(0, throttle - 1)
        if keys[pygame.K_LEFT]:
            rudder = max(-RUDDER_MAX, rudder - RUDDER_RATE)
        elif keys[pygame.K_RIGHT]:
            rudder = min(RUDDER_MAX, rudder + RUDDER_RATE)
        else:
            if rudder > 0: rudder = max(0, rudder - RUDDER_RATE*0.5)
            else: rudder = min(0, rudder + RUDDER_RATE*0.5)

        # 速度变化
        target_spd = (throttle / 10.0) * MAX_SPEED
        if speed < target_spd:
            speed += 0.08
        else:
            speed -= 0.03
        if throttle == 0 and speed < 0.05:
            speed = 0
        speed = max(0, speed)

        # 自动舵 PID 控制航向
        if auto_pilot and waypoint:
            dx = waypoint[0] - ship_x
            dy = waypoint[1] - ship_y
            target_heading = math.degrees(math.atan2(dy, dx))
            heading_error = (target_heading - heading + 360) % 360
            if heading_error > 180:
                heading_error -= 360
            rudder_pid = -0.8 * heading_error  # 简单P控制
            rudder_pid = max(-RUDDER_MAX, min(RUDDER_MAX, rudder_pid))
            rudder = rudder_pid
        else:
            # 手动舵已在前面设置
            pass

        # 船舶运动模型 (Nomoto 一阶)
        # r = yaw_rate, δ = rudder
        yaw_rate += (NOMOTO_K * rudder - yaw_rate) / NOMOTO_T
        heading = (heading + yaw_rate) % 360

        # 侧向漂移 (与 yaw_rate 和风相关)
        drift_angle = -0.8 * yaw_rate * DRIFT_COEFF  # 转向时侧滑
        # 风的影响
        wind_rad = math.radians(wind_dir)
        wind_vec = (wind_speed * math.cos(wind_rad), -wind_speed * math.sin(wind_rad))
        ship_rad = math.radians(heading)
        ship_dir = (math.cos(ship_rad), -math.sin(ship_rad))
        ship_side = (-ship_dir[1], ship_dir[0])
        wind_along = wind_vec[0]*ship_dir[0] + wind_vec[1]*ship_dir[1]
        wind_cross = wind_vec[0]*ship_side[0] + wind_vec[1]*ship_side[1]
        speed += wind_along * 0.002
        drift_x = wind_cross * DRIFT_COEFF * 2 + drift_angle * ship_side[0]
        drift_y = wind_cross * DRIFT_COEFF * 2 + drift_angle * ship_side[1]

        # 流漂移
        curr_rad = math.radians(curr_dir)
        curr_vec = (curr_speed * math.cos(curr_rad), -curr_speed * math.sin(curr_rad))
        ship_x += curr_vec[0] * 0.4
        ship_y += curr_vec[1] * 0.4

        # 位移
        ship_x += ship_dir[0] * speed + drift_x
        ship_y += ship_dir[1] * speed + drift_y

        # 横摇模拟
        roll_angle = math.sin(pygame.time.get_ticks()*0.02) * ROLL_AMPLITUDE * (speed/MAX_SPEED)

        # 尾迹
        wake.append((ship_x, ship_y))
        if len(wake) > 300:
            wake.pop(0)

        # 移动雷达目标
        for i in range(len(targets)):
            tx, ty = targets[i]
            vx, vy = target_speeds[i]
            tx += vx
            ty += vy
            if tx < 0 or tx > 1000: target_speeds[i] = (-vx, vy)
            if ty < 0 or ty > 800: target_speeds[i] = (vx, -vy)
            targets[i] = (tx, ty)

        # 雷达扫描角度
        radar_scan = (radar_scan + 1.5) % 360

        # ---------------- 渲染 ----------------
        screen.fill(C_DEEP)

        # 海图区域 (视口跟随船只)
        sea_surf = pygame.Surface((SEA_W, SEA_H))
        sea_surf.fill(C_SEA1)
        # 绘制动态波浪条纹
        for i in range(0, SEA_H, 15):
            y_off = i + 4 * math.sin(pygame.time.get_ticks()*0.005 + i*0.1)
            col = (20 + int(10 * math.sin(i*0.05)), 50 + int(20*math.cos(i*0.1)), 140)
            pygame.draw.line(sea_surf, col, (0, y_off), (SEA_W, y_off), 1)

        # 世界坐标 -> 屏幕坐标转换
        def world_to_screen(wx, wy):
            sx = SEA_W/2 + (wx - ship_x)
            sy = SEA_H/2 - (wy - ship_y)
            return int(sx), int(sy)

        # 绘制浅滩
        for shoal in shoals:
            sx, sy = world_to_screen(shoal.x, shoal.y + shoal.height)
            rect = pygame.Rect(sx, sy, shoal.width, shoal.height)
            pygame.draw.rect(sea_surf, (30, 70, 130), rect)
            pygame.draw.rect(sea_surf, C_GRAY, rect, 1)
        # 绘制岛屿
        for island in islands:
            pts = [world_to_screen(x, y) for (x, y) in island]
            if len(pts) > 2:
                pygame.draw.polygon(sea_surf, (34, 139, 34), pts)
                pygame.draw.polygon(sea_surf, (0, 100, 0), pts, 2)
        # 绘制雷达目标 (小型船只)
        for tx, ty in targets:
            sx, sy = world_to_screen(tx, ty)
            if 0 <= sx < SEA_W and 0 <= sy < SEA_H:
                pygame.draw.circle(sea_surf, C_ORANGE, (sx, sy), 3)
        # 绘制航路点
        if waypoint:
            sx, sy = world_to_screen(*waypoint)
            if 0 <= sx < SEA_W and 0 <= sy < SEA_H:
                pygame.draw.circle(sea_surf, C_YELLOW, (sx, sy), 6, 2)
                pygame.draw.line(sea_surf, C_YELLOW, (int(SEA_W/2), int(SEA_H/2)), (sx, sy), 1)
        # 绘制尾迹
        for i, pos in enumerate(wake):
            alpha = int(150 * (i/len(wake)))
            col = (200, 220, 255, alpha)
            sx, sy = world_to_screen(pos[0], pos[1])
            if 0 <= sx < SEA_W and 0 <= sy < SEA_H:
                pygame.draw.circle(sea_surf, (200,220,255), (sx, sy), 2)
        # 绘制本船 (固定屏幕中央，但船会根据世界位置微调)
        ship_screen_x, ship_screen_y = int(SEA_W/2), int(SEA_H/2)
        draw_ship(sea_surf, (ship_screen_x, ship_screen_y), heading)
        screen.blit(sea_surf, (0, 0))

        # ---------------- 右侧仪表板 ----------------
        panel_x = SEA_W
        pygame.draw.rect(screen, (25, 25, 45), (panel_x, 0, PANEL_W, SCREEN_H))
        # 标题
        title = font_md.render("NAVIGATION PANEL", True, C_WHITE)
        screen.blit(title, (panel_x + 30, 15))

        # 罗经 (上左)
        compass_center = (panel_x + 80, 140)
        draw_compass_rose(screen, compass_center, heading)

        # 速度表 (上右)
        draw_circular_gauge(screen, (panel_x + 220, 140), speed*60, MAX_SPEED*60, "SPD kn", C_GREEN)

        # 舵角表 (中左)
        rudder_norm = (rudder + RUDDER_MAX) / (2*RUDDER_MAX)  # 0-1
        draw_circular_gauge(screen, (panel_x + 80, 290), rudder_norm, 1.0, "RUDDER", C_YELLOW, 30)

        # 主机转速 (中右)
        draw_circular_gauge(screen, (panel_x + 220, 290), throttle, 10, "RPM x100", C_RED, 30)

        # 风向风速仪 (下左)
        wind_label = font_sm.render(f"Wind {wind_speed:.0f}kn {wind_dir:.0f}°", True, C_WHITE)
        screen.blit(wind_label, (panel_x + 20, 420))
        # 风向箭头
        arrow_center = (panel_x + 80, 480)
        rad_w = math.radians(wind_dir)
        arrow_len = 25
        end_x = arrow_center[0] + arrow_len * math.sin(rad_w)
        end_y = arrow_center[1] - arrow_len * math.cos(rad_w)
        pygame.draw.line(screen, C_LGRAY, arrow_center, (end_x, end_y), 3)
        pygame.draw.circle(screen, C_LGRAY, arrow_center, 5, 1)

        # 水深显示 (模拟)
        depth = 20 + 10 * math.sin(ship_x*0.01) * math.cos(ship_y*0.01)
        depth_text = font_sm.render(f"Depth {depth:.1f}m", True, C_WHITE)
        screen.blit(depth_text, (panel_x + 20, 500))

        # 雷达 (下中)
        if radar_on:
            radar_center = (panel_x + 150, 520)
            draw_radar(screen, radar_center, 60, radar_scan, targets, (ship_x, ship_y), heading)
            rad_label = font_sm.render("RADAR ON", True, C_GREEN)
        else:
            rad_label = font_sm.render("RADAR OFF", True, C_RED)
        screen.blit(rad_label, (panel_x + 200, 550))

        # 航路点信息
        if waypoint:
            dx = waypoint[0] - ship_x
            dy = waypoint[1] - ship_y
            dist = math.hypot(dx, dy)
            brg = math.degrees(math.atan2(dy, dx))
            brg = (brg + 360) % 360
            eta = dist / (speed + 0.001) if speed > 0 else 999
            info = f"WP: {dist:.0f}px  BRG {brg:.1f}°  ETA {eta:.1f}s"
            wp_surf = font_sm.render(info, True, C_YELLOW)
            screen.blit(wp_surf, (panel_x + 15, 580))
        # 自动舵状态
        ap_text = "AUTOPILOT ON" if auto_pilot else "MANUAL"
        ap_surf = font_md.render(ap_text, True, C_GREEN if auto_pilot else C_RED)
        screen.blit(ap_surf, (panel_x + 80, 620))
        # 操作提示
        hint = font_sm.render("Arrows: steer | A: auto | R: radar | Click: set WP", True, C_GRAY)
        screen.blit(hint, (panel_x + 10, SCREEN_H-25))

        pygame.display.flip()

    pygame.quit()
    sys.exit()

if __name__ == "__main__":
    main()