import tkinter as tk
from tkinter import messagebox, filedialog
import json
import random
import math

# ========================
# 全局配置
# ========================
CELL = 36
GRID_W, GRID_H = 20, 14
WIDTH = GRID_W * CELL + 280
HEIGHT = GRID_H * CELL + 100

# ========================
# 图块定义
# ========================
TILES = {
    "empty":    {"name": "空地",   "icon": "  ", "color": "#AED581", "walk": True},
    "wall":     {"name": "墙壁",   "icon": "🧱", "color": "#795548", "walk": False},
    "water":    {"name": "水域",   "icon": "🌊", "color": "#42A5F5", "walk": False},
    "lava":     {"name": "岩浆",   "icon": "🔥", "color": "#D84315", "walk": False},
    "grass":    {"name": "草地",   "icon": "🌿", "color": "#66BB6A", "walk": True},
    "sand":     {"name": "沙漠",   "icon": "🏜️", "color": "#FFD54F", "walk": True},
    "snow":     {"name": "雪地",   "icon": "❄️", "color": "#ECEFF1", "walk": True},
    "stone":    {"name": "石地",   "icon": "🪨", "color": "#9E9E9E", "walk": True},
    "wood":     {"name": "木板",   "icon": "🪵", "color": "#8D6E63", "walk": True},
    "road":     {"name": "道路",   "icon": "━",  "color": "#D7CCC8", "walk": True},
    "door":     {"name": "门",     "icon": "🚪", "color": "#5D4037", "walk": True},
    "chest":    {"name": "宝箱",   "icon": "📦", "color": "#FF8F00", "walk": False},
    "spawn":    {"name": "出生点", "icon": "⭐", "color": "#FFEB3B", "walk": True},
    "enemy":    {"name": "敌人",   "icon": "👾", "color": "#E53935", "walk": False},
    "npc":      {"name": "NPC",   "icon": "🧑", "color": "#7E57C2", "walk": False},
    "tree":     {"name": "树木",   "icon": "🌲", "color": "#2E7D32", "walk": False},
    "flower":   {"name": "花朵",   "icon": "🌸", "color": "#F48FB1", "walk": True},
    "crystal":  {"name": "水晶",   "icon": "🔮", "color": "#00ACC1", "walk": False},
    "trap":     {"name": "陷阱",   "icon": "⚠️", "color": "#FF6E40", "walk": True},
    "portal":   {"name": "传送门", "icon": "🌀", "color": "#AB47BC", "walk": True},
}

TILE_KEYS = list(TILES.keys())

# ========================
# 主题
# ========================
THEMES = {
    "🗺️ 古典羊皮": {"bg": "#D7CCC8", "fg": "#3E2723", "panel": "#BCAAA4", "btn": "#8D6E63", "accent": "#5D4037", "grid": "#A1887F"},
    "🌙 暗夜模式": {"bg": "#263238", "fg": "#ECEFF1", "panel": "#37474F", "btn": "#546E7A", "accent": "#FF6E40", "grid": "#455A64"},
    "🏜️ 沙漠之风": {"bg": "#FFF3E0", "fg": "#4E342E", "panel": "#FFE0B2", "btn": "#FF8F00", "accent": "#BF360C", "grid": "#FFB74D"},
    "❄️ 冰雪王国": {"bg": "#E3F2FD", "fg": "#0D47A1", "panel": "#BBDEFB", "btn": "#1976D2", "accent": "#D32F2F", "grid": "#90CAF9"},
    "🌸 樱花工坊": {"bg": "#FFF0F5", "fg": "#880E4F", "panel": "#F8BBD0", "btn": "#C2185B", "accent": "#4A148C", "grid": "#F48FB1"},
    "🔥 烈焰熔炉": {"bg": "#FBE9E7", "fg": "#BF360C", "panel": "#FFAB91", "btn": "#E64A19", "accent": "#FF3D00", "grid": "#FF8A65"},
}
cur_theme = "🗺️ 古典羊皮"

