# GESP等级：四级 | GESP Python 四级考点
"""
================================================================================
 挑战2 ★★ — 用 dict + set 管理画笔配置
================================================================================

  场景：
    一个绘图程序有多个画笔（pen），每支画笔都有：
      - 颜色、粗细、透明度、笔刷形状等属性
    需要：
      1. 用 dict 管理每支画笔的「配置项 → 值」
      2. 用 set 记录「当前生效的画笔组」
      3. 支持配置验证、批量更新、配置快照

  知识点：
    - dict 作为配置容器（嵌套 dict）
    - set 记录活跃/选中状态
    - 配置校验（类型检查、值域检查）
    - dict.copy() 做配置快照
    - 集合运算做批量切换
================================================================================
"""
from pprint import pprint

print("=" * 60)
print("挑战2 ★★ — 画笔配置管理")
print("=" * 60)

# ========== 第1步：定义配置模板（含默认值与类型约束）==========
CONFIG_TEMPLATE = {
    "color":      {"default": "black",  "type": str,   "valid": None},          # 不限值
    "width":      {"default": 2,        "type": int,   "valid": range(1, 21)},  # 1~20
    "opacity":    {"default": 1.0,      "type": float, "valid": lambda v: 0.0 <= v <= 1.0},
    "shape":      {"default": "round",  "type": str,   "valid": {"round", "square", "diamond"}},
    "is_active":  {"default": False,    "type": bool,  "valid": None},
}

print("\n1. 配置模板定义完成")
pprint(CONFIG_TEMPLATE)


# ========== 第2步：创建画笔 ==========
def create_pen(name: str, **overrides) -> dict:
    """基于模板创建画笔，overrides 可覆盖默认值"""
    pen = {}
    for key, meta in CONFIG_TEMPLATE.items():
        if key in overrides:
            val = overrides[key]
            # 类型检查
            if not isinstance(val, meta["type"]):
                raise TypeError(f"'{key}' 期望 {meta['type'].__name__}，收到 {type(val).__name__}")
            # 值域检查
            if meta["valid"] is not None:
                if callable(meta["valid"]):
                    if not meta["valid"](val):
                        raise ValueError(f"'{key}' 值 {val} 不满足校验函数")
                elif val not in meta["valid"]:
                    raise ValueError(f"'{key}' 值 {val} 不在有效集合 {meta['valid']} 中")
            pen[key] = val
        else:
            pen[key] = meta["default"]
    pen["_name"] = name
    pen["_tags"] = set()  # 每个画笔可以打标签（set 记录标签）
    return pen


# 创建几支画笔
pen1 = create_pen("铅笔", color="blue", width=3, opacity=0.9, shape="round")
pen2 = create_pen("马克笔", color="red", width=8, opacity=0.8, shape="square")
pen3 = create_pen("高光笔", color="yellow", width=5, opacity=0.4, shape="diamond")
pen4 = create_pen("钢笔", color="black", width=1, opacity=1.0, shape="round")

pens = {"pen1": pen1, "pen2": pen2, "pen3": pen3, "pen4": pen4}

print("\n2. 已创建画笔：")
for pname, pen in pens.items():
    print(f"   {pname}: {pen['_name']:4s}  颜色={pen['color']:6s} 粗细={pen['width']:2d} "
          f"透明度={pen['opacity']:.1f} 形状={pen['shape']:7s}")


# ========== 第3步：用 set 管理「活跃画笔」==========
active_brushes = set()  # 存储画笔标识

# 模拟激活操作
active_brushes.add("pen1")
active_brushes.add("pen2")
print(f"\n3. 当前活跃画笔组：{active_brushes}")

# 激活 pen3 的同时自动取消 pen1（互斥模式）
# 用集合运算实现：只保留 pen2，加 pen3
MUTEX_GROUP = {"pen1", "pen2", "pen3"}  # 这组画笔互斥
pen_to_activate = "pen3"

# 保留不在互斥组中的画笔 + 新激活的画笔
active_brushes = (active_brushes - MUTEX_GROUP) | {pen_to_activate}
print(f"   激活 '{pen_to_activate}'（互斥模式）→ 活跃组：{active_brushes}")


# ========== 第4步：批量配置更新 ==========
def batch_update(pens: dict, pen_names: set, **updates) -> None:
    """批量更新指定画笔组的配置"""
    for name in pen_names:
        if name in pens:
            for key, val in updates.items():
                if key in CONFIG_TEMPLATE:
                    pens[name][key] = val
            print(f"   已更新 '{pens[name]['_name']}' → {updates}")
        else:
            print(f"   警告：画笔 '{name}' 不存在，跳过")


print("\n4. 批量更新活跃画笔：")
batch_update(pens, active_brushes, width=10, opacity=0.5)
for name in active_brushes:
    p = pens[name]
    print(f"   {name} 新配置：粗细={p['width']} 透明度={p['opacity']}")


# ========== 第5步：配置快照与恢复 ==========
print("\n5. 配置快照：")
snapshot = {name: dict(pens[name]) for name in pens}  # 深拷贝快照
print("   已保存所有画笔配置快照")

# 修改一些值
pens["pen1"]["color"] = "green"
pens["pen1"]["width"] = 20
print(f"   修改后 pen1：颜色={pens['pen1']['color']} 粗细={pens['pen1']['width']}")

# 从快照恢复
for name in pens:
    pens[name].update(snapshot[name])
print(f"   恢复后 pen1：颜色={pens['pen1']['color']} 粗细={pens['pen1']['width']}")


# ========== 第6步：按标签分组（set 作为画笔分类工具）==========
print("\n6. 画笔标签分组：")
pens["pen1"]["_tags"] |= {"细笔", "常用"}
pens["pen2"]["_tags"] |= {"粗笔", "常用"}
pens["pen3"]["_tags"] |= {"特效"}
pens["pen4"]["_tags"] |= {"细笔", "标准"}

# 找出所有带 "常用" 标签的画笔
tag = "常用"
common_pens = {name for name, p in pens.items() if tag in p["_tags"]}
print(f"   标签「{tag}」的画笔：{common_pens}")

# 找出同时有 "细笔" 和 "常用" 标签的画笔（集合交集）
both_tags = {name for name, p in pens.items()
             if {"细笔", "常用"}.issubset(p["_tags"])}
print(f"   「细笔」且「常用」的画笔：{both_tags}")


# ========== 总结 ==========
print()
print(">>> 挑战2 关键收获 <<<")
print("1. dict 天然适合做配置容器，支持类型+值域校验")
print("2. set 高效管理「活跃/选中」状态集合")
print("3. 集合运算（-、|、&）优雅实现批量状态切换")
print("4. dict 快照是配置回滚的简单方案")
print("5. set 作为标签系统，支持交集/并集查询")
