"""
MC AutoClicker - McSkill Edition
pip install pywin32 keyboard pyinstaller
"""

import tkinter as tk
import threading
import time

try:
    import win32gui, win32con, win32api
    WIN32_OK = True
except ImportError:
    WIN32_OK = False

try:
    import keyboard
    KB_OK = True
except ImportError:
    KB_OK = False

LBDOWN = 0x0201
LBUP   = 0x0202
MK_LB  = 0x0001

# ── Win32 ─────────────────────────────────────────────────────────────────────

def find_hwnd(nickname):
    if not WIN32_OK or not nickname.strip():
        return None
    result = []
    nick_l = nickname.strip().lower()
    def cb(hwnd, _):
        if nick_l in win32gui.GetWindowText(hwnd).lower():
            result.append(hwnd)
        return True
    win32gui.EnumWindows(cb, None)
    return result[0] if result else None

def get_render_hwnd(hwnd):
    best = hwnd
    def cb(child, _):
        nonlocal best
        try:
            r = win32gui.GetClientRect(child)
            if r[2] > 0 and r[3] > 0:
                best = child
        except Exception:
            pass
        return True
    try:
        win32gui.EnumChildWindows(hwnd, cb, None)
    except Exception:
        pass
    return best

def make_lparam(hwnd):
    try:
        r = win32gui.GetClientRect(hwnd)
        cx = (r[2] - r[0]) // 2
        cy = (r[3] - r[1]) // 2
    except Exception:
        cx, cy = 100, 100
    return (cy << 16) | (cx & 0xFFFF)

def send_click(hwnd):
    target = get_render_hwnd(hwnd)
    lp = make_lparam(target)
    win32api.PostMessage(target, LBDOWN, MK_LB, lp)
    time.sleep(0.013)
    win32api.PostMessage(target, LBUP, 0, lp)

def send_down(hwnd):
    target = get_render_hwnd(hwnd)
    lp = make_lparam(target)
    win32api.PostMessage(target, LBDOWN, MK_LB, lp)

def send_up(hwnd):
    target = get_render_hwnd(hwnd)
    lp = make_lparam(target)
    win32api.PostMessage(target, LBUP, 0, lp)


# ── App ───────────────────────────────────────────────────────────────────────

