# GESP等级：二级 | GESP Python 二级考点
"""
================================================================
  第三关: 保存作品 (★★★☆☆)
  学习目标:
    1. 实现画布保存到本地文件 (EPS 矢量格式)
    2. 加入「清空画布」「保存作品」「新建设置」完整流程
    3. 代码设计更健壮：错误处理、边界检查、用户确认
    4. 完整画图编辑器的最终形态
================================================================

【与第二关的区别】
  - 增加文件保存功能 (filesavebox + postscript)
  - 增加底部功能按钮区域（视觉提示）
  - 更完善的错误处理和用户反馈
  - 保存前确认、退出前确认等用户体验细节
"""

import turtle
import easygui
import os


# =============================================================
# 一、全局状态管理
# =============================================================

config = {
    "tool": "画笔",
    "color": "black",
    "size": 3,
}

# 常量：画布尺寸（所有坐标基于此）
CANVAS_WIDTH = 800
CANVAS_HEIGHT = 600
HALF_W = CANVAS_WIDTH // 2   # 400
HALF_H = CANVAS_HEIGHT // 2  # 300
STATUS_Y = HALF_H - 30       # 状态栏 Y 坐标
BTN_Y = -HALF_H + 30         # 底部按钮 Y 坐标


# =============================================================
# 二、画布与画笔
# =============================================================

def setup_screen():
    """初始化画布"""
    screen = turtle.Screen()
    screen.title("画图编辑器 - 第三关: 保存作品")
    screen.bgcolor("white")
    screen.setup(width=CANVAS_WIDTH, height=CANVAS_HEIGHT)
    screen.setworldcoordinates(-HALF_W, -HALF_H, HALF_W, HALF_H)
    screen.tracer(0)  # 关闭自动刷新，手动刷新减少闪烁
    return screen


def setup_pen():
    """创建主画笔"""
    p = turtle.Turtle()
    p.shape("circle")
    p.shapesize(0.4)
    p.speed(0)
    p.penup()
    return p


def update_pen_style(p):
    """同步画笔样式与 config"""
    p.color(config["color"])
    p.pensize(config["size"])


# =============================================================
# 三、画布 UI 绘制（底部按钮 + 状态栏）
# =============================================================

def draw_status_bar(p):
    """在画布顶部绘制当前状态信息"""
    p.penup()
    p.goto(0, STATUS_Y)
    p.color("#333333")
    p.write(
        f"工具: {config['tool']}  |  颜色: {config['color']}  |  "
        f"粗细: {config['size']}px",
        align="center",
        font=("Microsoft YaHei", 13, "bold"),
    )


def draw_bottom_buttons(p):
    """在画布底部绘制功能按钮视觉区域

    注意: 这些只是视觉提示。实际按钮由 EasyGUI 菜单实现。
    如果安装 tkinter-buttons 可以集成真正的可点击按钮，但
    为了保持简单，我们使用 EasyGUI 菜单 + 视觉提示的方式。
    """
    p.penup()
    p.goto(0, BTN_Y)
    p.color("#666666")
    p.write(
        "[C] 清空  |  [S] 保存  |  [ESC] 菜单  |  [Q] 退出",
        align="center",
        font=("Microsoft YaHei", 11, "normal"),
    )


def draw_divider_line(p):
    """绘制分隔线（划分绘图区和状态区）"""
    p.penup()
    p.goto(-HALF_W + 10, STATUS_Y - 20)
    p.pendown()
    p.color("#dddddd")
    p.pensize(1)
    p.goto(HALF_W - 10, STATUS_Y - 20)
    p.penup()

    # 底部分隔线
    p.goto(-HALF_W + 10, BTN_Y + 20)
    p.pendown()
    p.goto(HALF_W - 10, BTN_Y + 20)
    p.penup()

    update_pen_style(p)


def redraw_ui(p):
    """重绘整个界面（更新后调用）"""
    p.clear()
    draw_divider_line(p)
    draw_status_bar(p)
    draw_bottom_buttons(p)
    screen.update()


# =============================================================
# 四、EasyGUI 菜单函数
# =============================================================

def choose_tool():
    """工具选择"""
    tools = ["画笔", "圆形", "矩形", "星形", "三角形", "五边形"]
    choice = easygui.choicebox(
        "请选择绘图工具：", "🎨 选择工具", tools
    )
    if choice:
        config["tool"] = choice
        return True
    return False


def choose_color():
    """颜色选择"""
    colors = {
        "红色": "red",
        "蓝色": "blue",
        "绿色": "green",
        "橙色": "orange",
        "紫色": "purple",
        "黑色": "black",
        "粉色": "pink",
        "棕色": "brown",
        "灰色": "gray",
        "青色": "cyan",
    }
    choice = easygui.choicebox(
        "请选择画笔颜色：", "🎨 选择颜色", list(colors.keys())
    )
    if choice:
        config["color"] = colors[choice]
        update_pen_style(pen)
        return True
    return False


