# -*- coding: utf-8 -*-
"""
桌面图标布局管理器 - 希希手作版
功能：
1. 保存当前桌面图标布局
2. 一键恢复布局（刷新/重启后图标不乱跑）
3. 分区功能：把桌面划成多个区域，图标按区域摆放
使用：双击运行，或命令行 python desktop_icons.py
"""
import ctypes
import ctypes.wintypes
import json
import os
import sys
import time

# ============ Win32 API ============
user32 = ctypes.windll.user32
kernel32 = ctypes.windll.kernel32

# ListView 消息
LVM_FIRST = 0x1000
LVM_GETITEMCOUNT = LVM_FIRST + 4      # 0x1004
LVM_GETITEMPOSITION = LVM_FIRST + 16  # 0x1010
LVM_SETITEMPOSITION = LVM_FIRST + 15  # 0x100F
LVM_GETITEMTEXT = LVM_FIRST + 45      # 0x102D

# 窗口
PROGMAN = "Progman"
SHELLDLL_DEFVIEW = "SHELLDLL_DefView"
SYS_LISTVIEW = "SysListView32"

CONFIG_FILE = os.path.join(os.path.expanduser("~"), "desktop_icons_layout.json")

class POINT(ctypes.Structure):
    _fields_ = [("x", ctypes.c_long), ("y", ctypes.c_long)]

class LVITEM(ctypes.Structure):
    _fields_ = [
        ("mask", ctypes.c_uint),
        ("iItem", ctypes.c_int),
        ("iSubItem", ctypes.c_int),
        ("state", ctypes.c_uint),
        ("stateMask", ctypes.c_uint),
        ("pszText", ctypes.c_wchar_p),
        ("cchTextMax", ctypes.c_int),
        ("iImage", ctypes.c_int),
        ("lParam", ctypes.c_void_p),
        ("iIndent", ctypes.c_int),
        ("iGroupId", ctypes.c_int),
        ("cColumns", ctypes.c_uint),
        ("puColumns", ctypes.POINTER(ctypes.c_int)),
        ("piColFmt", ctypes.POINTER(ctypes.c_int)),
        ("iGroup", ctypes.c_int),
    ]


def find_desktop_listview():
    """找到桌面图标 ListView 控件句柄"""
    # 方法1：Progman -> SHELLDLL_DefView -> SysListView32
    progman = user32.FindWindowW(PROGMAN, None)
    if not progman:
        # 方法2：直接找 SHELLDLL_DefView
        progman = user32.FindWindowW(SHELLDLL_DEFVIEW, None)
    if not progman:
        return None
    defview = user32.FindWindowExW(progman, None, SHELLDLL_DEFVIEW, None)
    if not defview:
        # 有的系统上 Progman 的子窗口就是 SHELLDLL_DefView 本身
        defview = progman
    lv = user32.FindWindowExW(defview, None, SYS_LISTVIEW, None)
    return lv


def get_icon_count(lv):
    return user32.SendMessageW(lv, LVM_GETITEMCOUNT, 0, 0)


def get_icon_name(lv, index):
    """获取图标名称（用于识别）"""
    buf = ctypes.create_unicode_buffer(512)
    item = LVITEM()
    item.pszText = buf
    item.cchTextMax = 512
    user32.SendMessageW(lv, LVM_GETITEMTEXT, index, ctypes.byref(item))
    return buf.value


def get_icon_positions(lv, count):
    """获取所有图标位置，返回 [{name, x, y}]"""
    icons = []
    for i in range(count):
        pt = POINT()
        user32.SendMessageW(lv, LVM_GETITEMPOSITION, i, ctypes.byref(pt))
        name = get_icon_name(lv, i)
        icons.append({"name": name, "x": pt.x, "y": pt.y})
    return icons


def save_layout():
    """保存布局到 JSON 文件"""
    lv = find_desktop_listview()
    if not lv:
        print("[错误] 找不到桌面图标控件，请确保桌面图标已显示（桌面右键->查看->显示桌面图标）")
        return False
    count = get_icon_count(lv)
    if count == 0:
        print("[提示] 桌面没有图标，无需保存")
        return False
    icons = get_icon_positions(lv, count)
    with open(CONFIG_FILE, "w", encoding="utf-8") as f:
        json.dump({"time": time.strftime("%Y-%m-%d %H:%M:%S"), "icons": icons}, f, ensure_ascii=False, indent=2)
    print(f"[成功] 已保存 {count} 个图标的位置 -> {CONFIG_FILE}")
    return True


