import ctypes
from ctypes import wintypes
import tkinter as tk
from tkinter import ttk, messagebox

user32 = ctypes.windll.user32

EnumWindows = user32.EnumWindows
EnumWindowsProc = ctypes.WINFUNCTYPE(wintypes.BOOL, wintypes.HWND, wintypes.LPARAM)

IsWindowVisible = user32.IsWindowVisible
GetWindowTextLengthW = user32.GetWindowTextLengthW
GetWindowTextW = user32.GetWindowTextW
GetWindowRect = user32.GetWindowRect
GetClassNameW = user32.GetClassNameW
ShowWindow = user32.ShowWindow
SetWindowPos = user32.SetWindowPos
IsIconic = user32.IsIconic
IsZoomed = user32.IsZoomed

SW_RESTORE = 9
SWP_NOZORDER = 0x0004
SWP_NOACTIVATE = 0x0010

EXCLUDE_CLASSES = {
    "Progman",
    "WorkerW",
    "Shell_TrayWnd",
    "Button",
}


class RECT(ctypes.Structure):
    _fields_ = [
        ("left", wintypes.LONG),
        ("top", wintypes.LONG),
        ("right", wintypes.LONG),
        ("bottom", wintypes.LONG),
    ]


def get_window_title(hwnd):
    length = GetWindowTextLengthW(hwnd)
    if length <= 0:
        return ""

    buffer = ctypes.create_unicode_buffer(length + 1)
    GetWindowTextW(hwnd, buffer, length + 1)
    return buffer.value.strip()


def get_window_class(hwnd):
    buffer = ctypes.create_unicode_buffer(256)
    GetClassNameW(hwnd, buffer, 256)
    return buffer.value.strip()


def get_window_rect(hwnd):
    rect = RECT()
    if not GetWindowRect(hwnd, ctypes.byref(rect)):
        return None

    x = rect.left
    y = rect.top
    w = rect.right - rect.left
    h = rect.bottom - rect.top
    return x, y, w, h


def enum_windows():
    windows = []

    def callback(hwnd, lparam):
        if not IsWindowVisible(hwnd):
            return True

        title = get_window_title(hwnd)
        if not title:
            return True

        class_name = get_window_class(hwnd)
        if class_name in EXCLUDE_CLASSES:
            return True

        rect = get_window_rect(hwnd)
        if not rect:
            return True

        x, y, w, h = rect

        if w <= 0 or h <= 0:
            return True

        if IsIconic(hwnd):
            state = "minimized"
        elif IsZoomed(hwnd):
            state = "maximized"
        else:
            state = "normal"

        windows.append({
            "hwnd": hwnd,
            "title": title,
            "class_name": class_name,
            "x": x,
            "y": y,
            "w": w,
            "h": h,
            "state": state,
        })

        return True

    EnumWindows(EnumWindowsProc(callback), 0)
    return windows


def move_window_home(hwnd):
    rect = get_window_rect(hwnd)
    if not rect:
        return False

    x, y, w, h = rect

    # 최소화/최대화 창은 복원 후 이동
    if IsIconic(hwnd) or IsZoomed(hwnd):
        ShowWindow(hwnd, SW_RESTORE)

        rect = get_window_rect(hwnd)
        if rect:
            x, y, w, h = rect

    result = SetWindowPos(
        hwnd,
        None,
        0,
        0,
        w,
        h,
        SWP_NOZORDER | SWP_NOACTIVATE,
    )

    return bool(result)


class WindowMoverApp:
    def __init__(self, root):
        self.root = root
        self.root.title("Window Home Mover")
        self.root.geometry("1000x600")

        self.windows = {}

        top = ttk.Frame(root)
        top.pack(fill="x", padx=10, pady=8)

        ttk.Button(top, text="새로고침", command=self.refresh).pack(side="left", padx=4)
        ttk.Button(top, text="선택 창 홈으로 이동", command=self.move_selected).pack(side="left", padx=4)
        ttk.Button(top, text="목록 전체 홈으로 이동", command=self.move_all).pack(side="left", padx=4)

        self.status_var = tk.StringVar(value="대기")
        ttk.Label(top, textvariable=self.status_var).pack(side="right", padx=4)

        columns = ("no", "hwnd", "title", "x", "y", "w", "h", "state", "class")
        self.tree = ttk.Treeview(root, columns=columns, show="headings", selectmode="extended")

        self.tree.heading("no", text="번호")
        self.tree.heading("hwnd", text="HWND")
        self.tree.heading("title", text="창 제목")
        self.tree.heading("x", text="X")
        self.tree.heading("y", text="Y")
        self.tree.heading("w", text="W")
        self.tree.heading("h", text="H")
        self.tree.heading("state", text="상태")
        self.tree.heading("class", text="Class")

        self.tree.column("no", width=50, anchor="center")
        self.tree.column("hwnd", width=110, anchor="center")
        self.tree.column("title", width=360)
        self.tree.column("x", width=70, anchor="center")
        self.tree.column("y", width=70, anchor="center")
        self.tree.column("w", width=70, anchor="center")
        self.tree.column("h", width=70, anchor="center")
        self.tree.column("state", width=100, anchor="center")
        self.tree.column("class", width=130)

        self.tree.pack(fill="both", expand=True, padx=10, pady=8)

        self.refresh()

    def refresh(self):
        for item in self.tree.get_children():
            self.tree.delete(item)

        self.windows.clear()

        windows = enum_windows()

        for idx, win in enumerate(windows, start=1):
            hwnd = win["hwnd"]
            item_id = str(hwnd)
            self.windows[item_id] = win

            self.tree.insert(
                "",
                "end",
                iid=item_id,
                values=(
                    idx,
                    hwnd,
                    win["title"],
                    win["x"],
                    win["y"],
                    win["w"],
                    win["h"],
                    win["state"],
                    win["class_name"],
                ),
            )

        self.status_var.set(f"창 {len(windows)}개 표시됨")

    def move_selected(self):
        selected = self.tree.selection()

        if not selected:
            messagebox.showwarning("선택 없음", "이동할 창을 선택하세요.")
            return

        ok = 0
        fail = 0

        for item_id in selected:
            hwnd = int(item_id)
            if move_window_home(hwnd):
                ok += 1
            else:
                fail += 1

        self.refresh()
        self.status_var.set(f"선택 이동 완료: 성공 {ok}, 실패 {fail}")

    def move_all(self):
        items = self.tree.get_children()

        if not items:
            return

        if not messagebox.askyesno("전체 이동", "목록에 표시된 모든 창을 0,0으로 이동할까요?"):
            return

        ok = 0
        fail = 0

        for item_id in items:
            hwnd = int(item_id)
            if move_window_home(hwnd):
                ok += 1
            else:
                fail += 1

        self.refresh()
        self.status_var.set(f"전체 이동 완료: 성공 {ok}, 실패 {fail}")


if __name__ == "__main__":
    root = tk.Tk()
    app = WindowMoverApp(root)
    root.mainloop()