def choose_size():
    """粗细选择"""
    sizes = ["1 (极细)", "2 (细)", "3 (适中)", "5 (粗)",
             "8 (极粗)", "10 (特粗)", "15 (超粗)"]
    choice = easygui.choicebox(
        "请选择画笔粗细：", "✏️ 选择粗细", sizes
    )
    if choice:
        config["size"] = int(choice.split()[0])
        update_pen_style(pen)
        return True
    return False


# =============================================================
# 五、图形绘制函数
# =============================================================

def draw_pen_shape(p, x, y):
    """画笔模式：自由画线"""
    p.pendown()
    p.goto(x, y)
    p.penup()


def draw_circle_shape(p, x, y):
    """圆形"""
    p.penup()
    p.goto(x, y - 50)
    p.pendown()
    p.circle(50)
    p.penup()


def draw_rectangle_shape(p, x, y):
    """矩形"""
    w, h = 120, 80
    p.penup()
    p.goto(x - w // 2, y - h // 2)
    p.pendown()
    for _ in range(2):
        p.forward(w)
        p.left(90)
        p.forward(h)
        p.left(90)
    p.penup()


def draw_star_shape(p, x, y):
    """五角星"""
    p.penup()
    p.goto(x, y + 40)
    p.pendown()
    p.setheading(90 - 36)
    for _ in range(5):
        p.forward(80)
        p.right(144)
    p.penup()
    p.setheading(0)


def draw_triangle_shape(p, x, y):
    """三角形"""
    side = 80
    p.penup()
    p.goto(x - side // 2, y - side // 3)
    p.pendown()
    for _ in range(3):
        p.forward(side)
        p.left(120)
    p.penup()


def draw_pentagon_shape(p, x, y):
    """五边形"""
    side = 60
    p.penup()
    p.goto(x - side // 2, y - side // 2)
    p.pendown()
    for _ in range(5):
        p.forward(side)
        p.left(72)
    p.penup()


# 工具分发映射表
TOOL_FUNCTIONS = {
    "画笔": draw_pen_shape,
    "圆形": draw_circle_shape,
    "矩形": draw_rectangle_shape,
    "星形": draw_star_shape,
    "三角形": draw_triangle_shape,
    "五边形": draw_pentagon_shape,
}

# =============================================================
# 六、鼠标事件处理
# =============================================================

def on_click_handler(x, y):
    """鼠标点击：根据当前工具绘图"""
    # 忽略状态栏/按钮区域的点击（避免误操作）
    if y > STATUS_Y - 20 or y < BTN_Y + 20:
        return

    pen.penup()
    pen.goto(x, y)

    draw_func = TOOL_FUNCTIONS.get(config["tool"])
    if draw_func:
        draw_func(pen, x, y)
        screen.update()


# =============================================================
# 七、核心功能: 保存 / 清空 / 退出
# =============================================================

def save_drawing():
    """
    将当前画布保存为 EPS 矢量图文件。

    使用 turtle 的 getcanvas().postscript() 方法输出为
    Encapsulated PostScript 格式，可在大多数图片查看器
    和矢量编辑软件中打开。

    若系统安装了 PIL (Pillow)，可进一步转换为 PNG 格式，
    这里提供可选的转换代码（注释状态）。
    """
    # 弹出保存文件对话框，默认文件名
    file_path = easygui.filesavebox(
        msg="请选择保存位置和文件名",
        title="💾 保存作品",
        default="我的画作.eps",
        filetypes=["*.eps"],
    )

    if file_path is None:
        return  # 用户取消

    try:
        # ---- 核心保存逻辑 ----
        # 获取 turtle 底层 tkinter Canvas 对象，输出 PostScript
        canvas = screen.getcanvas()
        canvas.postscript(file=file_path)

        # ---- 可选: 转换为 PNG (需安装 Pillow) ----
        # if file_path.lower().endswith('.png'):
        #     from PIL import Image
        #     img = Image.open(file_path.replace('.png', '.eps'))
        #     img.save(file_path, 'png')
        #     # 删除临时 eps 文件
        #     os.remove(file_path.replace('.png', '.eps'))

        easygui.msgbox(
            f"作品已成功保存！\n\n"
            f"路径: {file_path}\n\n"
            f"提示: EPS 文件可用 Photoshop、Illustrator、\n"
            f"Inkscape 等软件打开，也可直接拖入浏览器查看。",
            "✅ 保存成功",
        )

    except Exception as e:
        easygui.msgbox(
            f"保存失败: {str(e)}\n\n"
            f"可能原因:\n"
            f"1. 文件路径包含非法字符\n"
            f"2. 磁盘空间不足\n"
            f"3. 文件被其他程序占用",
            "❌ 保存失败",
        )


def clear_canvas():
    """清空画布（需确认，防止误操作）"""
    if easygui.ccbox("确定要清空画布吗？\n所有绘制内容将被删除。", "⚠️ 确认清空"):
        pen.clear()
        pen.penup()
        pen.home()
        update_pen_style(pen)
        redraw_ui(pen)
        easygui.msgbox("画布已清空，可以重新开始画图了！", "✅ 已清空")


def confirm_exit():
    """退出前确认"""
    if easygui.ccbox("确定要退出画图编辑器吗？\n\n未保存的作品将会丢失！", "⚠️ 确认退出"):
        turtle.bye()
        return True
    return False


# =============================================================
# 八、新建设置
# =============================================================

def new_settings():
    """重新选择工具、颜色和粗细（重置配置）"""
    if easygui.ccbox("重新设置会保留当前画布内容，仅改变绘图设置。\n要继续吗？", "🔧 新建设置"):
        choose_tool()
        choose_color()
        choose_size()
        update_pen_style(pen)
        redraw_ui(pen)
        easygui.msgbox(
            f"设置已更新！\n工具: {config['tool']}\n颜色: {config['color']}\n粗细: {config['size']}",
            "✅ 设置完成",
        )


# =============================================================
# 九、主菜单
# =============================================================

def show_main_menu():
    """弹出主菜单供用户选择"""
    choice = easygui.choicebox(
        f"当前: 工具[{config['tool']}] 颜色[{config['color']}] 粗细[{config['size']}]\n\n"
        "请选择操作：",
        "🎨 画图编辑器 - 主菜单",
        [
            "🎯 开始 / 继续画图",
            "🔧 切换工具",
            "🌈 切换颜色",
            "📏 调整粗细",
            "🆕 新建设置",
            "🗑️ 清空画布",
            "💾 保存作品",
            "🚪 退出程序",
        ],
    )
    return choice


# =============================================================
# 十、主循环
# =============================================================

def main_loop():
    """程序主流程循环：菜单驱动，持续交互"""
    while True:
        choice = show_main_menu()

        if choice is None or choice == "🚪 退出程序":
            if confirm_exit():
                break

        elif choice == "🎯 开始 / 继续画图":
            easygui.msgbox(
                "请在画布上点击鼠标绘图！\n"
                "按 ESC 键随时返回主菜单。\n"
                "按 S 键快速保存 | C 键清空 | Q 键退出",
                "✅ 开始画图",
            )

        elif choice == "🔧 切换工具":
            choose_tool()
            redraw_ui(pen)

        elif choice == "🌈 切换颜色":
            choose_color()
            redraw_ui(pen)

        elif choice == "📏 调整粗细":
            choose_size()
            redraw_ui(pen)

        elif choice == "🆕 新建设置":
            new_settings()

        elif choice == "🗑️ 清空画布":
            clear_canvas()

        elif choice == "💾 保存作品":
            save_drawing()


# =============================================================
# 十一、键盘快捷键
# =============================================================

def on_esc():
    """ESC: 打开主菜单"""
    main_loop()


def on_key_s():
    """S: 快速保存"""
    # 在底部按钮区域显示保存中提示
    pen.penup()
    pen.goto(0, BTN_Y - 25)
    pen.color("#009900")
    pen.write("💾 保存中...", align="center",
              font=("Microsoft YaHei", 10, "normal"))
    screen.update()
    import time
    time.sleep(0.1)  # 让用户看到提示

    save_drawing()
    redraw_ui(pen)


def on_key_c():
    """C: 快速清空"""
    clear_canvas()


def on_key_q():
    """Q: 快速退出"""
    confirm_exit()


# =============================================================
# 十二、欢迎界面
# =============================================================

def show_welcome(p):
    """显示启动欢迎信息"""
    p.penup()
    p.goto(0, 80)
    p.color("#555555")
    p.write("🎨 画图编辑器", align="center",
            font=("Microsoft YaHei", 24, "bold"))
    p.goto(0, 40)
    p.write("点击画布绘图  |  按 ESC 打开菜单  |  按 S 保存",
            align="center", font=("Microsoft YaHei", 12, "normal"))
    screen.update()


# =============================================================
# 十三、程序入口
# =============================================================

def main():
    global pen, screen

    # ---- 启动欢迎 ----
    easygui.msgbox(
        "🎨 欢迎使用画图编辑器 v3.0！\n\n"
        "本程序支持:\n"
        "  • 6 种绘图工具 (画笔/圆形/矩形/星形/三角形/五边形)\n"
        "  • 10 种颜色选择\n"
        "  • 可调画笔粗细 (1~15px)\n"
        "  • 保存作品为 EPS 矢量文件\n"
        "  • 快捷键操作\n\n"
        "点击「确定」开始创作！",
        "🎨 画图编辑器 - 第三关",
    )

    # ---- 初始化 ----
    screen = setup_screen()
    pen = setup_pen()

    # ---- 用户初始设置 ----
    choose_tool()
    choose_color()
    choose_size()
    update_pen_style(pen)

    # ---- 绘制界面 ----
    update_pen_style(pen)
    redraw_ui(pen)
    show_welcome(pen)

    # ---- 绑定事件 ----
    screen.onclick(on_click_handler)     # 鼠标点击
    screen.onkey(on_esc, "Escape")       # ESC -> 菜单
    screen.onkey(on_key_s, "s")          # S -> 保存
    screen.onkey(on_key_c, "c")          # C -> 清空
    screen.onkey(on_key_q, "q")          # Q -> 退出
    screen.listen()

    # ---- 进入主菜单（用户可以决定下一步） ----
    main_loop()

    # ---- 启动 turtle 事件循环（实际已接管） ----
    screen.mainloop()


if __name__ == "__main__":
    main()