# ========================
# 地图数据
# ========================
grid = []           # 图块网格
layers = {}         # 多图层
cur_layer = 0
num_layers = 3
cur_tile = "empty"
layers_visible = [True, True, True]
undo_stack = []
redo_stack = []
map_name = "未命名地图"
is_modified = False

# ========================
# UI 引用
# ========================
root = None
canvas = None
lbl_name = None
lbl_tile = None
lbl_layer = None
lbl_cursor = None
lbl_stats = None
tile_btns = []
layer_btns = []
theme_btns = []
all_widgets = []
panel_frame = None

# ========================
# 工具函数
# ========================
def get_tile_at(x, y, layer=None):
    if not (0 <= x < GRID_W and 0 <= y < GRID_H):
        return None
    l = layer if layer is not None else cur_layer
    if l in layers:
        return layers[l].get((x, y))
    return grid[y][x] if l == 0 else None

def set_tile_at(x, y, tile, layer=None):
    global is_modified
    l = layer if layer is not None else cur_layer
    if not (0 <= x < GRID_W and 0 <= y < GRID_H):
        return
    is_modified = True
    
    if l == 0:
        old = grid[y][x]
        grid[y][x] = tile
    else:
        if l not in layers:
            layers[l] = {}
        old = layers[l].get((x, y))
        if tile == "empty" and (x, y) in layers[l]:
            del layers[l][(x, y)]
        else:
            layers[l][(x, y)] = tile
    
    # 记录撤销
    undo_stack.append((l, x, y, old, tile))
    if len(undo_stack) > 200:
        undo_stack.pop(0)
    redo_stack.clear()

def fill_area(sx, sy, tile):
    """泛洪填充"""
    target = get_tile_at(sx, sy)
    if target == tile:
        return
    queue = [(sx, sy)]
    while queue:
        x, y = queue.pop(0)
        if get_tile_at(x, y) != target:
            continue
        set_tile_at(x, y, tile)
        for dx, dy in [(0,1),(0,-1),(1,0),(-1,0)]:
            nx, ny = x+dx, y+dy
            if 0 <= nx < GRID_W and 0 <= ny < GRID_H:
                if get_tile_at(nx, ny) == target:
                    queue.append((nx, ny))

