"""
二维地图应用 - 基于 tkinter
功能：绘制地图背景、添加地点标记、绘制路径、缩放、平移、测距
"""

import tkinter as tk
from tkinter import ttk, messagebox, simpledialog
import math
import random


class MapCanvas:
    """地图画布类，管理所有地图元素的绘制与交互"""

    def __init__(self, canvas):
        self.canvas = canvas
        self.scale = 1.0           # 缩放比例
        self.offset_x = 0          # 平移偏移 X
        self.offset_y = 0          # 平移偏移 Y
        self.markers = []          # 标记列表 [(x, y, name, color)]
        self.paths = []            # 路径列表 [(x1, y1, x2, y2, color)]
        self.polygons = []         # 区域列表 [[(x,y),...], color, name]
        self.dragging = False
        self.drag_start = (0, 0)
        self.measure_points = []   # 测距点
        self.mode = "pan"          # 当前模式: pan / add_marker / measure / draw_path

        # 绑定事件
        self.canvas.bind("<ButtonPress-1>", self.on_press)
        self.canvas.bind("<B1-Motion>", self.on_drag)
        self.canvas.bind("<ButtonRelease-1>", self.on_release)
        self.canvas.bind("<MouseWheel>", self.on_zoom)

    # ── 坐标转换 ──
    def world_to_screen(self, wx, wy):
        """世界坐标 → 屏幕坐标"""
        sx = wx * self.scale + self.offset_x
        sy = wy * self.scale + self.offset_y
        return sx, sy

    def screen_to_world(self, sx, sy):
        """屏幕坐标 → 世界坐标"""
        wx = (sx - self.offset_x) / self.scale
        wy = (sy - self.offset_y) / self.scale
        return wx, wy

    # ── 绘制 ──
    def draw(self):
        """重绘整个地图"""
        self.canvas.delete("all")
        w = self.canvas.winfo_width()
        h = self.canvas.winfo_height()

        # 1. 背景网格（模拟地图底图）
        self._draw_grid(w, h)

        # 2. 模拟地形（湖泊、绿地）
        self._draw_terrain()

        # 3. 区域多边形
        for points, color, name in self.polygons:
            sp = []
            for px, py in points:
                sx, sy = self.world_to_screen(px, py)
                sp.extend([sx, sy])
            if len(sp) >= 6:
                self.canvas.create_polygon(
                    sp, fill=color, outline="#555", width=1, stipple="gray25"
                )
                # 标注名称
                cx = sum(p[0] for p in points) / len(points)
                cy = sum(p[1] for p in points) / len(points)
                sx, sy = self.world_to_screen(cx, cy)
                self.canvas.create_text(sx, sy, text=name, fill="#333",
                                        font=("WenQuanYi Micro Hei", 10, "bold"))

        # 4. 路径
        for x1, y1, x2, y2, color in self.paths:
            sx1, sy1 = self.world_to_screen(x1, y1)
            sx2, sy2 = self.world_to_screen(x2, y2)
            self.canvas.create_line(sx1, sy1, sx2, sy2, fill=color, width=3,
                                    arrow=tk.LAST, arrowshape=(10, 12, 5))

        # 5. 标记
        for x, y, name, color in self.markers:
            sx, sy = self.world_to_screen(x, y)
            r = 8
            self.canvas.create_oval(sx - r, sy - r, sx + r, sy + r,
                                    fill=color, outline="#222", width=2, tags="marker")
            self.canvas.create_text(sx, sy - r - 10, text=name, fill="#111",
                                    font=("WenQuanYi Micro Hei", 9, "bold"),
                                    tags="marker")

        # 6. 测距线
        if len(self.measure_points) >= 2:
            for i in range(len(self.measure_points) - 1):
                mx1, my1 = self.measure_points[i]
                mx2, my2 = self.measure_points[i + 1]
                sx1, sy1 = self.world_to_screen(mx1, my1)
                sx2, sy2 = self.world_to_screen(mx2, my2)
                self.canvas.create_line(sx1, sy1, sx2, sy2, fill="#e74c3c",
                                        width=2, dash=(6, 3))
            # 标注距离
            total = 0
            for i in range(len(self.measure_points) - 1):
                dx = self.measure_points[i + 1][0] - self.measure_points[i][0]
                dy = self.measure_points[i + 1][1] - self.measure_points[i][1]
                seg = math.sqrt(dx * dx + dy * dy)
                total += seg
                mx = (self.measure_points[i][0] + self.measure_points[i + 1][0]) / 2
                my = (self.measure_points[i][1] + self.measure_points[i + 1][1]) / 2
                sx, sy = self.world_to_screen(mx, my)
                self.canvas.create_text(sx, sy - 10, text=f"{seg:.0f}m",
                                        fill="#e74c3c", font=("Arial", 8))
            # 总距离
            last = self.measure_points[-1]
            sx, sy = self.world_to_screen(last[0], last[1])
            self.canvas.create_text(sx + 20, sy, text=f"总长:{total:.0f}m",
                                    fill="#c0392b", font=("Arial", 9, "bold"))

        # 7. 比例尺
        self._draw_scale_bar(w, h)

    def _draw_grid(self, w, h):
        """绘制背景网格"""
        grid_size = 50 * self.scale
        ox = self.offset_x % grid_size
        oy = self.offset_y % grid_size

        # 浅色网格
        for x in range(int(ox), w, int(grid_size)):
            self.canvas.create_line(x, 0, x, h, fill="#e8e8e8", width=1)
        for y in range(int(oy), h, int(grid_size)):
            self.canvas.create_line(0, y, w, y, fill="#e8e8e8", width=1)

        # 十字准星（地图中心）
        cx, cy = w // 2, h // 2
        self.canvas.create_line(cx - 15, cy, cx + 15, cy, fill="#bbb", width=1)
        self.canvas.create_line(cx, cy - 15, cx, cy + 15, fill="#bbb", width=1)

    def _draw_terrain(self):
        """绘制模拟地形（湖泊 + 绿地）"""
        # 湖泊
        lake_pts = [(300, 200), (450, 180), (550, 250), (500, 350),
                    (400, 380), (280, 320), (250, 260)]
        sl = []
        for px, py in lake_pts:
            sx, sy = self.world_to_screen(px, py)
            sl.extend([sx, sy])
        self.canvas.create_polygon(sl, fill="#aed6f1", outline="#5dade2", width=2)

        # 绿地公园
        park_pts = [(100, 400), (250, 380), (300, 480), (200, 550), (80, 500)]
        sp = []
        for px, py in park_pts:
            sx, sy = self.world_to_screen(px, py)
            sp.extend([sx, sy])
        self.canvas.create_polygon(sp, fill="#d5f5e3", outline="#58d68d", width=2)

    def _draw_scale_bar(self, w, h):
        """绘制比例尺"""
        bar_len = 100 * self.scale  # 像素长度
        real_len = bar_len / self.scale  # 实际长度（米）
        y_pos = h - 30
        x_pos = 20
        self.canvas.create_rectangle(x_pos, y_pos, x_pos + bar_len, y_pos + 6,
                                     fill="#34495e", outline="#2c3e50")
        self.canvas.create_text(x_pos + bar_len / 2, y_pos - 8,
                                 text=f"0 ───── {real_len:.0f} m",
                                 fill="#2c3e50", font=("Arial", 8))

    # ── 鼠标事件 ──
    def on_press(self, event):
        if self.mode == "pan":
            self.dragging = True
            self.drag_start = (event.x, event.y)
        elif self.mode == "add_marker":
            wx, wy = self.screen_to_world(event.x, event.y)
            name = simpledialog.askstring("添加标记", "请输入地点名称:", parent=self.canvas)
            if name:
                colors = ["#e74c3c", "#3498db", "#2ecc71", "#f39c12", "#9b59b6", "#1abc9c"]
                color = random.choice(colors)
                self.markers.append((wx, wy, name, color))
                self.draw()
        elif self.mode == "measure":
            wx, wy = self.screen_to_world(event.x, event.y)
            self.measure_points.append((wx, wy))
            self.draw()
        elif self.mode == "draw_path":
            wx, wy = self.screen_to_world(event.x, event.y)
            if not hasattr(self, '_path_temp'):
                self._path_temp = []
            self._path_temp.append((wx, wy))
            if len(self._path_temp) >= 2:
                x1, y1 = self._path_temp[-2]
                x2, y2 = self._path_temp[-1]
                colors = ["#e74c3c", "#3498db", "#2ecc71", "#f39c12"]
                self.paths.append((x1, y1, x2, y2, random.choice(colors)))
            self.draw()

    def on_drag(self, event):
        if self.mode == "pan" and self.dragging:
            dx = event.x - self.drag_start[0]
            dy = event.y - self.drag_start[1]
            self.offset_x += dx
            self.offset_y += dy
            self.drag_start = (event.x, event.y)
            self.draw()

    def on_release(self, event):
        self.dragging = False

    def on_zoom(self, event):
        factor = 1.1 if event.delta > 0 else 0.9
        new_scale = self.scale * factor
        if 0.2 <= new_scale <= 5.0:
            # 以鼠标位置为缩放中心
            wx, wy = self.screen_to_world(event.x, event.y)
            self.scale = new_scale
            self.offset_x = event.x - wx * self.scale
            self.offset_y = event.y - wy * self.scale
            self.draw()

    # ── 公共方法 ──
    def set_mode(self, mode):
        self.mode = mode
        if mode != "draw_path":
            self._path_temp = []
        self.canvas.config(cursor={"pan": "hand2", "add_marker": "crosshair",
                                    "measure": "crosshair", "draw_path": "pencil"}.get(mode, ""))

    def clear_markers(self):
        self.markers.clear()
        self.draw()

    def clear_paths(self):
        self.paths.clear()
        self.draw()

    def clear_measure(self):
        self.measure_points.clear()
        self.draw()

    def reset_view(self):
        self.scale = 1.0
        self.offset_x = 0
        self.offset_y = 0
        self.draw()

    def add_sample_data(self):
        """添加示例数据"""
        self.markers = [
            (200, 150, "市政府", "#e74c3c"),
            (500, 300, "火车站", "#3498db"),
            (350, 500, "中心医院", "#2ecc71"),
            (600, 150, "体育中心", "#f39c12"),
            (150, 450, "图书馆", "#9b59b6"),
        ]
        self.paths = [
            (200, 150, 500, 300, "#e74c3c"),
            (500, 300, 350, 500, "#3498db"),
            (200, 150, 150, 450, "#2ecc71"),
        ]
        self.polygons = [
            ([(100, 400), (250, 380), (300, 480), (200, 550), (80, 500)],
             "#d5f5e3", "中央公园"),
            ([(400, 100), (550, 80), (600, 150), (500, 200), (420, 180)],
             "#fdebd0", "商业区"),
        ]
        self.draw()