def restore_layout():
    """恢复布局"""
    if not os.path.exists(CONFIG_FILE):
        print("[错误] 没有找到保存的布局文件，请先运行'保存布局'")
        return False
    with open(CONFIG_FILE, "r", encoding="utf-8") as f:
        data = json.load(f)
    lv = find_desktop_listview()
    if not lv:
        print("[错误] 找不到桌面图标控件")
        return False
    count = get_icon_count(lv)
    saved = data.get("icons", [])
    restored = 0
    for i in range(count):
        name = get_icon_name(lv, i)
        for s in saved:
            if s["name"] == name:
                # 按名称匹配，设置位置
                user32.SendMessageW(lv, LVM_SETITEMPOSITION, i,
                                    (s["y"] << 16) | (s["x"] & 0xFFFF))
                restored += 1
                break
    # 刷新桌面
    user32.SendMessageW(lv, 0x0018, 0, 0)  # WM_SETREDRAW
    print(f"[成功] 已恢复 {restored} 个图标的位置（共保存 {len(saved)} 个）")
    return True


def make_zones(lv, count, cols=3, rows=2):
    """把桌面图标重新排成网格分区布局
    cols=列数, rows=行数
    从保存的布局文件读取图标，按顺序放入网格
    """
    # 获取桌面工作区大小
    rect = ctypes.wintypes.RECT()
    user32.SystemParametersInfoW(0x0030, 0, ctypes.byref(rect), 0)  # SPI_GETWORKAREA
    work_w = rect.right - rect.left
    work_h = rect.bottom - rect.top
    # 图标大小估算（大图标 48px + 间距，这里用 80px 网格）
    cell_w = max(80, work_w // cols)
    cell_h = max(100, work_h // rows)
    print(f"[提示] 分区: {cols}x{rows}，桌面 {work_w}x{work_h}，每格 {cell_w}x{cell_h}")
    for i in range(count):
        col = (i // rows) % cols
        row = i % rows
        x = col * cell_w + 10
        y = row * cell_h + 10
        user32.SendMessageW(lv, LVM_SETITEMPOSITION, i, (y << 16) | (x & 0xFFFF))
    user32.SendMessageW(lv, 0x0018, 0, 0)
    print(f"[成功] 已按 {cols}x{rows} 网格重排 {count} 个图标")


def print_menu():
    print("=" * 50)
    print("  桌面图标布局管理器 v1.0")
    print("=" * 50)
    print("  1. 保存当前布局")
    print("  2. 恢复布局（上次保存的）")
    print("  3. 自动网格分区（3列2行）")
    print("  4. 自动网格分区（4列3行）")
    print("  5. 自定义网格分区")
    print("  0. 退出")
    print("=" * 50)


def main():
    if len(sys.argv) > 1:
        cmd = sys.argv[1]
        if cmd in ("save", "1"):
            save_layout()
        elif cmd in ("restore", "2"):
            restore_layout()
        elif cmd in ("zone", "3"):
            lv = find_desktop_listview()
            if lv:
                make_zones(lv, get_icon_count(lv), 3, 2)
        elif cmd in ("zone4", "4"):
            lv = find_desktop_listview()
            if lv:
                make_zones(lv, get_icon_count(lv), 4, 3)
        return

    while True:
        print_menu()
        choice = input("请选择: ").strip()
        if choice == "1":
            save_layout()
        elif choice == "2":
            restore_layout()
        elif choice == "3":
            lv = find_desktop_listview()
            if lv:
                make_zones(lv, get_icon_count(lv), 3, 2)
        elif choice == "4":
            lv = find_desktop_listview()
            if lv:
                make_zones(lv, get_icon_count(lv), 4, 3)
        elif choice == "5":
            try:
                cols = int(input("列数(默认3): ") or 3)
                rows = int(input("行数(默认2): ") or 2)
                lv = find_desktop_listview()
                if lv:
                    make_zones(lv, get_icon_count(lv), cols, rows)
            except ValueError:
                print("[错误] 请输入数字")
        elif choice == "0":
            print("再见！")
            break
        else:
            print("[错误] 无效选择")
        print()


if __name__ == "__main__":
    main()