def random_map():
    """随机生成地图"""
    global is_modified
    for y in range(GRID_H):
        for x in range(GRID_W):
            r = random.random()
            if r < 0.15:
                grid[y][x] = "wall"
            elif r < 0.25:
                grid[y][x] = "water"
            elif r < 0.30:
                grid[y][x] = "tree"
            elif r < 0.35:
                grid[y][x] = "grass"
            elif r < 0.38:
                grid[y][x] = "sand"
            elif r < 0.40:
                grid[y][x] = "stone"
            elif r < 0.42:
                grid[y][x] = "flower"
            else:
                grid[y][x] = "empty"
    
    # 边界墙
    for x in range(GRID_W):
        grid[0][x] = "wall"
        grid[GRID_H-1][x] = "wall"
    for y in range(GRID_H):
        grid[y][0] = "wall"
        grid[y][GRID_W-1] = "wall"
    
    # 出生点
    grid[GRID_H//2][1] = "spawn"
    
    # 随机敌人
    for _ in range(5):
        ex, ey = random.randint(2, GRID_W-3), random.randint(2, GRID_H-3)
        grid[ey][ex] = "enemy"
    
    # 宝箱
    for _ in range(3):
        cx, cy = random.randint(2, GRID_W-3), random.randint(2, GRID_H-3)
        grid[cy][cx] = "chest"
    
    is_modified = True
    log_msg("🎲 已生成随机地图")

def clear_map():
    """清空地图"""
    global grid, layers, is_modified
    grid = [["empty" for _ in range(GRID_W)] for _ in range(GRID_H)]
    layers = {}
    undo_stack.clear()
    redo_stack.clear()
    is_modified = False
    log_msg("🗑️ 地图已清空")

def new_map():
    """新建地图"""
    global grid, layers, undo_stack, redo_stack, map_name, is_modified
    if is_modified:
        if not messagebox.askyesno("确认", "当前地图未保存，确定新建？"):
            return
    grid = [["empty" for _ in range(GRID_W)] for _ in range(GRID_H)]
    layers = {}
    undo_stack.clear()
    redo_stack.clear()
    map_name = "未命名地图"
    is_modified = False
    update_title()
    log_msg("📄 已新建地图")

# ========================
# 保存/加载
# ========================
def save_map():
    """保存地图"""
    global map_name, is_modified
    path = filedialog.asksaveasfilename(
        defaultextension=".json",
        filetypes=[("JSON地图", "*.json"), ("所有文件", "*.*")]
    )
    if not path:
        return
    data = {
        "name": map_name,
        "width": GRID_W,
        "height": GRID_H,
        "grid": grid,
        "layers": {str(k): [[x, y, v] for (x, y), v in v.items()] for k, v in layers.items()}
    }
    with open(path, "w", encoding="utf-8") as f:
        json.dump(data, f, ensure_ascii=False, indent=2)
    map_name = path.split("/")[-1].replace(".json", "")
    is_modified = False
    update_title()
    log_msg(f"💾 已保存: {map_name}")

def load_map():
    """加载地图"""
    global grid, layers, map_name, is_modified
    if is_modified:
        if not messagebox.askyesno("确认", "当前地图未保存，确定加载？"):
            return
    path = filedialog.askopenfilename(
        filetypes=[("JSON地图", "*.json"), ("所有文件", "*.*")]
    )
    if not path:
        return
    try:
        with open(path, "r", encoding="utf-8") as f:
            data = json.load(f)
        w, h = data.get("width", GRID_W), data.get("height", GRID_H)
        grid = data.get("grid", [["empty"]*w for _ in range(h)])
        raw_layers = data.get("layers", {})
        layers = {}
        for k, v in raw_layers.items():
            layers[int(k)] = {(x, y): t for x, y, t in v}
        map_name = path.split("/")[-1].replace(".json", "")
        is_modified = False
        update_title()
        log_msg(f"📂 已加载: {map_name}")
    except Exception as e:
        log_msg(f"❌ 加载失败: {e}")

# ========================
# 导出
# ========================
def export_png():
    """导出为 PNG（需要 PIL）"""
    try:
        from PIL import Image, ImageDraw, ImageFont
    except ImportError:
        log_msg("❌ 需要安装 Pillow: pip install pillow")
        return
    
    path = filedialog.asksaveasfilename(
        defaultextension=".png",
        filetypes=[("PNG图片", "*.png")]
    )
    if not path:
        return
    
    img = Image.new("RGB", (GRID_W * CELL, GRID_H * CELL), "#FFFFFF")
    draw = ImageDraw.Draw(img)
    
    for y in range(GRID_H):
        for x in range(GRID_W):
            tile = grid[y][x]
            color = TILES.get(tile, {}).get("color", "#CCCCCC")
            draw.rectangle([x*CELL, y*CELL, (x+1)*CELL, (y+1)*CELL], fill=color)
    
    # 网格线
    for x in range(GRID_W + 1):
        draw.line([(x*CELL, 0), (x*CELL, GRID_H*CELL)], fill="#999999", width=1)
    for y in range(GRID_H + 1):
        draw.line([(0, y*CELL), (GRID_W*CELL, y*CELL)], fill="#999999", width=1)
    
    img.save(path)
    log_msg(f"🖼️ 已导出: {path.split('/')[-1]}")

def export_txt():
    """导出为文本地图"""
    path = filedialog.asksaveasfilename(
        defaultextension=".txt",
        filetypes=[("文本文件", "*.txt")]
    )
    if not path:
        return
    
    lines = []
    lines.append(f"# 地图: {map_name}")
    lines.append(f"# 尺寸: {GRID_W}x{GRID_H}")
    lines.append("")
    
    # 图例
    used = set()
    for row in grid:
        for t in row:
            used.add(t)
    lines.append("# 图例:")
    for t in sorted(used):
        info = TILES.get(t, {})
        lines.append(f"#   {t:8s} = {info.get('name','?')} {info.get('icon','')}")
    lines.append("")
    
    # 地图
    for row in grid:
        line = ""
        for t in row:
            icon = TILES.get(t, {}).get("icon", "?")
            line += icon if icon.strip() else "  "
        lines.append(line)
    
    with open(path, "w", encoding="utf-8") as f:
        f.write("\n".join(lines))
    
    log_msg(f"📄 已导出文本地图")

# ========================
# 撤销/重做
# ========================
def undo():
    if not undo_stack:
        return
    l, x, y, old, new = undo_stack.pop()
    redo_stack.append((l, x, y, old, new))
    if l == 0:
        grid[y][x] = old
    else:
        if old is None:
            if (x, y) in layers.get(l, {}):
                del layers[l][(x, y)]
        else:
            layers.setdefault(l, {})[(x, y)] = old
    log_msg(f"↩️ 撤销 ({x},{y})")

def redo():
    if not redo_stack:
        return
    l, x, y, old, new = redo_stack.pop()
    undo_stack.append((l, x, y, old, new))
    if l == 0:
        grid[y][x] = new
    else:
        if new is None:
            if (x, y) in layers.get(l, {}):
                del layers[l][(x, y)]
        else:
            layers.setdefault(l, {})[(x, y)] = new
    log_msg(f"↪️ 重做 ({x},{y})")

# ========================
# 绘制
# ========================
def draw():
    """绘制地图"""
    canvas.delete("all")
    t = THEMES[cur_theme]
    
    # 各层绘制
    for l in range(num_layers):
        if not layers_visible[l]:
            continue
        if l not in layers:
            continue
        alpha = 0.7 if l > 0 else 1.0
        for (x, y), tile in layers[l].items():
            if not (0 <= x < GRID_W and 0 <= y < GRID_H):
                continue
            info = TILES.get(tile, {})
            color = info.get("color", "#CCCCCC")
            px, py = x * CELL, y * CELL + 30
            canvas.create_rectangle(px+1, py+1, px+CELL-1, py+CELL-1,
                                    fill=color, outline="", stipple="gray50" if l > 0 else "")
    
    # 底层
    for y in range(GRID_H):
        for x in range(GRID_W):
            tile = grid[y][x]
            info = TILES.get(tile, {})
            color = info.get("color", "#CCCCCC")
            icon = info.get("icon", "")
            px, py = x * CELL, y * CELL + 30
            
            canvas.create_rectangle(px, py, px+CELL, py+CELL,
                                    fill=color, outline=t["grid"], width=1)
            if icon.strip():
                canvas.create_text(px+CELL//2, py+CELL//2, text=icon,
                                   font=("", 14))
    
    # 网格高亮（当前层）
    for y in range(GRID_H):
        for x in range(GRID_W):
            if get_tile_at(x, y, cur_layer) and cur_layer > 0:
                px, py = x * CELL, y * CELL + 30
                canvas.create_rectangle(px+1, py+1, px+CELL-1, py+CELL-1,
                                        outline=t["accent"], width=1, dash=(3,2))
    
    # 鼠标悬停高亮
    if hover_pos:
        hx, hy = hover_pos
        if 0 <= hx < GRID_W and 0 <= hy < GRID_H:
            px, py = hx * CELL, hy * CELL + 30
            canvas.create_rectangle(px+1, py+1, px+CELL-1, py+CELL-1,
                                    outline="#FFEB3B", width=2)
    
    # 统计
    update_stats()

def update_stats():
    """更新统计"""
    counts = {}
    for row in grid:
        for t in row:
            counts[t] = counts.get(t, 0) + 1
    for l in layers:
        for t in layers[l].values():
            counts[t] = counts.get(t, 0) + 1
    
    total = GRID_W * GRID_H
    walkable = sum(1 for row in grid for t in row if TILES.get(t,{}).get("walk",True))
    
    parts = []
    for t, c in sorted(counts.items(), key=lambda x: -x[1])[:5]:
        name = TILES.get(t, {}).get("name", t)
        parts.append(f"{name}:{c}")
    
    lbl_stats.config(text=f"📊 {' | '.join(parts)} | 可行走:{walkable}/{total}")

# ========================
# 日志
# ========================
log_lines = []

def log_msg(msg):
    global log_lines
    log_lines.append(msg)
    if len(log_lines) > 6:
        log_lines.pop(0)
    if lbl_log:
        lbl_log.config(state="normal")
        lbl_log.delete("1.0", "end")
        for line in log_lines:
            lbl_log.insert("end", line + "\n")
        lbl_log.config(state="disabled")

# ========================
# 鼠标/键盘
# ========================
hover_pos = None
is_painting = False
is_erasing = False

def on_motion(e):
    global hover_pos
    gx = e.x // CELL
    gy = (e.y - 30) // CELL
    hover_pos = (gx, gy)
    if 0 <= gx < GRID_W and 0 <= gy < GRID_H:
        tile = get_tile_at(gx, gy) or "empty"
        info = TILES.get(tile, {})
        lbl_cursor.config(text=f"📍 ({gx},{gy}) [{info.get('name','?')}]")
        
        if is_painting:
            set_tile_at(gx, gy, cur_tile)
        elif is_erasing:
            set_tile_at(gx, gy, "empty")
    draw()

def on_click(e):
    global is_painting, is_erasing
    gx = e.x // CELL
    gy = (e.y - 30) // CELL
    
    if not (0 <= gx < GRID_W and 0 <= gy < GRID_H):
        return
    
    if e.num == 1:  # 左键
        if cur_tile == "empty":
            is_erasing = True
            set_tile_at(gx, gy, "empty")
        else:
            is_painting = True
            set_tile_at(gx, gy, cur_tile)
    elif e.num == 3:  # 右键
        is_erasing = True
        set_tile_at(gx, gy, "empty")
    
    draw()

def on_release(e):
    global is_painting, is_erasing
    is_painting = False
    is_erasing = False

def on_key(e):
    global cur_layer
    if e.keysym == "z" and (e.state & 0x4):  # Ctrl+Z
        undo()
        draw()
    elif e.keysym == "y" and (e.state & 0x4):  # Ctrl+Y
        redo()
        draw()
    elif e.keysym == "Escape":
        global is_painting, is_erasing
        is_painting = False
        is_erasing = False
    elif e.keysym in ("1", "2", "3"):
        cur_layer = int(e.keysym) - 1
        update_layer_btns()
        draw()
    elif e.keysym == "Delete":
        # 删除悬停处
        if hover_pos:
            set_tile_at(hover_pos[0], hover_pos[1], "empty")
            draw()

# ========================
# 图层管理
# ========================
def set_layer(l):
    global cur_layer
    cur_layer = l
    update_layer_btns()
    draw()

def toggle_layer(l):
    layers_visible[l] = not layers_visible[l]
    update_layer_btns()
    draw()

def update_layer_btns():
    for i, btn in enumerate(layer_btns):
        if i == cur_layer:
            btn.config(relief="sunken", bd=3)
        else:
            btn.config(relief="raised", bd=1)
        # 可见性标记
        vis = "👁️" if layers_visible[i] else "🚫"
        btn.config(text=f"{vis} 层{i+1}")

# ========================
# 填色工具
# ========================
def flood_fill():
    if not hover_pos:
        log_msg("❌ 先将鼠标移到要填充的位置")
        return
    x, y = hover_pos
    fill_area(x, y, cur_tile)
    log_msg(f"🪣 已填充区域为 {TILES[cur_tile]['name']}")
    draw()

# ========================
# 换肤
# ========================
def apply_theme(name):
    global cur_theme
    cur_theme = name
    t = THEMES[name]
    
    root.config(bg=t["bg"])
    for w in all_widgets:
        try:
            w.config(bg=t["bg"], fg=t["fg"])
        except:
            pass
    
    for btn in theme_btns:
        btn.config(bg=t["btn"], fg="white")
    
    for btn in tile_btns:
        try:
            btn.config(bg=t["panel"])
        except:
            pass
    
    if panel_frame:
        panel_frame.config(bg=t["panel"])
        for child in panel_frame.winfo_children():
            try:
                child.config(bg=t["panel"], fg=t["fg"])
            except:
                pass
    
    draw()

# ========================
# 标题
# ========================
def update_title():
    mod = "*" if is_modified else ""
    root.title(f"🗺️ 二维地图编辑器 - {map_name}{mod} ({GRID_W}x{GRID_H})")

# ========================
# 构建界面
# ========================
def build_ui():
    global root, canvas, lbl_name, lbl_tile, lbl_layer, lbl_cursor
    global lbl_stats, lbl_log, panel_frame
    global theme_btns, tile_btns, layer_btns, all_widgets
    
    root = tk.Tk()
    root.title("🗺️ 二维地图编辑器")
    root.geometry(f"{WIDTH}x{HEIGHT}")
    root.resizable(False, False)
    
    # ====== 菜单栏 ======
    menubar = tk.Menu(root)
    root.config(menu=menubar)
    
    file_menu = tk.Menu(menubar, tearoff=0)
    menubar.add_cascade(label="📁 文件", menu=file_menu)
    file_menu.add_command(label="📄 新建", command=new_map)
    file_menu.add_command(label="📂 打开", command=load_map)
    file_menu.add_command(label="💾 保存", command=save_map)
    file_menu.add_separator()
    file_menu.add_command(label="🖼️ 导出PNG", command=export_png)
    file_menu.add_command(label="📄 导出文本", command=export_txt)
    file_menu.add_separator()
    file_menu.add_command(label="退出", command=root.quit)
    
    edit_menu = tk.Menu(menubar, tearoff=0)
    menubar.add_cascade(label="✏️ 编辑", menu=edit_menu)
    edit_menu.add_command(label="↩️ 撤销 (Ctrl+Z)", command=undo)
    edit_menu.add_command(label="↪️ 重做 (Ctrl+Y)", command=redo)
    edit_menu.add_separator()
    edit_menu.add_command(label="🗑️ 清空地图", command=clear_map)
    edit_menu.add_command(label="🎲 随机生成", command=random_map)
    
    # ====== 顶部工具栏 ======
    toolbar = tk.Frame(root)
    toolbar.pack(fill="x", pady=2)
    
    lbl_name = tk.Label(toolbar, text=f"📋 {map_name}", font=("Comic Sans MS", 11, "bold"))
    lbl_name.pack(side="left", padx=8)
    
    lbl_layer = tk.Label(toolbar, text="", font=("Comic Sans MS", 10))
    lbl_layer.pack(side="left", padx=5)
    
    lbl_tile = tk.Label(toolbar, text="🧱 当前: 空地", font=("Comic Sans MS", 10))
    lbl_tile.pack(side="left", padx=8)
    
    lbl_cursor = tk.Label(toolbar, text="📍 (-,-)", font=("Consolas", 9))
    lbl_cursor.pack(side="left", padx=8)
    
    lbl_stats = tk.Label(toolbar, text="", font=("Comic Sans MS", 9))
    lbl_stats.pack(side="left", padx=8)
    
    # 撤销/重做
    btn_undo = tk.Button(toolbar, text="↩️", font=("", 12), width=3, command=undo)
    btn_undo.pack(side="right", padx=2)
    btn_redo = tk.Button(toolbar, text="↪️", font=("", 12), width=3, command=redo)
    btn_redo.pack(side="right", padx=2)
    
    # ====== 主题栏 ======
    theme_bar = tk.Frame(root)
    theme_bar.pack(fill="x", pady=1)
    tk.Label(theme_bar, text="🎨 ", font=("", 8)).pack(side="left", padx=3)
    for name in THEMES:
        btn = tk.Button(theme_bar, text=name, font=("Comic Sans MS", 7, "bold"),
                         relief="raised", bd=1, padx=3,
                         command=lambda n=name: apply_theme(n))
        btn.pack(side="left", padx=1)
        theme_btns.append(btn)
    
    # ====== 主区域 ======
    main = tk.Frame(root)
    main.pack(fill="both", expand=True)
    
    # 左侧图块面板
    left_panel = tk.Frame(main, width=140)
    left_panel.pack(side="left", fill="y", padx=2)
    
    tk.Label(left_panel, text="🧱 图块", font=("Comic Sans MS", 10, "bold"),
             anchor="w").pack(fill="x", padx=3, pady=(3,0))
    
    # 图块按钮（2列）
    tile_grid = tk.Frame(left_panel)
    tile_grid.pack(fill="both", expand=True, padx=2, pady=2)
    
    for i, (key, info) in enumerate(TILES.items()):
        row = i // 2
        col = i % 2
        btn = tk.Button(tile_grid, text=f"{info['icon']} {info['name']}",
                         font=("Comic Sans MS", 7), anchor="w",
                         width=10, relief="raised", bd=1,
                         command=lambda k=key, n=info['name']: select_tile(k, n))
        btn.grid(row=row, column=col, padx=1, pady=1, sticky="ew")
        tile_btns.append(btn)
    
    # 填充工具
    btn_fill = tk.Button(left_panel, text="🪣 填充", font=("Comic Sans MS", 9, "bold"),
                          bg="#FF9800", fg="white", command=flood_fill)
    btn_fill.pack(fill="x", padx=3, pady=3)
    
    # 图层控制
    tk.Label(left_panel, text="📚 图层", font=("Comic Sans MS", 10, "bold"),
             anchor="w").pack(fill="x", padx=3, pady=(5,0))
    
    layer_frame = tk.Frame(left_panel)
    layer_frame.pack(fill="x", padx=2)
    
    for i in range(num_layers):
        btn = tk.Button(layer_frame, text=f"👁️ 层{i+1}",
                         font=("Comic Sans MS", 8),
                         command=lambda l=i: set_layer(l))
        btn.pack(fill="x", pady=1)
        layer_btns.append(btn)
    
    vis_frame = tk.Frame(left_panel)
    vis_frame.pack(fill="x", padx=2)
    for i in range(num_layers):
        btn = tk.Button(vis_frame, text=f"{'显示' if layers_visible[i] else '隐藏'}",
                         font=("Comic Sans MS", 7),
                         command=lambda l=i: toggle_layer(l))
        btn.pack(side="left", padx=1, pady=1, expand=True, fill="x")
    
    # ====== 画布 ======
    canvas_frame = tk.Frame(main)
    canvas_frame.pack(side="left", fill="both", expand=True, padx=2)
    
    canvas = tk.Canvas(canvas_frame, width=GRID_W*CELL, height=GRID_H*CELL+30,
                         highlightthickness=0)
    canvas.pack(padx=2, pady=2)
    
    canvas.bind("<Motion>", on_motion)
    canvas.bind("<Button-1>", on_click)
    canvas.bind("<Button-3>", on_click)
    canvas.bind("<ButtonRelease-1>", on_release)
    canvas.bind("<ButtonRelease-3>", on_release)
    
    # ====== 右侧面板 ======
    right_panel = tk.Frame(main, width=140)
    right_panel.pack(side="right", fill="y", padx=2)
    
    tk.Label(right_panel, text="🛠️ 工具", font=("Comic Sans MS", 10, "bold"),
             anchor="w").pack(fill="x", padx=3, pady=(3,0))
    
    btn_erase = tk.Button(right_panel, text="🧽 擦除模式", font=("Comic Sans MS", 9),
                           command=lambda: select_tile("empty", "空地(擦除)"))
    btn_erase.pack(fill="x", padx=3, pady=1)
    
    btn_rand = tk.Button(right_panel, text="🎲 随机地图", font=("Comic Sans MS", 9),
                          bg="#7B1FA2", fg="white", command=random_map)
    btn_rand.pack(fill="x", padx=3, pady=1)
    
    btn_clear = tk.Button(right_panel, text="🗑️ 清空", font=("Comic Sans MS", 9),
                           bg="#F44336", fg="white", command=clear_map)
    btn_clear.pack(fill="x", padx=3, pady=1)
    
    tk.Label(right_panel, text="💾 导出", font=("Comic Sans MS", 10, "bold"),
             anchor="w").pack(fill="x", padx=3, pady=(8,0))
    
    btn_exp_png = tk.Button(right_panel, text="🖼️ PNG图片", font=("Comic Sans MS", 8),
                              command=export_png)
    btn_exp_png.pack(fill="x", padx=3, pady=1)
    
    btn_exp_txt = tk.Button(right_panel, text="📄 文本地图", font=("Comic Sans MS", 8),
                              command=export_txt)
    btn_exp_txt.pack(fill="x", padx=3, pady=1)
    
    # 图例
    tk.Label(right_panel, text="📖 快捷键", font=("Comic Sans MS", 10, "bold"),
             anchor="w").pack(fill="x", padx=3, pady=(8,0))
    
    help_text = ("左键: 放置图块\n"
                 "右键: 擦除\n"
                 "Ctrl+Z: 撤销\n"
                 "Ctrl+Y: 重做\n"
                 "1/2/3: 切换图层\n"
                 "Esc: 取消操作\n"
                 "Del: 删除悬停处")
    tk.Label(right_panel, text=help_text, font=("Consolas", 7),
             justify="left", anchor="nw").pack(fill="x", padx=3, pady=1)
    
    # ====== 底部日志 ======
    log_frame = tk.Frame(root)
    log_frame.pack(fill="x", side="bottom", pady=1)
    
    log_scroll = tk.Scrollbar(log_frame)
    log_scroll.pack(side="right", fill="y")
    
    lbl_log = tk.Text(log_frame, font=("Consolas", 8), height=4,
                        wrap="word", yscrollcommand=log_scroll.set)
    lbl_log.pack(fill="both", expand=True, padx=3)
    log_scroll.config(command=lbl_log.yview)
    lbl_log.config(state="disabled")
    
    # 收集
    all_widgets.extend([toolbar, theme_bar, main, left_panel, tile_grid,
                        right_panel, log_frame, btn_undo, btn_redo,
                        btn_fill, btn_erase, btn_rand, btn_clear,
                        btn_exp_png, btn_exp_txt, lbl_name, lbl_tile,
                        lbl_layer, lbl_cursor, lbl_stats])
    
    # 键盘绑定
    root.bind("<Key>", on_key)
    root.bind("<Control-z>", lambda e: undo())
    root.bind("<Control-y>", lambda e: redo())
    root.focus_set()

# ========================
# 选择图块
# ========================
def select_tile(key, name):
    global cur_tile
    cur_tile = key
    info = TILES[key]
    lbl_tile.config(text=f"{info['icon']} 当前: {name}")
    for btn in tile_btns:
        try:
            btn.config(relief="raised", bd=1)
        except:
            pass
    log_msg(f"🧱 已选择: {name}")

# ========================
# 主循环
# ========================
def game_loop():
    draw()
    root.after(100, game_loop)

# ========================
# 启动
# ========================
def init():
    global grid
    grid = [["empty" for _ in range(GRID_W)] for _ in range(GRID_H)]
    build_ui()
    apply_theme("🗺️ 古典羊皮")
    update_layer_btns()
    update_title()
    log_msg("🗺️ 地图编辑器已就绪")
    log_msg("💡 点击左侧图块 → 在画布上绘制")
    draw()

if __name__ == "__main__":
    root = None
    init()
    game_loop()
    root.mainloop()