class MapApp:
    """主应用窗口"""

    def __init__(self, root):
        self.root = root
        self.root.title("🗺️  二维地图编辑器")
        self.root.geometry("1100x750")
        self.root.minsize(800, 600)

        # 样式
        style = ttk.Style()
        style.theme_use("clam")
        style.configure("TButton", font=("WenQuanYi Micro Hei", 10), padding=5)
        style.configure("TLabel", font=("WenQuanYi Micro Hei", 10))
        style.configure("Header.TLabel", font=("WenQuanYi Micro Hei", 14, "bold"))

        self._build_ui()

    def _build_ui(self):
        # ── 顶部工具栏 ──
        toolbar = ttk.Frame(self.root, padding=5)
        toolbar.pack(side=tk.TOP, fill=tk.X)

        ttk.Label(toolbar, text="🗺️ 二维地图编辑器", style="Header.TLabel").pack(side=tk.LEFT, padx=10)

        ttk.Separator(toolbar, orient=tk.VERTICAL).pack(side=tk.LEFT, fill=tk.Y, padx=10)

        self.btn_pan = ttk.Button(toolbar, text="✋ 平移", command=lambda: self._set_mode("pan"))
        self.btn_pan.pack(side=tk.LEFT, padx=2)

        self.btn_marker = ttk.Button(toolbar, text="📍 添加标记", command=lambda: self._set_mode("add_marker"))
        self.btn_marker.pack(side=tk.LEFT, padx=2)

        self.btn_path = ttk.Button(toolbar, text="✏️ 绘制路径", command=lambda: self._set_mode("draw_path"))
        self.btn_path.pack(side=tk.LEFT, padx=2)

        self.btn_measure = ttk.Button(toolbar, text="📏 测距", command=lambda: self._set_mode("measure"))
        self.btn_measure.pack(side=tk.LEFT, padx=2)

        ttk.Separator(toolbar, orient=tk.VERTICAL).pack(side=tk.LEFT, fill=tk.Y, padx=8)

        ttk.Button(toolbar, text="🔄 重置视图", command=self._reset_view).pack(side=tk.LEFT, padx=2)
        ttk.Button(toolbar, text="📊 示例数据", command=self._load_sample).pack(side=tk.LEFT, padx=2)
        ttk.Button(toolbar, text="🗑️ 清除标记", command=self._clear_markers).pack(side=tk.LEFT, padx=2)
        ttk.Button(toolbar, text="🗑️ 清除路径", command=self._clear_paths).pack(side=tk.LEFT, padx=2)
        ttk.Button(toolbar, text="🗑️ 清除测距", command=self._clear_measure).pack(side=tk.LEFT, padx=2)

        # ── 中间主区域 ──
        main = ttk.Frame(self.root)
        main.pack(side=tk.TOP, fill=tk.BOTH, expand=True)

        # 左侧地图画布
        self.canvas = tk.Canvas(main, bg="#f5f5f5", highlightthickness=0)
        self.canvas.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)

        self.map_canvas = MapCanvas(self.canvas)

        # 右侧信息面板
        self._build_sidebar(main)

        # ── 底部状态栏 ──
        self.status = ttk.Label(self.root, text="就绪 | 模式: 平移 | 滚轮缩放，拖拽平移",
                                relief=tk.SUNKEN, anchor=tk.W, padding=3)
        self.status.pack(side=tk.BOTTOM, fill=tk.X)

        # 绑定画布大小变化
        self.canvas.bind("<Configure>", lambda e: self.map_canvas.draw())

        # 初始绘制
        self.root.after(100, self.map_canvas.draw)

    def _build_sidebar(self, parent):
        sidebar = ttk.Frame(parent, width=220, padding=10)
        sidebar.pack(side=tk.RIGHT, fill=tk.Y)
        sidebar.pack_propagate(False)

        ttk.Label(sidebar, text="📋 操作说明", font=("WenQuanYi Micro Hei", 12, "bold")).pack(anchor=tk.W, pady=(0, 8))

        tips = [
            ("✋ 平移模式", "鼠标拖拽移动地图视图"),
            ("📍 添加标记", "点击地图添加地点标记"),
            ("✏️ 绘制路径", "依次点击连接路径点"),
            ("📏 测距工具", "点击多点测量总距离"),
            ("🔍 缩放", "鼠标滚轮放大/缩小"),
        ]
        for title, desc in tips:
            frame = ttk.Frame(sidebar)
            frame.pack(fill=tk.X, pady=3)
            ttk.Label(frame, text=title, font=("WenQuanYi Micro Hei", 10, "bold")).pack(anchor=tk.W)
            ttk.Label(frame, text=desc, font=("WenQuanYi Micro Hei", 9), foreground="#666").pack(anchor=tk.W)

        ttk.Separator(sidebar, orient=tk.HORIZONTAL).pack(fill=tk.X, pady=10)

        ttk.Label(sidebar, text="📍 标记列表", font=("WenQuanYi Micro Hei", 11, "bold")).pack(anchor=tk.W, pady=(0, 5))
        self.marker_listbox = tk.Listbox(sidebar, height=10, font=("WenQuanYi Micro Hei", 9))
        self.marker_listbox.pack(fill=tk.BOTH, expand=False, pady=2)

        ttk.Separator(sidebar, orient=tk.HORIZONTAL).pack(fill=tk.X, pady=10)

        ttk.Label(sidebar, text="⚙️ 设置", font=("WenQuanYi Micro Hei", 11, "bold")).pack(anchor=tk.W, pady=(0, 5))

        # 缩放滑块
        ttk.Label(sidebar, text="缩放级别:").pack(anchor=tk.W)
        self.zoom_slider = ttk.Scale(sidebar, from_=0.2, to=5.0, value=1.0,
                                      orient=tk.HORIZONTAL, command=self._on_slider_zoom)
        self.zoom_slider.pack(fill=tk.X, pady=2)

        ttk.Button(sidebar, text="🌍 重置视图", command=self._reset_view).pack(fill=tk.X, pady=5)

    # ── 回调 ──
    def _set_mode(self, mode):
        self.map_canvas.set_mode(mode)
        mode_names = {"pan": "平移", "add_marker": "添加标记",
                      "measure": "测距", "draw_path": "绘制路径"}
        self.status.config(text=f"模式: {mode_names.get(mode, mode)} | 滚轮缩放，拖拽平移")
        # 高亮当前按钮
        for btn, m in [(self.btn_pan, "pan"), (self.btn_marker, "add_marker"),
                       (self.btn_path, "draw_path"), (self.btn_measure, "measure")]:
            btn.config(style="TButton")
            if m == mode:
                btn.config(style="TButton")

    def _on_slider_zoom(self, val):
        self.map_canvas.scale = float(val)
        self.map_canvas.draw()

    def _reset_view(self):
        self.map_canvas.reset_view()
        self.zoom_slider.set(1.0)

    def _load_sample(self):
        self.map_canvas.add_sample_data()
        self._refresh_marker_list()

    def _clear_markers(self):
        self.map_canvas.clear_markers()
        self._refresh_marker_list()

    def _clear_paths(self):
        self.map_canvas.clear_paths()

    def _clear_measure(self):
        self.map_canvas.clear_measure()

    def _refresh_marker_list(self):
        self.marker_listbox.delete(0, tk.END)
        for _, _, name, color in self.map_canvas.markers:
            self.marker_listbox.insert(tk.END, f"● {name}")
            self.marker_listbox.itemconfig(tk.END, fg=color)


def main():
    root = tk.Tk()
    app = MapApp(root)
    root.mainloop()


if __name__ == "__main__":
    main()
