import tkinter as tk
import math
import random

class SailingSimulator:
    def __init__(self, root):
        self.root = root
        self.root.title("航海模拟器 - 键盘控制")
        self.root.resizable(False, False)

        # ---------- 航行参数 ----------
        self.canvas_width = 800
        self.canvas_height = 600

        self.boat_x = self.canvas_width / 2
        self.boat_y = self.canvas_height / 2
        self.heading = 0          # 航向角，0°=右（东），逆时针增加
        self.speed = 0            # 节
        self.max_speed = 10

        # 风
        self.wind_direction = random.uniform(0, 360)
        self.wind_speed = random.uniform(5, 15)

        # 操控灵敏度
        self.turn_rate = 60       # 度/秒（按住时）
        self.acceleration = 2.0   # 节/秒
        self.dt = 0.05            # 模拟步长（秒）

        # 按键状态（持续按下时为True）
        self.key_left = False
        self.key_right = False
        self.key_up = False
        self.key_down = False

        self.running = True

        # ---------- 构建界面 ----------
        main_frame = tk.Frame(root)
        main_frame.pack(padx=10, pady=10)

        self.canvas = tk.Canvas(main_frame, width=self.canvas_width,
                                height=self.canvas_height, bg='lightblue',
                                highlightthickness=0)
        self.canvas.pack(side=tk.LEFT)

        self.draw_grid()

        ctrl_frame = tk.Frame(main_frame, width=200)
        ctrl_frame.pack(side=tk.RIGHT, fill=tk.Y, padx=10)
        ctrl_frame.pack_propagate(False)

        # 说明文字
        tk.Label(ctrl_frame, text="键盘操控", font=('Arial', 12, 'bold')).pack(pady=5)
        tk.Label(ctrl_frame, text="← → 转向\n↑ 加速 / ↓ 减速",
                 font=('Arial', 10), justify=tk.LEFT).pack(pady=5)

        tk.Label(ctrl_frame, text="航行数据", font=('Arial', 12, 'bold')).pack(pady=10)
        self.info_var = tk.StringVar()
        tk.Label(ctrl_frame, textvariable=self.info_var, font=('Courier', 10),
                 justify=tk.LEFT, anchor='w').pack(fill=tk.X, padx=5)

        tk.Label(ctrl_frame, text="风力", font=('Arial', 12, 'bold')).pack(pady=10)
        self.wind_canvas = tk.Canvas(ctrl_frame, width=120, height=120, bg='white')
        self.wind_canvas.pack()

        tk.Button(ctrl_frame, text="重置船只", command=self.reset_boat).pack(pady=20)

        # ---------- 图形元素 ----------
        self.boat_shape = None
        self.draw_boat()
        self.update_wind_indicator()

        # ---------- 绑定键盘事件（按下/释放） ----------
        self.root.bind('<KeyPress-Left>', self.on_key_press)
        self.root.bind('<KeyPress-Right>', self.on_key_press)
        self.root.bind('<KeyPress-Up>', self.on_key_press)
        self.root.bind('<KeyPress-Down>', self.on_key_press)
        self.root.bind('<KeyRelease-Left>', self.on_key_release)
        self.root.bind('<KeyRelease-Right>', self.on_key_release)
        self.root.bind('<KeyRelease-Up>', self.on_key_release)
        self.root.bind('<KeyRelease-Down>', self.on_key_release)

        self.update_simulation()

    # ---------- 键盘事件处理 ----------
    def on_key_press(self, event):
        if event.keysym == 'Left':
            self.key_left = True
        elif event.keysym == 'Right':
            self.key_right = True
        elif event.keysym == 'Up':
            self.key_up = True
        elif event.keysym == 'Down':
            self.key_down = True

    def on_key_release(self, event):
        if event.keysym == 'Left':
            self.key_left = False
        elif event.keysym == 'Right':
            self.key_right = False
        elif event.keysym == 'Up':
            self.key_up = False
        elif event.keysym == 'Down':
            self.key_down = False

    # ---------- 绘图 ----------
    def draw_grid(self):
        for i in range(0, self.canvas_width, 50):
            self.canvas.create_line(i, 0, i, self.canvas_height, fill='white', dash=(2, 4))
        for j in range(0, self.canvas_height, 50):
            self.canvas.create_line(0, j, self.canvas_width, j, fill='white', dash=(2, 4))

    def draw_boat(self):
        self.canvas.delete("boat")
        size = 15
        angle_rad = math.radians(-self.heading + 90)
        tip_x = self.boat_x + size * math.cos(angle_rad)
        tip_y = self.boat_y - size * math.sin(angle_rad)
        angle_left = angle_rad + math.radians(140)
        angle_right = angle_rad - math.radians(140)
        left_x = self.boat_x + size * 0.6 * math.cos(angle_left)
        left_y = self.boat_y - size * 0.6 * math.sin(angle_left)
        right_x = self.boat_x + size * 0.6 * math.cos(angle_right)
        right_y = self.boat_y - size * 0.6 * math.sin(angle_right)

        self.boat_shape = self.canvas.create_polygon(
            tip_x, tip_y, left_x, left_y, right_x, right_y,
            fill='darkred', outline='black', width=2, tags="boat"
        )

    def reset_boat(self):
        self.boat_x = self.canvas_width / 2
        self.boat_y = self.canvas_height / 2
        self.heading = 0
        self.speed = 0

    def update_wind_indicator(self):
        self.wind_canvas.delete("wind")
        cx, cy = 60, 60
        wind_math = (90 - self.wind_direction) % 360
        rad = math.radians(wind_math)
        arrow_len = 30
        dx = arrow_len * math.cos(rad)
        dy = -arrow_len * math.sin(rad)
        self.wind_canvas.create_line(cx, cy, cx + dx, cy + dy,
                                     arrow=tk.LAST, width=2, fill='blue', tags="wind")
        self.wind_canvas.create_text(cx, cy - 25, text=f"{self.wind_direction:.0f}°",
                                     font=('Arial', 8), tags="wind")
        self.wind_canvas.create_text(cx, cy + 25, text=f"{self.wind_speed:.1f} kn",
                                     font=('Arial', 8), tags="wind")

    # ---------- 物理与操控 ----------
    def apply_wind_effect(self):
        if self.speed == 0:
            return 0, 0
        boat_heading_met = (90 - self.heading) % 360
        diff = (self.wind_direction - boat_heading_met + 180) % 360 - 180
        rel_wind_angle = abs(diff)

        factor = math.cos(math.radians(rel_wind_angle))
        speed_change = 0.02 * self.wind_speed * factor * self.dt

        side_factor = math.sin(math.radians(rel_wind_angle)) * (1 if diff > 0 else -1)
        heading_change = 0.5 * self.wind_speed * side_factor * self.dt

        return speed_change, heading_change

    def update_simulation(self):
        if not self.running:
            return

        # ----- 根据按住键的状态计算操控输入 -----
        if self.key_left:
            self.heading = (self.heading + self.turn_rate * self.dt) % 360
        if self.key_right:
            self.heading = (self.heading - self.turn_rate * self.dt) % 360
        if self.key_up:
            self.speed = min(self.speed + self.acceleration * self.dt, self.max_speed)
        if self.key_down:
            self.speed = max(self.speed - self.acceleration * self.dt, 0)

        # ----- 风力影响 -----
        ds, dh = self.apply_wind_effect()
        self.speed = max(0, min(self.speed + ds, self.max_speed))
        self.heading = (self.heading + dh) % 360

        # ----- 根据速度移动 -----
        if self.speed > 0:
            rad = math.radians(90 - self.heading)
            dx = self.speed * math.cos(rad) * self.dt
            dy = -self.speed * math.sin(rad) * self.dt
            self.boat_x += dx
            self.boat_y += dy

            self.boat_x = self.boat_x % self.canvas_width
            self.boat_y = self.boat_y % self.canvas_height

        self.draw_boat()
        self.update_info()

        # 风缓慢变化
        if random.random() < 0.01:
            self.wind_direction = (self.wind_direction + random.uniform(-5, 5)) % 360
            self.wind_speed = max(0, self.wind_speed + random.uniform(-0.5, 0.5))
            self.update_wind_indicator()

        self.root.after(int(self.dt * 1000), self.update_simulation)

    def update_info(self):
        nav_heading = (90 - self.heading) % 360
        info = (
            f"船位: ({self.boat_x:.0f}, {self.boat_y:.0f})\n"
            f"航向: {nav_heading:.1f}°\n"
            f"航速: {self.speed:.1f} 节\n"
            f"风向: {self.wind_direction:.0f}°\n"
            f"风速: {self.wind_speed:.1f} 节"
        )
        self.info_var.set(info)

    def on_closing(self):
        self.running = False
        self.root.destroy()


if __name__ == "__main__":
    root = tk.Tk()
    app = SailingSimulator(root)
    root.protocol("WM_DELETE_WINDOW", app.on_closing)
    root.mainloop()