"""Desktop-Icons langsam hin und her schütteln (Windows Explorer)."""

from __future__ import annotations

import ctypes
import math
import random
from ctypes import wintypes

user32 = ctypes.windll.user32

LVM_FIRST = 0x1000
LVM_GETITEMCOUNT = LVM_FIRST + 4
LVM_GETITEMPOSITION = LVM_FIRST + 16
LVM_SETITEMPOSITION32 = LVM_FIRST + 43

LVM_GETITEMPOSITION32 = LVM_FIRST + 59


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


def _find_desktop_listview() -> int | None:
    """SysListView32 des Explorer-Desktops finden."""
    progman = user32.FindWindowW("Progman", None)
    if not progman:
        return None

    # WorkerW-Hintergrund (Windows 10/11)
    workerw = wintypes.HWND(0)

    @ctypes.WINFUNCTYPE(wintypes.BOOL, wintypes.HWND, wintypes.LPARAM)
    def enum_windows(hwnd, _lparam):
        nonlocal workerw
        if user32.FindWindowExW(hwnd, 0, "SHELLDLL_DefView", None):
            workerw = hwnd
        return True

    user32.EnumWindows(enum_windows, 0)

    shell = user32.FindWindowExW(workerw or progman, 0, "SHELLDLL_DefView", None)
    if not shell:
        shell = user32.FindWindowExW(progman, 0, "SHELLDLL_DefView", None)
    if not shell:
        return None

    listview = user32.FindWindowExW(shell, 0, "SysListView32", None)
    return int(listview) if listview else None


def _icon_count(hwnd: int) -> int:
    return int(user32.SendMessageW(hwnd, LVM_GETITEMCOUNT, 0, 0))


def _get_icon_pos(hwnd: int, index: int) -> tuple[int, int] | None:
    pt = POINT()
    ok = user32.SendMessageW(hwnd, LVM_GETITEMPOSITION32, index, ctypes.byref(pt))
    if not ok:
        ok = user32.SendMessageW(hwnd, LVM_GETITEMPOSITION, index, ctypes.byref(pt))
    if ok:
        return pt.x, pt.y
    return None


def _set_icon_pos(hwnd: int, index: int, x: int, y: int) -> None:
    pt = POINT(int(x), int(y))
    user32.SendMessageW(hwnd, LVM_SETITEMPOSITION32, index, ctypes.byref(pt))


def _client_to_screen(hwnd: int, x: int, y: int) -> tuple[int, int]:
    pt = POINT(x, y)
    user32.ClientToScreen(hwnd, ctypes.byref(pt))
    return pt.x, pt.y


def pick_icons(hwnd: int, count: int = 2) -> list[tuple[int, int, int]]:
    """Zufällige Icons: (index, x, y) in Desktop-Koordinaten."""
    total = _icon_count(hwnd)
    if total <= 0:
        return []
    n = min(count, total, 4)
    indices = random.sample(range(total), k=n)
    out: list[tuple[int, int, int]] = []
    for i in indices:
        pos = _get_icon_pos(hwnd, i)
        if pos:
            out.append((i, pos[0], pos[1]))
    return out


def screen_center_of_icons(hwnd: int, icons: list[tuple[int, int, int]]) -> tuple[int, int] | None:
    if not icons:
        return None
    sx, sy = 0, 0
    for _, x, y in icons:
        cx, cy = _client_to_screen(hwnd, x, y)
        sx += cx
        sy += cy
    return sx // len(icons), sy // len(icons)


class IconShakeSession:
    """Langsames Hin-und-Her-Schütteln mit Wiederherstellung."""

    def __init__(
        self,
        hwnd: int,
        icons: list[tuple[int, int, int]],
        *,
        amplitude: int = 12,
        cycles: int = 5,
        steps_per_half: int = 8,
    ) -> None:
        self.hwnd = hwnd
        self.icons = icons
        self.amplitude = amplitude
        self.total_steps = cycles * steps_per_half * 2
        self.step = 0
        self._orig = {idx: (x, y) for idx, x, y in icons}

    @property
    def done(self) -> bool:
        return self.step >= self.total_steps

    def tick(self) -> None:
        if self.done:
            return
        t = self.step / max(1, self.total_steps)
        offset = int(math.sin(t * math.pi * 2 * 3) * self.amplitude)
        for idx, ox, oy in self.icons:
            _set_icon_pos(self.hwnd, idx, ox + offset, oy)
        self.step += 1

    def restore(self) -> None:
        for idx, (ox, oy) in self._orig.items():
            _set_icon_pos(self.hwnd, idx, ox, oy)


def start_session(icon_count: int = 2) -> IconShakeSession | None:
    hwnd = _find_desktop_listview()
    if not hwnd:
        return None
    icons = pick_icons(hwnd, icon_count)
    if not icons:
        return None
    return IconShakeSession(
        hwnd,
        icons,
        amplitude=random.randint(10, 16),
        cycles=random.randint(4, 6),
        steps_per_half=random.randint(6, 10),
    )


def jam_position_near_icons(session: IconShakeSession) -> tuple[int, int] | None:
    center = screen_center_of_icons(session.hwnd, session.icons)
    if not center:
        return None
    return center[0] + 40, center[1] - 60