class App(tk.Tk):
    C = {
        "bg0":      "#07070b",
        "bg1":      "#0f0f17",
        "bg2":      "#171723",
        "bg3":      "#1e1e2e",
        "border":   "#1f1f32",
        "border2":  "#2e2e4a",
        "green":    "#00ff87",
        "green2":   "#00cc6a",
        "greendim": "#042b1a",
        "greenglow":"#00ff8733",
        "red":      "#ff2d55",
        "reddim":   "#2b0410",
        "yellow":   "#ffcc00",
        "yellowdim":"#2b2100",
        "cyan":     "#00d4ff",
        "cyandim":  "#002b38",
        "fg":       "#e2e8f8",
        "fgdim":    "#444466",
        "fgmid":    "#7878aa",
        "purple":   "#7c5cfc",
    }

    def __init__(self):
        super().__init__()
        self.title("MC AutoClicker")
        self.configure(bg=self.C["bg0"])
        self.resizable(False, False)

        self.running = False
        self.hwnd    = None
        self._thread = None

        self.v_nick     = tk.StringVar(value="")
        self.v_mode     = tk.StringVar(value="spam")
        self.v_cps      = tk.DoubleVar(value=12.0)
        self.v_status   = tk.StringVar(value="ОСТАНОВЛЕН")
        self.v_window   = tk.StringVar(value="--")
        self.v_hk_start = tk.StringVar(value="F6")
        self.v_hk_stop  = tk.StringVar(value="F7")
        self.v_topmost  = tk.BooleanVar(value=False)

        self._build()
        self._register_hotkeys()
        self._poll_window()

    # ── UI ────────────────────────────────────────────────────────────────────

    def _build(self):
        C = self.C

        # Градиентная шапка (эмулируем через два фрейма)
        tk.Frame(self, bg=C["green"], height=2).pack(fill="x")
        tk.Frame(self, bg=C["purple"], height=1).pack(fill="x")

        hdr = tk.Frame(self, bg=C["bg1"])
        hdr.pack(fill="x")

        # Декоративная полоса под шапкой
        tk.Frame(hdr, bg=C["bg0"], height=1).pack(fill="x", side="bottom")

        hi = tk.Frame(hdr, bg=C["bg1"])
        hi.pack(fill="x", padx=18, pady=14)

        # Иконка + заголовок
        icon_frame = tk.Frame(hi, bg=C["bg1"])
        icon_frame.pack(side="left")

        tk.Label(icon_frame, text="⚡", font=("Segoe UI Emoji", 20),
                 bg=C["bg1"], fg=C["green"]).pack(side="left")

        title_frame = tk.Frame(icon_frame, bg=C["bg1"])
        title_frame.pack(side="left", padx=(6, 0))

        tk.Label(title_frame, text="MC AUTOCLICKER",
                 font=("Consolas", 16, "bold"),
                 bg=C["bg1"], fg=C["fg"]).pack(anchor="w")

        tk.Label(title_frame, text="McSkill Edition  //  background clicks",
                 font=("Consolas", 8),
                 bg=C["bg1"], fg=C["fgdim"]).pack(anchor="w")

        # PIN кнопка
        self.pin_btn = tk.Label(hi, text="  PIN  ",
                                font=("Consolas", 8, "bold"),
                                bg=C["bg2"], fg=C["fgdim"],
                                cursor="hand2", pady=4,
                                highlightthickness=1,
                                highlightbackground=C["border2"])
        self.pin_btn.pack(side="right", padx=(8, 0))
        self.pin_btn.bind("<Button-1>", self._toggle_topmost)

        body = tk.Frame(self, bg=C["bg0"])
        body.pack(fill="both", expand=True, padx=14, pady=(10, 8))

        # ── Обнаружение окна ─────────────────────────────────────────────────
        self._section_label(body, "ОБНАРУЖЕНИЕ ОКНА")
        nc = self._card(body)

        row1 = tk.Frame(nc, bg=C["bg1"])
        row1.pack(fill="x", pady=(0, 8))

        tk.Label(row1, text="Никнейм", font=("Consolas", 8, "bold"),
                 bg=C["bg1"], fg=C["fgdim"], width=9, anchor="w").pack(side="left")

        entry_wrap = tk.Frame(row1, bg=C["green"], padx=1, pady=1)
        entry_wrap.pack(side="left", fill="x", expand=True, padx=(4, 8))
        self.nick_entry = tk.Entry(entry_wrap, textvariable=self.v_nick,
                                   font=("Consolas", 12, "bold"),
                                   bg=C["bg2"], fg=C["fg"],
                                   insertbackground=C["green"],
                                   relief="flat", bd=0)
        self.nick_entry.pack(fill="x", ipady=6, padx=2)

        self._glow_btn(row1, "НАЙТИ", self._manual_find,
                       C["cyan"], C["cyandim"], w=9).pack(side="left")

        win_row = tk.Frame(nc, bg=C["bg1"])
        win_row.pack(fill="x")
        self.win_dot = tk.Label(win_row, text="●", font=("Consolas", 11),
                                bg=C["bg1"], fg=C["red"])
        self.win_dot.pack(side="left")
        tk.Label(win_row, textvariable=self.v_window,
                 font=("Consolas", 9),
                 bg=C["bg1"], fg=C["fgmid"]).pack(side="left", padx=4)

        # ── Режим ────────────────────────────────────────────────────────────
        self._section_label(body, "РЕЖИМ КЛИКА", top=10)
        mc = self._card(body)

        mf = tk.Frame(mc, bg=C["bg1"])
        mf.pack(fill="x")
        self._tile_spam = self._radio_tile(mf, "⚡  СПАМ ЛКМ",  "spam", 0)
        self._tile_hold = self._radio_tile(mf, "🖱  ЗАЖАТЬ ЛКМ", "hold", 1)
        self.v_mode.trace_add("write", self._on_mode)

        self.mode_desc = tk.Label(mc, text="Повторяет одиночные клики с заданной частотой",
                                  font=("Consolas", 8),
                                  bg=C["bg1"], fg=C["fgdim"])
        self.mode_desc.pack(anchor="w", pady=(8, 0))

        # ── CPS ──────────────────────────────────────────────────────────────
        self._section_label(body, "КЛИКОВ В СЕКУНДУ (CPS)", top=10)
        cc = self._card(body)

        cps_top = tk.Frame(cc, bg=C["bg1"])
        cps_top.pack(fill="x", pady=(0, 8))

        # Большой счётчик CPS
        cps_num_frame = tk.Frame(cps_top, bg=C["bg2"],
                                 highlightthickness=1,
                                 highlightbackground=C["border2"])
        cps_num_frame.pack(side="left", padx=(0, 14))

        self.cps_big = tk.Label(cps_num_frame, text="12.0",
                                font=("Consolas", 30, "bold"),
                                bg=C["bg2"], fg=C["green"],
                                padx=16, pady=4)
        self.cps_big.pack()

        tk.Label(cps_num_frame, text="кл/сек",
                 font=("Consolas", 8),
                 bg=C["bg2"], fg=C["fgdim"]).pack(pady=(0, 4))

        right_cps = tk.Frame(cps_top, bg=C["bg1"])
        right_cps.pack(side="left", fill="both", expand=True)

        # Пресеты
        pf = tk.Frame(right_cps, bg=C["bg1"])
        pf.pack(anchor="w", pady=(0, 8))
        tk.Label(pf, text="Пресеты:", font=("Consolas", 8),
                 bg=C["bg1"], fg=C["fgdim"]).pack(side="left", padx=(0, 6))
        for v in (6, 10, 16, 20, 30):
            self._glow_btn(pf, str(v), lambda x=v: self._set_cps(x),
                           C["yellow"], C["yellowdim"], w=4, small=True).pack(side="left", padx=2)

        # Ручной ввод
        mr = tk.Frame(right_cps, bg=C["bg1"])
        mr.pack(anchor="w")
        tk.Label(mr, text="Вручную:", font=("Consolas", 8),
                 bg=C["bg1"], fg=C["fgdim"]).pack(side="left", padx=(0, 6))
        self.cps_entry = tk.Entry(mr, width=7,
                                  font=("Consolas", 11, "bold"),
                                  bg=C["bg2"], fg=C["fg"],
                                  insertbackground=C["green"],
                                  relief="flat", bd=0,
                                  highlightthickness=1,
                                  highlightbackground=C["border2"],
                                  highlightcolor=C["green"])
        self.cps_entry.insert(0, "12.0")
        self.cps_entry.pack(side="left", ipady=4, padx=(0, 6))
        self.cps_entry.bind("<Return>", self._on_entry)
        self._glow_btn(mr, "OK", self._on_entry,
                       C["green"], C["greendim"], w=5).pack(side="left")

        # Слайдер (до 200, без жёсткого лимита — вручную можно больше)
        slider_frame = tk.Frame(cc, bg=C["bg1"])
        slider_frame.pack(fill="x", pady=(4, 0))
        tk.Label(slider_frame, text="1", font=("Consolas", 8),
                 bg=C["bg1"], fg=C["fgdim"]).pack(side="left")
        self.slider = tk.Scale(slider_frame,
                               variable=self.v_cps,
                               from_=1, to=200, resolution=0.5,
                               orient="horizontal",
                               bg=C["bg1"], fg=C["fg"],
                               troughcolor=C["bg2"],
                               activebackground=C["green"],
                               highlightthickness=0, bd=0,
                               showvalue=False,
                               command=self._on_slide)
        self.slider.pack(side="left", fill="x", expand=True, padx=4)
        tk.Label(slider_frame, text="200+", font=("Consolas", 8),
                 bg=C["bg1"], fg=C["fgdim"]).pack(side="left")

        # ── Горячие клавиши ───────────────────────────────────────────────────
        self._section_label(body, "ГОРЯЧИЕ КЛАВИШИ", top=10)
        hc = self._card(body)
        hf = tk.Frame(hc, bg=C["bg1"])
        hf.pack(fill="x")

        for text, var, cmd in [("▶ СТАРТ:", self.v_hk_start, self._pick_start),
                                ("■ СТОП:",  self.v_hk_stop,  self._pick_stop)]:
            g = tk.Frame(hf, bg=C["bg1"])
            g.pack(side="left", padx=(0, 20))
            tk.Label(g, text=text, font=("Consolas", 9),
                     bg=C["bg1"], fg=C["fgdim"]).pack(side="left", padx=(0, 6))
            kl = tk.Label(g, textvariable=var,
                          font=("Consolas", 13, "bold"),
                          bg=C["bg2"], fg=C["yellow"],
                          padx=14, pady=5,
                          cursor="hand2",
                          highlightthickness=1,
                          highlightbackground=C["yellow"])
            kl.pack(side="left")
            kl.bind("<Button-1>", lambda e, c=cmd: c())

        tk.Label(hf, text="(кликни чтобы сменить)",
                 font=("Consolas", 8),
                 bg=C["bg1"], fg=C["fgdim"]).pack(side="right")

        # ── Кнопки управления ────────────────────────────────────────────────
        ctrl = tk.Frame(body, bg=C["bg0"])
        ctrl.pack(fill="x", pady=(14, 4))

        self.btn_start = self._glow_btn(ctrl, "▶   СТАРТ",
                                        self.start, C["green"], C["greendim"],
                                        w=19, h=2)
        self.btn_start.pack(side="left", padx=(0, 8))

        self.btn_stop = self._glow_btn(ctrl, "■   СТОП",
                                       self.stop, C["red"], C["reddim"],
                                       w=19, h=2)
        self.btn_stop.pack(side="left")
        self._active(self.btn_stop, False)

        # ── Статус-бар ────────────────────────────────────────────────────────
        tk.Frame(self, bg=C["border2"], height=1).pack(fill="x")
        bar = tk.Frame(self, bg=C["bg1"])
        bar.pack(fill="x", side="bottom")
        bi = tk.Frame(bar, bg=C["bg1"])
        bi.pack(fill="x", padx=16, pady=7)

        self.status_dot = tk.Label(bi, text="●", font=("Consolas", 13),
                                   bg=C["bg1"], fg=C["red"])
        self.status_dot.pack(side="left")

        tk.Label(bi, textvariable=self.v_status,
                 font=("Consolas", 11, "bold"),
                 bg=C["bg1"], fg=C["fg"]).pack(side="left", padx=6)

        warns = []
        if not WIN32_OK: warns.append("pywin32 не найден")
        if not KB_OK:    warns.append("keyboard не найден")
        if warns:
            tk.Label(bi, text=" | ".join(warns),
                     font=("Consolas", 8),
                     bg=C["bg1"], fg=C["red"]).pack(side="right")

    # ── Хелперы виджетов ─────────────────────────────────────────────────────

    def _section_label(self, parent, text, top=4):
        C = self.C
        f = tk.Frame(parent, bg=C["bg0"])
        f.pack(fill="x", pady=(top, 0))
        # декоративная линия + текст
        line = tk.Frame(f, bg=C["bg0"])
        line.pack(fill="x", padx=0)
        tk.Label(line, text=text,
                 font=("Consolas", 8, "bold"),
                 bg=C["bg0"], fg=C["purple"]).pack(side="left", padx=(2, 8))
        tk.Frame(line, bg=C["border"], height=1).pack(side="left", fill="x", expand=True, pady=4)

    def _card(self, parent):
        C = self.C
        outer = tk.Frame(parent, bg=C["border2"],
                         padx=1, pady=1)
        outer.pack(fill="x", pady=(3, 0))
        inner_bg = tk.Frame(outer, bg=C["bg1"])
        inner_bg.pack(fill="x")
        inner = tk.Frame(inner_bg, bg=C["bg1"])
        inner.pack(fill="x", padx=14, pady=12)
        return inner

    def _radio_tile(self, parent, text, value, col):
        C = self.C
        sel = self.v_mode.get() == value
        f = tk.Frame(parent,
                     bg=C["greendim"] if sel else C["bg2"],
                     cursor="hand2",
                     highlightthickness=1,
                     highlightbackground=C["green"] if sel else C["border2"])
        f.grid(row=0, column=col, padx=(0, 8), ipadx=14, ipady=10, sticky="ew")
        parent.columnconfigure(col, weight=1)
        lbl = tk.Label(f, text=text,
                       font=("Consolas", 11, "bold"),
                       bg=C["greendim"] if sel else C["bg2"],
                       fg=C["green"] if sel else C["fgmid"],
                       cursor="hand2")
        lbl.pack()
        for w in (f, lbl):
            w.bind("<Button-1>", lambda e, v=value: self.v_mode.set(v))
        return (f, lbl)

    def _on_mode(self, *_):
        C = self.C
        mode = self.v_mode.get()
        for val, (f, lbl) in [("spam", self._tile_spam), ("hold", self._tile_hold)]:
            sel = val == mode
            f.config(bg=C["greendim"] if sel else C["bg2"],
                     highlightbackground=C["green"] if sel else C["border2"])
            lbl.config(bg=C["greendim"] if sel else C["bg2"],
                       fg=C["green"] if sel else C["fgmid"])
        descs = {"spam": "Повторяет одиночные клики с заданной частотой",
                 "hold": "Удерживает ЛКМ нажатой постоянно (CPS не используется)"}
        self.mode_desc.config(text=descs[mode])

    def _glow_btn(self, parent, text, cmd, color, dim, w=10, h=1, small=False):
        C = self.C
        sz = 9 if small else 11
        btn = tk.Label(parent, text=text,
                       font=("Consolas", sz, "bold"),
                       bg=dim, fg=color,
                       width=w, height=h,
                       relief="flat", cursor="hand2",
                       padx=4, pady=3 if small else 6,
                       highlightthickness=1,
                       highlightbackground=color)
        btn._active = True
        btn._dim = dim
        btn._color = color

        def enter(e):
            if btn._active:
                btn.config(bg=color, fg=C["bg0"])
        def leave(e):
            if btn._active:
                btn.config(bg=btn._dim, fg=btn._color)

        btn.bind("<Enter>", enter)
        btn.bind("<Leave>", leave)
        btn.bind("<Button-1>", lambda e: cmd() if btn._active else None)
        return btn

    def _active(self, btn, on):
        C = self.C
        btn._active = on
        if on:
            btn.config(fg=btn._color, bg=btn._dim, cursor="hand2",
                       highlightbackground=btn._color)
        else:
            btn.config(fg=C["fgdim"], bg=C["bg2"], cursor="arrow",
                       highlightbackground=C["border"])

    # ── CPS ──────────────────────────────────────────────────────────────────

    def _on_slide(self, val=None):
        v = self.v_cps.get()
        self.cps_big.config(text=f"{v:.1f}")
        self.cps_entry.delete(0, "end")
        self.cps_entry.insert(0, f"{v:.1f}")

    def _on_entry(self, e=None):
        try:
            v = max(0.1, float(self.cps_entry.get()))
            self._set_cps(v)
        except ValueError:
            pass

    def _set_cps(self, v):
        self.v_cps.set(v)
        self.cps_big.config(text=f"{v:.1f}")
        self.cps_entry.delete(0, "end")
        self.cps_entry.insert(0, f"{v:.1f}")

    # ── Поиск окна ───────────────────────────────────────────────────────────

    def _manual_find(self):
        self._find()

    def _find(self):
        nick = self.v_nick.get().strip()
        if not nick:
            self.v_window.set("Введите никнейм")
            self.win_dot.config(fg=self.C["fgdim"])
            self.hwnd = None
            return
        h = find_hwnd(nick)
        if h:
            title = win32gui.GetWindowText(h) if WIN32_OK else "--"
            self.hwnd = h
            short = title[:50] + "..." if len(title) > 50 else title
            self.v_window.set(f"HWND {h}  |  {short}")
            self.win_dot.config(fg=self.C["green"])
        else:
            self.hwnd = None
            self.v_window.set(f"Окно '{nick}' не найдено")
            self.win_dot.config(fg=self.C["red"])

    def _poll_window(self):
        self._find()
        self.after(3000, self._poll_window)

    # ── Горячие клавиши ──────────────────────────────────────────────────────

    def _register_hotkeys(self):
        if not KB_OK:
            return
        try:
            keyboard.remove_all_hotkeys()
        except Exception:
            pass
        try:
            keyboard.add_hotkey(self.v_hk_start.get(), lambda: self.after(0, self.start))
            keyboard.add_hotkey(self.v_hk_stop.get(),  lambda: self.after(0, self.stop))
        except Exception:
            pass

    def _pick_key(self, var):
        C = self.C
        dlg = tk.Toplevel(self)
        dlg.title("Нажмите клавишу")
        dlg.configure(bg=C["bg1"])
        dlg.resizable(False, False)
        dlg.geometry("280x110")
        dlg.grab_set()
        dlg.attributes("-topmost", True)
        tk.Label(dlg, text="Нажмите любую клавишу...",
                 font=("Consolas", 11),
                 bg=C["bg1"], fg=C["fg"]).pack(pady=(20, 8))
        cap = tk.Label(dlg, text="",
                       font=("Consolas", 16, "bold"),
                       bg=C["bg1"], fg=C["green"])
        cap.pack()
        def on_key(event):
            k = event.keysym
            if k == "Escape":
                dlg.destroy()
                return
            var.set(k)
            cap.config(text=k)
            self._register_hotkeys()
            dlg.after(500, dlg.destroy)
        dlg.bind("<KeyPress>", on_key)
        dlg.focus_set()

    def _pick_start(self): self._pick_key(self.v_hk_start)
    def _pick_stop(self):  self._pick_key(self.v_hk_stop)

    # ── Поверх окон ──────────────────────────────────────────────────────────

    def _toggle_topmost(self, e=None):
        val = not self.v_topmost.get()
        self.v_topmost.set(val)
        self.attributes("-topmost", val)
        self.pin_btn.config(
            fg=self.C["green"] if val else self.C["fgdim"],
            bg=self.C["greendim"] if val else self.C["bg2"],
            highlightbackground=self.C["green"] if val else self.C["border2"])

    # ── Кликер ───────────────────────────────────────────────────────────────

    def start(self):
        if self.running:
            return
        if not self.hwnd:
            self._find()
        if not self.hwnd:
            self._shake()
            self.v_status.set("ОКНО НЕ НАЙДЕНО")
            return
        self.running = True
        self.v_status.set("РАБОТАЕТ...")
        self.status_dot.config(fg=self.C["green"])
        self._active(self.btn_start, False)
        self._active(self.btn_stop,  True)
        fn = self._loop_spam if self.v_mode.get() == "spam" else self._loop_hold
        self._thread = threading.Thread(target=fn, daemon=True)
        self._thread.start()

    def stop(self):
        if not self.running:
            return
        self.running = False
        if self.hwnd and WIN32_OK:
            try:
                send_up(self.hwnd)
            except Exception:
                pass
        self.v_status.set("ОСТАНОВЛЕН")
        self.status_dot.config(fg=self.C["red"])
        self._active(self.btn_start, True)
        self._active(self.btn_stop,  False)

    def _loop_spam(self):
        while self.running:
            cps = max(self.v_cps.get(), 0.1)
            interval = 1.0 / cps
            try:
                if self.hwnd:
                    send_click(self.hwnd)
            except Exception:
                pass
            slept, chunk = 0.0, 0.02
            while slept < interval and self.running:
                t = min(chunk, interval - slept)
                time.sleep(t)
                slept += t

    def _loop_hold(self):
        try:
            if self.hwnd:
                send_down(self.hwnd)
        except Exception:
            pass
        while self.running:
            try:
                if self.hwnd:
                    target = get_render_hwnd(self.hwnd)
                    lp = make_lparam(target)
                    win32api.PostMessage(target, LBDOWN, MK_LB, lp)
            except Exception:
                pass
            time.sleep(0.05)
        try:
            if self.hwnd:
                send_up(self.hwnd)
        except Exception:
            pass

    def _shake(self):
        x, y = self.winfo_x(), self.winfo_y()
        for i in range(8):
            self.geometry(f"+{x + (10 if i % 2 == 0 else -10)}+{y}")
            self.update()
            time.sleep(0.03)
        self.geometry(f"+{x}+{y}")

    def on_close(self):
        self.stop()
        if KB_OK:
            try:
                keyboard.remove_all_hotkeys()
            except Exception:
                pass
        self.destroy()


if __name__ == "__main__":
    app = App()
    app.protocol("WM_DELETE_WINDOW", app.on_close)
    app.mainloop()
