import json
import os
import traceback
from datetime import datetime
import tkinter as tk
from tkinter import ttk, simpledialog, messagebox
from openpyxl import Workbook, load_workbook
from openpyxl.styles import PatternFill
from PIL import Image, ImageTk
import ctypes
import ctypes.wintypes

APP_VERSION = "0.8.9 patch"

# ---------- Složka pro pomocné soubory (včetně ikon a log) ----------
SOURCE_DIR = "source"

def get_source_path(filename):
    return os.path.join(SOURCE_DIR, filename)

# Cesty k souborům
DATA_FILE = get_source_path("dochazka_cache.json")
HISTORY_FILE = "dochazka_history.xlsx"
ROOMS_FILE = get_source_path("rooms.json")
CHIPS_FILE = get_source_path("chips.json")
SETTINGS_FILE = get_source_path("settings.json")
LOGO_ICO = get_source_path("logo.ico")
LOGO_PNG = get_source_path("logo.png")
LOGO1_PNG = get_source_path("logo1.png")

# Kniha návštěv
VISITS_DATA_FILE = get_source_path("navsteva_cache.json")
VISITS_HISTORY_FILE = "navsteva_history.xlsx"

# Samostatný soubor pro historii odjezdů a příjezdů (odjezdovka)
TRANSPORT_HISTORY_FILE = "odjezdovka_history.xlsx"

# Dočasné soubory pro neuložené záznamy (když je Excel zamčený)
PENDING_HISTORY_FILE = get_source_path("dochazka_pending.json")
PENDING_VISITS_FILE = get_source_path("navsteva_pending.json")
PENDING_TRANSPORT_FILE = get_source_path("transport_pending.json")

# Vytvoření složky source, pokud neexistuje
if not os.path.exists(SOURCE_DIR):
    os.makedirs(SOURCE_DIR)

# ---------- Logování chyb ----------
def log_error(message, exc_info=True):
    """Zapíše chybu do log souboru."""
    log_file = get_source_path("error.log")
    try:
        with open(log_file, "a", encoding="utf-8") as f:
            f.write(f"[{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}] {message}\n")
            if exc_info:
                f.write(traceback.format_exc())
            f.write("\n")
    except:
        pass

# ---------- Bezpečné načítání JSON ----------
def safe_json_load(filepath, default, log_error_flag=True):
    """Bezpečně načte JSON soubor. Při chybě vrátí default a zaloguje."""
    if not os.path.exists(filepath):
        return default
    try:
        with open(filepath, "r", encoding="utf-8") as f:
            return json.load(f)
    except (json.JSONDecodeError, OSError, IOError) as e:
        if log_error_flag:
            log_error(f"Chyba při načítání JSON souboru {filepath}: {e}")
        return default

def safe_json_save(filepath, data):
    """Bezpečně uloží data do JSON souboru."""
    try:
        with open(filepath, "w", encoding="utf-8") as f:
            json.dump(data, f, indent=4, ensure_ascii=False)
        return True
    except Exception as e:
        log_error(f"Chyba při ukládání JSON souboru {filepath}: {e}")
        return False

# ---------- Default settings ----------
DEFAULT_SETTINGS = {
    "rfid_enabled": True,
    "kiosk_enabled": True
}

def load_settings():
    data = safe_json_load(SETTINGS_FILE, DEFAULT_SETTINGS)
    if not isinstance(data, dict):
        log_error(f"Settings soubor neobsahuje data, vracím default. Obsah: {data}")
        return DEFAULT_SETTINGS.copy()
    return data

def save_settings(settings):
    safe_json_save(SETTINGS_FILE, settings)

# ---------- Default rooms ----------
def get_default_rooms():
    skip_rooms = {7, 25, 45}
    rooms = {str(i): [] for i in range(1, 61) if i not in skip_rooms}
    return rooms

# ---------- Rooms storage ----------
def load_rooms():
    data = safe_json_load(ROOMS_FILE, get_default_rooms())
    if not isinstance(data, dict):
        log_error(f"Rooms soubor neobsahuje data, vracím default. Obsah: {data}")
        return get_default_rooms()
    return data

def save_rooms(rooms_data):
    safe_json_save(ROOMS_FILE, rooms_data)

rooms = load_rooms()

# ---------- RFID chips ----------
def load_chips():
    chips_data = safe_json_load(CHIPS_FILE, {})
    if not isinstance(chips_data, dict):
        log_error(f"Chips soubor neobsahuje data, vracím prázdný data. Obsah: {chips_data}")
        chips_data = {}
    new_data = {}
    for k, v in chips_data.items():
        new_data[k.lower()] = v
    if new_data != chips_data:
        save_chips(new_data)
    return new_data

def save_chips(chips_data):
    safe_json_save(CHIPS_FILE, chips_data)

chips = load_chips()

# ---------- Attendance storage ----------
def load_data():
    data = safe_json_load(DATA_FILE, {})
    if not isinstance(data, dict):
        log_error(f"Docházková data neobsahují data, vracím prázdný data. Obsah: {data}")
        return {}
    return data

def save_data(data):
    safe_json_save(DATA_FILE, data)

# ---------- Excel history (docházka) - ukládají se pouze běžné stavy (ne odjezdovka) ----------
def _append_pending_history(record, pending_file):
    pending = []
    if os.path.exists(pending_file):
        try:
            with open(pending_file, "r", encoding="utf-8") as f:
                pending = json.load(f)
        except (json.JSONDecodeError, OSError) as e:
            log_error(f"Chyba při čtení pending souboru {pending_file}: {e}")
            pending = []
    pending.append(record)
    with open(pending_file, "w", encoding="utf-8") as f:
        json.dump(pending, f, indent=4, ensure_ascii=False)

def _load_pending_history(pending_file):
    if not os.path.exists(pending_file):
        return []
    try:
        with open(pending_file, "r", encoding="utf-8") as f:
            pending = json.load(f)
        os.remove(pending_file)
        return pending
    except (json.JSONDecodeError, OSError) as e:
        log_error(f"Chyba při načítání pending souboru {pending_file}: {e}")
        return []

def _safe_save_excel(workbook_func, pending_records_func, pending_file, target_file):
    try:
        wb, ws = workbook_func()
        pending_records_func(ws)
        wb.save(target_file)
        return True
    except (PermissionError, OSError) as e:
        log_error(f"Excel soubor je zamčený (nelze zapsat): {e}")
        return False

def save_history(record):
    """Uloží záznam do hlavní docházkové historie, ale ignoruje odjezdovku."""
    # Neukládat záznamy, které obsahují "odjezdovka" (bez ohledu na velikost písmen)
    if "odjezdovka" in record.get("status", "").lower():
        return

    red_fill = PatternFill(start_color="FFC7CE", end_color="FFC7CE", fill_type="solid")
    green_fill = PatternFill(start_color="C6EFCE", end_color="C6EFCE", fill_type="solid")

    month_name = datetime.now().strftime("%m-%Y")
    day_label = datetime.now().strftime("%d.%m.%Y")

    def workbook_setup():
        if os.path.exists(HISTORY_FILE):
            wb = load_workbook(HISTORY_FILE)
        else:
            wb = Workbook()

        if month_name in wb.sheetnames:
            ws = wb[month_name]
        else:
            ws = wb.create_sheet(title=month_name)
            ws.append(["Jméno", "Stav", "Kam", "Čas", "Datum"])

        last_date = None
        if ws.max_row > 1:
            last_date = ws.cell(row=ws.max_row, column=5).value
        if last_date != day_label:
            ws.append(["", f"--- {day_label} ---", "", "", ""])
        return wb, ws

    def write_records(ws):
        ws.append([
            record["person"],
            record["status"],
            record["place"],
            record["time"],
            record["date"]
        ])
        row = ws.max_row
        if record.get("person"):
            fill = green_fill if record["status"] == "Přítomen" else red_fill
            for col in range(1, 6):
                ws.cell(row=row, column=col).fill = fill

    success = _safe_save_excel(workbook_setup, write_records, PENDING_HISTORY_FILE, HISTORY_FILE)
    if not success:
        _append_pending_history(record, PENDING_HISTORY_FILE)
    else:
        process_pending_history()

def process_pending_history():
    pending = _load_pending_history(PENDING_HISTORY_FILE)
    if not pending:
        return
    for rec in pending:
        save_history(rec)

# ---------- Historie odjezdovky (odjezdy i příjezdy) ----------
def save_transport_history(record):
    """Uloží záznam o odjezdu nebo příjezdu do samostatného Excelu odjezdovka_history.xlsx."""
    month_name = datetime.now().strftime("%m-%Y")
    day_label = datetime.now().strftime("%d.%m.%Y")
    # Různé barvy pro odjezd a příjezd
    if record["typ"] == "Odjezd":
        fill_color = PatternFill(start_color="FFEB9C", end_color="FFEB9C", fill_type="solid")  # žlutá
    else:
        fill_color = PatternFill(start_color="C6EFCE", end_color="C6EFCE", fill_type="solid")  # zelená

    def workbook_setup():
        if os.path.exists(TRANSPORT_HISTORY_FILE):
            wb = load_workbook(TRANSPORT_HISTORY_FILE)
        else:
            wb = Workbook()
            if "Sheet" in wb.sheetnames:
                del wb["Sheet"]

        if month_name in wb.sheetnames:
            ws = wb[month_name]
        else:
            ws = wb.create_sheet(title=month_name)
            ws.append(["Jméno", "Datum", "Čas", "Typ", "Poznámka"])

        # Kontrola oddělovače dne
        last_date = None
        if ws.max_row > 1:
            for row in range(ws.max_row, 0, -1):
                val = ws.cell(row=row, column=2).value
                if val and not str(val).startswith("---"):
                    try:
                        last_date = datetime.strptime(str(val), "%d.%m.%Y").strftime("%d.%m.%Y")
                        break
                    except:
                        pass
        if last_date != day_label:
            ws.append(["", f"--- {day_label} ---", "", "", ""])
        return wb, ws

    def write_records(ws):
        ws.append([
            record["person"],
            record["date"],
            record["time"],
            record["typ"],
            record["note"]
        ])
        row = ws.max_row
        for col in range(1, 6):
            ws.cell(row=row, column=col).fill = fill_color

    success = _safe_save_excel(workbook_setup, write_records, PENDING_TRANSPORT_FILE, TRANSPORT_HISTORY_FILE)
    if not success:
        _append_pending_history(record, PENDING_TRANSPORT_FILE)
    else:
        process_pending_transport()

def process_pending_transport():
    pending = _load_pending_history(PENDING_TRANSPORT_FILE)
    if not pending:
        return
    for rec in pending:
        save_transport_history(rec)

# ---------- Kniha návštěv – správa dat s ochranou proti zamčenému Excelu ----------
def load_visits():
    data = safe_json_load(VISITS_DATA_FILE, [])
    if not isinstance(data, list):
        log_error(f"Visits data nejsou seznam, vracím prázdný seznam. Obsah: {data}")
        return []
    return data

def save_visits(visits):
    safe_json_save(VISITS_DATA_FILE, visits)

def save_visit_history(visit_record):
    place_colors = {
        "Učebna PC": PatternFill(start_color="C6EFCE", end_color="C6EFCE", fill_type="solid"),
        "Návštěva": PatternFill(start_color="FCE4D6", end_color="FCE4D6", fill_type="solid")
    }
    default_fill = PatternFill(start_color="FFFFFF", end_color="FFFFFF", fill_type="solid")

    odchod_dt = datetime.strptime(visit_record["odchod"], "%d.%m.%Y %H:%M:%S")
    month_name = odchod_dt.strftime("%m-%Y")
    day_label = odchod_dt.strftime("%d.%m.%Y")

    def workbook_setup():
        if os.path.exists(VISITS_HISTORY_FILE):
            wb = load_workbook(VISITS_HISTORY_FILE)
        else:
            wb = Workbook()
            if "Sheet" in wb.sheetnames:
                del wb["Sheet"]

        if month_name in wb.sheetnames:
            ws = wb[month_name]
        else:
            ws = wb.create_sheet(title=month_name)
            ws.append(["Jméno", "Typ", "Číslo pokoje", "Číslo počítače", "Detail návštěvy", "Příchod", "Odchod"])

        last_date = None
        if ws.max_row > 1:
            for row in range(ws.max_row, 0, -1):
                val = ws.cell(row=row, column=6).value
                if val and not str(val).startswith("---"):
                    try:
                        last_date = datetime.strptime(str(val), "%d.%m.%Y %H:%M:%S").strftime("%d.%m.%Y")
                        break
                    except:
                        pass
        if last_date != day_label:
            ws.append(["", f"--- {day_label} ---", "", "", "", "", ""])
        return wb, ws

    def write_records(ws):
        ws.append([
            visit_record["jmeno"],
            visit_record["typ"],
            visit_record.get("cislo_pokoje", ""),
            visit_record.get("cislo_pocitace", ""),
            visit_record.get("detail_mista", ""),
            visit_record["prichod"],
            visit_record["odchod"]
        ])
        row = ws.max_row
        fill = place_colors.get(visit_record["typ"], default_fill)
        for col in range(1, 8):
            ws.cell(row=row, column=col).fill = fill

    success = _safe_save_excel(workbook_setup, write_records, PENDING_VISITS_FILE, VISITS_HISTORY_FILE)
    if not success:
        _append_pending_history(visit_record, PENDING_VISITS_FILE)
    else:
        process_pending_visits()

def process_pending_visits():
    pending = _load_pending_history(PENDING_VISITS_FILE)
    if not pending:
        return
    for rec in pending:
        save_visit_history(rec)

# ---------- Windows API pro získání geometrie monitorů ----------
def get_monitors_windows():
    monitors = []
    class RECT(ctypes.Structure):
        _fields_ = [
            ('left', ctypes.wintypes.LONG),
            ('top', ctypes.wintypes.LONG),
            ('right', ctypes.wintypes.LONG),
            ('bottom', ctypes.wintypes.LONG)
        ]
    class MONITORINFO(ctypes.Structure):
        _fields_ = [
            ('cbSize', ctypes.wintypes.DWORD),
            ('rcMonitor', RECT),
            ('rcWork', RECT),
            ('dwFlags', ctypes.wintypes.DWORD)
        ]
    def monitor_enum_proc(hMonitor, hdcMonitor, lprcMonitor, dwData):
        mi = MONITORINFO()
        mi.cbSize = ctypes.sizeof(MONITORINFO)
        ctypes.windll.user32.GetMonitorInfoW(hMonitor, ctypes.byref(mi))
        monitors.append({
            'x': mi.rcMonitor.left,
            'y': mi.rcMonitor.top,
            'width': mi.rcMonitor.right - mi.rcMonitor.left,
            'height': mi.rcMonitor.bottom - mi.rcMonitor.top
        })
        return True
    callback = ctypes.WINFUNCTYPE(ctypes.wintypes.BOOL, ctypes.wintypes.HANDLE, ctypes.wintypes.HDC, ctypes.POINTER(RECT), ctypes.wintypes.LPARAM)
    ctypes.windll.user32.EnumDisplayMonitors(None, None, callback(monitor_enum_proc), 0)
    return monitors

def get_second_screen_geometry():
    try:
        monitors = get_monitors_windows()
        if len(monitors) >= 2:
            return monitors[1]['x'], monitors[1]['y'], monitors[1]['width'], monitors[1]['height']
        elif monitors:
            return monitors[0]['x'], monitors[0]['y'], monitors[0]['width'], monitors[0]['height']
        else:
            root = tk._default_root or tk.Tk()
            root.withdraw()
            w = root.winfo_screenwidth()
            h = root.winfo_screenheight()
            root.destroy()
            return 0, 0, w, h
    except Exception as e:
        log_error(f"Chyba při získávání geometrie monitoru: {e}")
        root = tk._default_root or tk.Tk()
        root.withdraw()
        w = root.winfo_screenwidth()
        h = root.winfo_screenheight()
        root.destroy()
        return 0, 0, w, h

# ---------- Okno Knihy návštěv ----------
class VisitBookWindow:
    def __init__(self, parent):
        self.parent = parent
        self.window = tk.Toplevel(parent)
        self.window.title("Kniha návštěv")
        self.window.geometry("1100x700")
        self.window.transient(parent)
        self.window.grab_set()

        self.visits = load_visits()
        self.create_widgets()
        self.refresh_list()
        # Zaregistrujeme validaci vstupů u tohoto okna
        self.parent._register_validation(self.window)

    def create_widgets(self):
        main_frame = ttk.Frame(self.window, padding=10)
        main_frame.pack(fill="both", expand=True)

        self.stats_frame = ttk.LabelFrame(main_frame, text="Aktuální stav návštěv", padding=5)
        self.stats_frame.pack(fill="x", pady=(0, 10))
        self.stats_label = ttk.Label(self.stats_frame, text="", font=("Arial", 10))
        self.stats_label.pack()

        add_frame = ttk.LabelFrame(main_frame, text="Přidat novou návštěvu", padding=10)
        add_frame.pack(fill="x", pady=(0, 10))

        ttk.Label(add_frame, text="Jméno:").grid(row=0, column=0, padx=5, pady=5, sticky="e")
        self.name_entry = ttk.Entry(add_frame, width=20)
        self.name_entry.grid(row=0, column=1, padx=5, pady=5)

        ttk.Label(add_frame, text="Typ:").grid(row=0, column=2, padx=5, pady=5, sticky="e")
        self.type_combo = ttk.Combobox(add_frame, values=["Učebna PC", "Návštěva"], state="readonly", width=15)
        self.type_combo.grid(row=0, column=3, padx=5, pady=5)
        self.type_combo.bind("<<ComboboxSelected>>", self.on_type_selected)
        self.type_combo.set("Učebna PC")

        self.dynamic_frame = ttk.Frame(add_frame)
        self.dynamic_frame.grid(row=1, column=0, columnspan=7, pady=5)

        self.add_btn = ttk.Button(add_frame, text="Přidat návštěvu", command=self.add_visit)
        self.add_btn.grid(row=0, column=6, padx=20, pady=5)

        columns = ("Jméno", "Typ", "Číslo pokoje", "Číslo počítače", "Detail", "Příchod", "Odchod", "Stav")
        self.tree = ttk.Treeview(main_frame, columns=columns, show="headings")
        for col in columns:
            self.tree.heading(col, text=col)
            self.tree.column(col, anchor="center", width=110)
        self.tree.column("Jméno", width=140)
        self.tree.column("Typ", width=100)
        self.tree.column("Číslo pokoje", width=90)
        self.tree.column("Číslo počítače", width=100)
        self.tree.column("Detail", width=120)
        self.tree.column("Příchod", width=140)
        self.tree.column("Odchod", width=140)
        self.tree.column("Stav", width=70)

        self.tree.pack(fill="both", expand=True)

        self.context_menu = tk.Menu(self.window, tearoff=0)
        self.context_menu.add_command(label="Zapsat odchod", command=self.set_departure)
        self.tree.bind("<Button-3>", self.show_context_menu)
        self.tree.bind("<Double-1>", lambda e: self.set_departure())

        info_label = ttk.Label(main_frame, text="Dvojklik nebo pravé tlačítko na řádku → Zapsat odchod", font=("Arial", 9))
        info_label.pack(pady=5)

        self.tree.tag_configure("Učebna PC", background="#C6EFCE")
        self.tree.tag_configure("Návštěva", background="#FCE4D6")

        self.on_type_selected()

    def on_type_selected(self, event=None):
        for widget in self.dynamic_frame.winfo_children():
            widget.destroy()
        typ = self.type_combo.get()
        if typ == "Učebna PC":
            ttk.Label(self.dynamic_frame, text="Číslo pokoje:*").pack(side="left", padx=5)
            self.room_entry = ttk.Entry(self.dynamic_frame, width=10)
            self.room_entry.pack(side="left", padx=5)
            ttk.Label(self.dynamic_frame, text="Číslo počítače:*").pack(side="left", padx=5)
            self.pc_entry = ttk.Entry(self.dynamic_frame, width=10)
            self.pc_entry.pack(side="left", padx=5)
            self.detail_entry = None
        else:
            ttk.Label(self.dynamic_frame, text="Číslo pokoje (volitelné):").pack(side="left", padx=5)
            self.room_entry = ttk.Entry(self.dynamic_frame, width=10)
            self.room_entry.pack(side="left", padx=5)
            ttk.Label(self.dynamic_frame, text="Místo (volitelné):").pack(side="left", padx=5)
            self.detail_entry = ttk.Entry(self.dynamic_frame, width=15)
            self.detail_entry.pack(side="left", padx=5)
            self.pc_entry = None

    def add_visit(self):
        jmeno = self.name_entry.get().strip()
        if not jmeno:
            messagebox.showwarning("Chyba", "Zadejte jméno návštěvníka.")
            return
        typ = self.type_combo.get()
        cislo_pokoje = None
        cislo_pocitace = None
        detail_mista = None
        if typ == "Učebna PC":
            cislo = self.room_entry.get().strip()
            if not cislo:
                messagebox.showwarning("Chyba", "Zadejte číslo pokoje.")
                return
            cislo_pokoje = cislo
            pc = self.pc_entry.get().strip()
            if not pc:
                messagebox.showwarning("Chyba", "Zadejte číslo počítače.")
                return
            cislo_pocitace = pc
        else:
            cislo = self.room_entry.get().strip()
            if cislo:
                cislo_pokoje = cislo
            detail_mista = self.detail_entry.get().strip() if self.detail_entry else ""
        now = datetime.now()
        prichod_str = now.strftime("%d.%m.%Y %H:%M:%S")
        new_visit = {
            "id": len(self.visits) + 1,
            "jmeno": jmeno,
            "typ": typ,
            "cislo_pokoje": cislo_pokoje,
            "cislo_pocitace": cislo_pocitace,
            "detail_mista": detail_mista,
            "prichod": prichod_str,
            "odchod": None,
            "aktivni": True
        }
        self.visits.append(new_visit)
        save_visits(self.visits)
        self.name_entry.delete(0, tk.END)
        if hasattr(self, 'room_entry'):
            self.room_entry.delete(0, tk.END)
        if hasattr(self, 'pc_entry') and self.pc_entry:
            self.pc_entry.delete(0, tk.END)
        if hasattr(self, 'detail_entry') and self.detail_entry:
            self.detail_entry.delete(0, tk.END)
        self.type_combo.set("Učebna PC")
        self.on_type_selected()
        self.refresh_list()

    def refresh_list(self):
        for item in self.tree.get_children():
            self.tree.delete(item)
        counts = {"Učebna PC": 0, "Návštěva": 0}
        for v in self.visits:
            if v["aktivni"]:
                odchod_str = v["odchod"] if v["odchod"] else "—"
                stav = "Přítomen"
                typ = v["typ"]
                counts[typ] = counts.get(typ, 0) + 1
                if typ == "Učebna PC":
                    detail_zobraz = f"PC {v.get('cislo_pocitace', '')}"
                else:
                    detail_zobraz = v.get("detail_mista", "") or "—"
                self.tree.insert("", "end", values=(
                    v["jmeno"],
                    typ,
                    v.get("cislo_pokoje") or "—",
                    v.get("cislo_pocitace") or "—" if typ == "Učebna PC" else "—",
                    detail_zobraz,
                    v["prichod"],
                    odchod_str,
                    stav
                ), tags=(typ,))
        stats_text = f"Učebna PC: {counts['Učebna PC']}   |   Návštěva: {counts['Návštěva']}"
        self.stats_label.config(text=stats_text)

    def set_departure(self):
        selected = self.tree.selection()
        if not selected:
            return
        item = selected[0]
        values = self.tree.item(item, "values")
        jmeno = values[0]
        prichod = values[5]
        for v in self.visits:
            if v["jmeno"] == jmeno and v["prichod"] == prichod and v["aktivni"]:
                now = datetime.now()
                odchod_str = now.strftime("%d.%m.%Y %H:%M:%S")
                v["odchod"] = odchod_str
                v["aktivni"] = False
                save_visit_history({
                    "jmeno": v["jmeno"],
                    "typ": v["typ"],
                    "cislo_pokoje": v.get("cislo_pokoje"),
                    "cislo_pocitace": v.get("cislo_pocitace"),
                    "detail_mista": v.get("detail_mista"),
                    "prichod": v["prichod"],
                    "odchod": odchod_str
                })
                save_visits(self.visits)
                messagebox.showinfo("Odchod zapsán", f"Návštěvníkovi {jmeno} byl zapsán odchod v {odchod_str}")
                self.refresh_list()
                break

    def show_context_menu(self, event):
        item = self.tree.identify_row(event.y)
        if item:
            self.tree.selection_set(item)
            self.context_menu.post(event.x_root, event.y_root)

# ---------- Splash screen ----------
def show_splash():
    splash = tk.Tk()
    splash.title("")
    splash.overrideredirect(True)
    splash.configure(bg="#f0f0f0")
    screen_width = splash.winfo_screenwidth()
    screen_height = splash.winfo_screenheight()
    width, height = 450, 320
    x = (screen_width - width) // 2
    y = (screen_height - height) // 2
    splash.geometry(f"{width}x{height}+{x}+{y}")
    try:
        splash.iconbitmap(LOGO_ICO)
    except:
        pass
    main_frame = tk.Frame(splash, bg="#f0f0f0")
    main_frame.pack(fill="both", expand=True)
    try:
        img = Image.open(LOGO_PNG)
        img = img.resize((80, 80), Image.Resampling.LANCZOS)
        logo_img = ImageTk.PhotoImage(img)
        logo_label = tk.Label(main_frame, image=logo_img, bg="#f0f0f0")
        logo_label.image = logo_img
        logo_label.pack(pady=(25, 5))
    except Exception:
        pass
    label_title = tk.Label(main_frame, text="Docházkový systém\nSLŠ Hranice", 
                           font=("Arial", 22, "bold"), fg="#2c3e50", bg="#f0f0f0")
    label_title.pack(pady=(10, 5))
    label_sub = tk.Label(main_frame, text="PCZ - 2026", font=("Arial", 14), 
                         fg="#7f8c8d", bg="#f0f0f0")
    label_sub.pack()
    separator = tk.Frame(main_frame, height=2, bg="#bdc3c7")
    separator.pack(fill="x", pady=(15, 10), padx=40)
    label_loading = tk.Label(main_frame, text="Načítání dat, prosím čekejte...", 
                             font=("Arial", 10), fg="#34495e", bg="#f0f0f0")
    label_loading.pack(side="bottom", pady=20)
    version_label = tk.Label(main_frame, text=f"verze {APP_VERSION}", 
                             font=("Arial", 8), fg="#95a5a6", bg="#f0f0f0")
    version_label.place(relx=1.0, rely=1.0, anchor="se", x=-15, y=-15)
    splash.update()
    splash.after(2000, splash.destroy)
    return splash

# ---------- Hlavní aplikace (Docházka) ----------
class App:
    def __init__(self, root):
        self.root = root
        self.root.title("Docházka")
        self.root.state("zoomed")
        try:
            self.root.iconbitmap(LOGO_ICO)
        except:
            pass

        self.settings = load_settings()
        self.data = load_data()

        # Cooldown pro RFID – ochrana proti opakovanému čtení stejného čipu
        self.last_rfid_chip = None
        self.last_rfid_time = 0
        self.rfid_cooldown_ms = 2000

        # Stav skenování čipu (pro potlačení vkládání do Entry)
        self.rfid_scanning = False
        self.rfid_scanning_timer = None
        self.rfid_buffer = ""

        self.root.bind_all("<Key>", self.handle_rfid_input)

        self.current_person = None
        self.reset_job = None
        self.second_window = None
        self.last_unknown_chip = None
        self.kiosk_error_job = None

        self.create_menu()
        self.create_ui()
        if self.settings["kiosk_enabled"]:
            self.setup_second_screen()
        self.refresh()

        process_pending_history()
        process_pending_visits()
        process_pending_transport()

        # Zaregistrujeme validaci vstupů pro hlavní okno
        self._register_validation(self.root)

    # ---------- Validace vstupů (zabraňuje zápisu během skenování čipu) ----------
    def _register_validation(self, window):
        """Projde všechny widgety v okně a nastaví validatecommand na Entry/Combobox."""
        def apply_validation(widget):
            if isinstance(widget, (tk.Entry, ttk.Entry, ttk.Combobox)):
                # Uložíme původní validatecommand, pokud existuje, ale my ho nahradíme
                # Abychom neblokovali speciální klávesy (Enter, Backspace, atd.)
                widget.configure(validate='key')
                # Zaregistrujeme funkci
                vcmd = (self.root.register(self._rfid_validate), '%S', '%d', widget)
                widget.configure(validatecommand=vcmd)
            # Rekurzivně pro vnitřní widgety
            try:
                for child in widget.winfo_children():
                    apply_validation(child)
            except:
                pass
        apply_validation(window)

    def _rfid_validate(self, char, action_type, widget):
        """Validatecommand – vrací False, pokud probíhá skenování čipu a nejedná se o Enter/Backspace."""
        # action_type '0' = delete, '1' = insert, '-1' = focus?
        if action_type != '1':  # Zajímá nás pouze vkládání znaků
            return True
        if self.rfid_scanning:
            # Během skenování zakážeme vkládání jakýchkoli znaků
            return False
        return True

    def _reset_rfid_scanning(self):
        """Ukončí režim skenování čipu a zruší timeout."""
        self.rfid_scanning = False
        if self.rfid_scanning_timer:
            self.root.after_cancel(self.rfid_scanning_timer)
            self.rfid_scanning_timer = None

    # ---------- Menu ----------
    def create_menu(self):
        menubar = tk.Menu(self.root)
        self.root.config(menu=menubar)
        settings_menu = tk.Menu(menubar, tearoff=0)
        menubar.add_cascade(label="Nastavení", menu=settings_menu)
        settings_menu.add_command(label="Nastavení aplikace", command=self.open_settings)
        settings_menu.add_separator()
        settings_menu.add_command(label="Konec", command=self.root.quit)
        
        # Nové menu "Akce" pro hromadné operace
        akce_menu = tk.Menu(menubar, tearoff=0)
        menubar.add_cascade(label="Odjezdy", menu=akce_menu)
        akce_menu.add_command(label="Hromadný odjezd - pátek", command=self.bulk_departure_weekend)
        akce_menu.add_command(label="Hromadný návrat z víkendu - příjezd", command=self.bulk_return_weekend)
        
        menubar.add_command(label="Kniha návštěv", command=self.open_visit_book)
        help_menu = tk.Menu(menubar, tearoff=0)
        menubar.add_cascade(label="Nápověda", menu=help_menu)
        help_menu.add_command(label="Nápověda", command=self.show_help)
        help_menu.add_command(label="O programu", command=self.show_about)

    def open_visit_book(self):
        VisitBookWindow(self.root)

    # ---------- UI ----------
    def create_ui(self):
        frame = ttk.Frame(self.root, padding=20)
        frame.pack(fill="both", expand=True)
        top_bar = ttk.Frame(frame)
        top_bar.pack(fill="x", pady=(0, 10))
        try:
            img = Image.open(LOGO1_PNG)
            img = img.resize((325, 80), Image.Resampling.LANCZOS)
            self.logo_img = ImageTk.PhotoImage(img)
            logo_label = ttk.Label(top_bar, image=self.logo_img)
            logo_label.pack(side="left", padx=5)
        except Exception:
            pass
        self.date_label = ttk.Label(frame, text="", font=("Arial", 20))
        self.date_label.pack(pady=10)
        self.stats_label = ttk.Label(frame, text="", font=("Arial", 14))
        self.stats_label.pack(pady=5)
        search_frame = ttk.Frame(frame)
        search_frame.pack(pady=5)
        ttk.Label(search_frame, text="Pokoj:").pack(side="left")
        self.search_var = tk.StringVar()
        ttk.Entry(search_frame, textvariable=self.search_var, width=10).pack(side="left", padx=5)
        ttk.Button(search_frame, text="Najít", command=self.search_room).pack(side="left")
        columns = ("Pokoj", "Jméno", "Stav", "Čas", "Kam")
        self.tree = ttk.Treeview(frame, columns=columns, show="headings")
        for col in columns:
            self.tree.heading(col, text=col)
            self.tree.column(col, anchor="center")
        self.tree.pack(fill="both", expand=True)
        self.tree.bind("<Double-1>", self.toggle_status)
        self.tree.bind("<Button-3>", self.show_menu)
        self.menu = tk.Menu(self.root, tearoff=0)
        self.menu.add_command(label="Upravit jméno", command=self.edit_name)
        self.menu.add_command(label="Přidat osobu", command=self.add_person)
        self.menu.add_separator()
        self.menu.add_command(label="Smazat osobu", command=self.delete_person)
        self.menu.add_separator()
        self.menu.add_command(label="Přiřadit čip", command=self.assign_chip)
        self.menu.add_separator()
        self.menu.add_command(label="Odjezdovka - odjezd domů", command=self.set_individual_departure_weekend)
        self.menu.add_command(label="Návrat z víkendu (příjezd na internát)", command=self.set_individual_return_weekend)
        self.tree.tag_configure("in", background="#c8f7c5")
        self.tree.tag_configure("out", background="#f7c5c5")
        self.tree.tag_configure("weekend", background="#FFD966")  # oranžová pro speciální stav
        self.tree.tag_configure("separator", background="#d9d9d9")
        version_label = ttk.Label(frame, text=f"verze {APP_VERSION}", font=("Arial", 8))
        version_label.pack(side="bottom", anchor="se", padx=10, pady=5)
        self.copyright_label = ttk.Label(
            frame,
            text="Autor se vzdává veškeré odpovědnosti za případnou ztrátu či poškození dat. Úpravy JSON souborů ručně nebo úpravy Excel souboru mohou vést ke ztrátě dat a provádíte je na vlastní riziko.",
            font=("Arial", 5),
            foreground="red",
            wraplength=800,
            justify="center"
        )
        self.copyright_label.pack(pady=(5,0))
        self.copyright_label2 = ttk.Label(
            frame,
            text="PCZ - 2026",
            font=("Arial", 4),
            foreground="black",
            wraplength=800,
            justify="center"
        )
        self.copyright_label2.pack(pady=(5,0))

    # ---------- Kiosk ----------
    def setup_second_screen(self):
        if not self.settings["kiosk_enabled"] or self.second_window:
            return
        x, y, width, height = get_second_screen_geometry()
        self.second_window = tk.Toplevel(self.root)
        self.second_window.geometry(f"{width}x{height}+{x}+{y}")
        self.second_window.overrideredirect(True)
        self.second_window.configure(bg="black")
        version_label = tk.Label(self.second_window, text=f"{APP_VERSION}", font=("Arial", 12), fg="white", bg="black")
        version_label.place(x=10, y=10)
        main_frame = tk.Frame(self.second_window, bg="black")
        main_frame.pack(fill="both", expand=True)
        center_block = tk.Frame(main_frame, bg="black")
        center_block.place(relx=0.5, rely=0.5, anchor="center")
        self.second_label = tk.Label(center_block, text="Přiložte čip", fg="white", bg="black", font=("Arial", 50))
        self.second_label.pack(pady=(0, 40))
        self.depart_buttons = {}
        places = ["Město", "Naparia", "Jídelna", "Školní akce", "Kroužek"]
        btn_container = tk.Frame(center_block, bg="black")
        btn_container.pack()
        for p in places:
            btn = tk.Button(btn_container, text=p, font=("Arial", 25), command=lambda x=p: self.second_departure(x))
            btn.pack(side="left", padx=15, pady=10)
            self.depart_buttons[p] = btn
        self.hide_depart_buttons()

    def destroy_second_screen(self):
        if self.second_window:
            self.second_window.destroy()
            self.second_window = None

    def show_depart_buttons(self):
        for btn in self.depart_buttons.values():
            btn.pack(side="left", padx=15, pady=10)

    def hide_depart_buttons(self):
        for btn in self.depart_buttons.values():
            btn.pack_forget()

    def reset_second_screen(self):
        if self.second_window:
            self.second_label.config(text="Přiložte čip")
            self.hide_depart_buttons()
            self.current_person = None
            self.last_unknown_chip = None
            if self.kiosk_error_job:
                self.root.after_cancel(self.kiosk_error_job)
                self.kiosk_error_job = None
        if self.reset_job:
            self.root.after_cancel(self.reset_job)
            self.reset_job = None

    def start_reset_timer(self):
        if self.reset_job:
            self.root.after_cancel(self.reset_job)
        self.reset_job = self.root.after(20000, self.reset_second_screen)

    def second_departure(self, place):
        if place == "Jiné":
            place = simpledialog.askstring("Odchod", "Kam jde?")
            if not place:
                place = "Neuvedeno"
        self.set_status_external("Nepřítomen", place)

    def set_status_external(self, status, place):
        if not self.current_person:
            return
        now = datetime.now()
        self.data[self.current_person] = {
            "status": status,
            "time": now.strftime("%H:%M:%S"),
            "date": now.strftime("%d.%m.%Y"),
            "place": place
        }
        save_data(self.data)
        save_history(self.data[self.current_person] | {"person": self.current_person})
        self.refresh()
        self.reset_second_screen()

    # ---------- Pomocné hlášky ----------
    def show_temp_message(self, message, duration=2000):
        popup = tk.Toplevel(self.root)
        popup.title("")
        popup.overrideredirect(True)
        popup.configure(bg="#ffffcc", padx=10, pady=5)
        label = tk.Label(popup, text=message, bg="#ffffcc", font=("Arial", 12))
        label.pack()
        x = self.root.winfo_x() + (self.root.winfo_width() // 2) - 150
        y = self.root.winfo_y() + (self.root.winfo_height() // 2) - 50
        popup.geometry(f"+{x}+{y}")
        popup.after(duration, popup.destroy)

    def show_kiosk_error(self, message, reset_after=3000):
        if not self.second_window:
            return
        self.second_label.config(text=message)
        if self.kiosk_error_job:
            self.root.after_cancel(self.kiosk_error_job)
        self.kiosk_error_job = self.root.after(reset_after, self.reset_second_screen)

    # ---------- RFID handling ----------
    def handle_rfid_input(self, event):
        if not self.settings["rfid_enabled"]:
            return
        # Zpracování klávesy Enter (ukončení sekvence)
        if event.keysym == "Return":
            chip_id = self.rfid_buffer.strip()
            self.rfid_buffer = ""
            # Ukončíme skenování
            self._reset_rfid_scanning()
            if chip_id:
                self.process_chip(chip_id)
            return
        # Jinak ukládáme znak do bufferu
        # První znak aktivuje režim skenování (potlačí vkládání do Entry)
        if not self.rfid_scanning:
            self.rfid_scanning = True
        # Reset časovače (prodlužujeme dobu skenování)
        if self.rfid_scanning_timer:
            self.root.after_cancel(self.rfid_scanning_timer)
        self.rfid_scanning_timer = self.root.after(300, self._reset_rfid_scanning)
        self.rfid_buffer += event.char
        # Zrušíme šíření události, aby se znak nepropsal do widgetu (ale stejně validatecommand ho zahodí)
        return "break"

    # ZMĚNA: Rozšířeno o zprávu při příjezdu na kiosku a vynechání hlavní historie
    def process_chip(self, chip_id):
        # Zastavíme skenování
        self._reset_rfid_scanning()
        if not self.settings["rfid_enabled"]:
            return
        chip_id = chip_id.strip().lower()
        # Cooldown
        now_ms = int(datetime.now().timestamp() * 1000)
        if chip_id == self.last_rfid_chip and (now_ms - self.last_rfid_time) < self.rfid_cooldown_ms:
            return
        self.last_rfid_chip = chip_id
        self.last_rfid_time = now_ms

        if chip_id not in chips:
            self.show_temp_message("Čip nenalezen, zkuste čip přiložit znova.\nPokud se chyba objeví znova, zkuste jej přiřadit.", 3000)
            if self.second_window and self.settings["kiosk_enabled"]:
                if self.last_unknown_chip == chip_id:
                    self.show_kiosk_error("Čip nenalezen, kontaktujte vychovatele.", 4000)
                else:
                    self.show_kiosk_error("Chyba čtení čipu, přiložte čip znova!", 3000)
                self.last_unknown_chip = chip_id
            return

        self.last_unknown_chip = None
        person = chips[chip_id]
        current = self.data.get(person, {}).get("status", "Přítomen")
        now = datetime.now()

        if self.second_window and self.settings["kiosk_enabled"]:
            self.current_person = person
            # ZMĚNA: Pokud je osoba na odjezdovce -> příjezd, neukládat do hlavní historie
            if current == "Nepřítomen - odjezdovka":
                self.data[person] = {
                    "status": "Přítomen",
                    "time": now.strftime("%H:%M:%S"),
                    "date": now.strftime("%d.%m.%Y"),
                    "place": "V budově"
                }
                save_data(self.data)
                # Neukládáme do hlavní docházkové historie!
                self._record_transport(person, "Příjezd")
                self.refresh()
                # Zobrazení zprávy na kiosku
                self.second_label.config(text=f"Příjezd zaznamenán {now.strftime('%H:%M:%S')}")
                self.start_reset_timer()
                return
            # Pokud je osoba nepřítomná (normálně), změníme na Přítomen a zaznamenáme příjezd
            if current == "Nepřítomen":
                self.data[person] = {
                    "status": "Přítomen",
                    "time": now.strftime("%H:%M:%S"),
                    "date": now.strftime("%d.%m.%Y"),
                    "place": "V budově"
                }
                save_data(self.data)
                save_history(self.data[person] | {"person": person})  # běžný příjezd do hlavní historie
                self.refresh()
                self.reset_second_screen()
                return
            # Jinak je přítomen → zobraz tlačítka odchodů
            self.second_label.config(text=f"Dobrý den {person}")
            self.hide_depart_buttons()
            self.show_depart_buttons()
            self.start_reset_timer()
            return

        # Fallback (kiosk vypnutý)
        if current == "Přítomen":
            place = simpledialog.askstring("Odchod", "Kam jde?")
            status = "Nepřítomen"
            place_val = place or "Neuvedeno"
        else:  # "Nepřítomen" nebo "Nepřítomen - odjezdovka"
            status = "Přítomen"
            place_val = "V budově"
            # Zaznamenáme příjezd do transportního Excelu (pouze pokud byl na odjezdovce)
            if current == "Nepřítomen - odjezdovka":
                self._record_transport(person, "Příjezd")
        self.data[person] = {
            "status": status,
            "time": now.strftime("%H:%M:%S"),
            "date": now.strftime("%d.%m.%Y"),
            "place": place_val
        }
        save_data(self.data)
        # ZMĚNA: Neukládat do hlavní historie, pokud jde o změnu z odjezdovky
        if current != "Nepřítomen - odjezdovka" or status != "Přítomen":
            save_history(self.data[person] | {"person": person})
        self.refresh()

    # ---------- GUI refresh a ostatní funkce ----------
    def refresh(self):
        self.tree.delete(*self.tree.get_children())
        today = datetime.now().strftime("%d.%m.%Y")
        self.date_label.config(text=f"Datum: {today}")
        present, absent, weekend = 0, 0, 0
        for room in sorted(rooms.keys(), key=lambda x: int(x)):
            self.tree.insert("", "end", values=(f"--- Pokoj {room} ---", "", "", "", ""), tags=("separator",))
            for person in rooms[room]:
                info = self.data.get(person, {})
                status = info.get("status", "Přítomen")
                if status == "Přítomen":
                    present += 1
                    tag = "in"
                elif status == "Nepřítomen - odjezdovka":
                    weekend += 1
                    tag = "weekend"
                else:
                    absent += 1
                    tag = "out"
                self.tree.insert("", "end", values=(
                    room,
                    person,
                    status,
                    f"{info.get('time','-')} ({info.get('date','')})",
                    info.get("place", "-")
                ), tags=(tag,))
        self.stats_label.config(text=f"Přítomní: {present} | Nepřítomní: {absent} | Odjezdovka: {weekend}")

    def search_room(self):
        target = self.search_var.get()
        for item in self.tree.get_children():
            if f"--- Pokoj {target} ---" in str(self.tree.item(item, "values")):
                self.tree.selection_set(item)
                self.tree.see(item)
                break

    def show_menu(self, event):
        self.selected_item = self.tree.identify_row(event.y)
        if self.selected_item:
            self.tree.selection_set(self.selected_item)
            self.menu.post(event.x_root, event.y_root)

    def assign_chip(self):
        values = self.tree.item(self.selected_item, "values")
        if "---" in values[0]:
            return
        person = values[1]
        chip_id = simpledialog.askstring("Čip", "Přilož čip (nebo zadej ID):")
        if not chip_id:
            return
        chip_id = chip_id.strip().lower()
        chips[chip_id] = person
        save_chips(chips)
        messagebox.showinfo("Hotovo", f"Čip přiřazen k {person}")

    def edit_name(self):
        values = self.tree.item(self.selected_item, "values")
        if "---" in values[0]:
            return
        old = values[1]
        new = simpledialog.askstring("Jméno", "Nové jméno:", initialvalue=old)
        if new:
            room = values[0]
            rooms[room].remove(old)
            rooms[room].append(new)
            self.data[new] = self.data.pop(old, {})
            save_rooms(rooms)
            save_data(self.data)
            self.refresh()

    def add_person(self):
        values = self.tree.item(self.selected_item, "values")
        if "---" not in values[0]:
            return
        room = values[0].replace("--- Pokoj ", "").replace(" ---", "")
        name = simpledialog.askstring("Nový člověk", "Jméno:")
        if name:
            rooms[room].append(name)
            save_rooms(rooms)
            self.refresh()

    def delete_person(self):
        values = self.tree.item(self.selected_item, "values")
        if "---" in values[0]:
            return
        person = values[1]
        room = values[0]
        confirm = messagebox.askyesno("Potvrzení", f"Opravdu smazat {person}?")
        if confirm:
            if person in rooms[room]:
                rooms[room].remove(person)
            if person in self.data:
                del self.data[person]
            save_rooms(rooms)
            save_data(self.data)
            self.refresh()

    # ZMĚNA: toggle_status – při návratu z odjezdovky neukládat do hlavní historie
    def toggle_status(self, event):
        item = self.tree.selection()[0]
        values = self.tree.item(item, "values")
        if "---" in values[0]:
            return
        person = values[1]
        current = self.data.get(person, {}).get("status", "Přítomen")
        now = datetime.now()
        if current == "Přítomen":
            place = simpledialog.askstring("Odchod", "Kam jde?")
            status = "Nepřítomen"
            place_val = place or "Neuvedeno"
        elif current == "Nepřítomen":
            status = "Přítomen"
            place_val = "V budově"
        elif current == "Nepřítomen - odjezdovka":
            status = "Přítomen"
            place_val = "V budově"
            # ZMĚNA: Neukládáme do hlavní historie, pouze do transportní
            self._record_transport(person, "Příjezd")
            self.data[person] = {
                "status": status,
                "time": now.strftime("%H:%M:%S"),
                "date": now.strftime("%d.%m.%Y"),
                "place": place_val
            }
            save_data(self.data)
            self.refresh()
            return
        else:
            status = "Přítomen"
            place_val = "V budově"
        self.data[person] = {
            "status": status,
            "time": now.strftime("%H:%M:%S"),
            "date": now.strftime("%d.%m.%Y"),
            "place": place_val
        }
        save_data(self.data)
        save_history(self.data[person] | {"person": person})
        self.refresh()

    # ---------- Funkce pro záznam přeprav (odjezd/příjezd) ----------
    # ZMĚNA: rozšířeno o volitelnou poznámku
    def _record_transport(self, person, typ, note=None):
        """Uloží záznam o odjezdu nebo příjezdu do odjezdovka_history.xlsx.
           Parametr note umožňuje vlastní text (např. místo odjezdu)."""
        now = datetime.now()
        if note is None:
            note = "Odjezdovka" if typ == "Odjezd" else "Návrat z víkendu"
        record = {
            "person": person,
            "date": now.strftime("%d.%m.%Y"),
            "time": now.strftime("%H:%M:%S"),
            "typ": typ,
            "note": note
        }
        save_transport_history(record)

    # ZMĚNA: individuální odjezd – dotaz na místo
    def set_individual_departure_weekend(self):
        """Nastaví vybranou osobu na 'Nepřítomen - odjezdovka' a uloží odjezd s místem."""
        if not hasattr(self, 'selected_item') or not self.selected_item:
            messagebox.showwarning("Chyba", "Nejprve vyberte osobu.")
            return
        values = self.tree.item(self.selected_item, "values")
        if "---" in values[0]:
            messagebox.showwarning("Chyba", "Nelze označit hlavičku pokoje.")
            return
        person = values[1]
        # Dotaz na místo odjezdu
        place = simpledialog.askstring("Místo odjezdu", "Kam odjíždí? (např. domů, výlet, ...)", initialvalue="domů")
        if not place:
            place = "domů"
        now = datetime.now()
        self.data[person] = {
            "status": "Nepřítomen - odjezdovka",
            "time": now.strftime("%H:%M:%S"),
            "date": now.strftime("%d.%m.%Y"),
            "place": f"Odjezd - {place}"
        }
        save_data(self.data)
        # Uložíme do přepravní historie s poznámkou = místo odjezdu
        self._record_transport(person, "Odjezd", note=place)
        self.refresh()
        messagebox.showinfo("Hotovo", f"{person} byl označen jako odjezdovka (víkend) - {place}.")

    # ZMĚNA: individuální návrat – neukládá do hlavní historie
    def set_individual_return_weekend(self):
        """Nastaví vybranou osobu na 'Přítomen' – ukládá pouze do přepravní historie (ne hlavní)."""
        if not hasattr(self, 'selected_item') or not self.selected_item:
            messagebox.showwarning("Chyba", "Nejprve vyberte osobu.")
            return
        values = self.tree.item(self.selected_item, "values")
        if "---" in values[0]:
            messagebox.showwarning("Chyba", "Nelze označit hlavičku pokoje.")
            return
        person = values[1]
        now = datetime.now()
        self.data[person] = {
            "status": "Přítomen",
            "time": now.strftime("%H:%M:%S"),
            "date": now.strftime("%d.%m.%Y"),
            "place": "V budově"
        }
        save_data(self.data)
        # Neukládáme do hlavní historie (save_history se nevolá)
        self._record_transport(person, "Příjezd")
        self.refresh()
        messagebox.showinfo("Hotovo", f"{person} byl označen jako přítomen (návrat z víkendu).")

    # ZMĚNA: hromadný odjezd – dotaz na místo
    def bulk_departure_weekend(self):
        """Nastaví všechny přítomné na odjezdovku s jedním společným místem."""
        confirm = messagebox.askyesno("Potvrzení", "Opravdu chcete označit VŠECHNY PŘÍTOMNÉ jako odjezdovka (víkend)?\nTuto akci nelze snadno vrátit.")
        if not confirm:
            return
        place = simpledialog.askstring("Místo hromadného odjezdu", "Kam všichni odjíždějí? (např. domů, výlet, ...)", initialvalue="domů")
        if not place:
            place = "domů"
        now = datetime.now()
        changed = 0
        for room, persons in rooms.items():
            for person in persons:
                current = self.data.get(person, {}).get("status", "Přítomen")
                if current == "Přítomen":
                    self.data[person] = {
                        "status": "Nepřítomen - odjezdovka",
                        "time": now.strftime("%H:%M:%S"),
                        "date": now.strftime("%d.%m.%Y"),
                        "place": f"Odjezd - {place}"
                    }
                    self._record_transport(person, "Odjezd", note=place)
                    changed += 1
        save_data(self.data)
        self.refresh()
        messagebox.showinfo("Hotovo", f"Celkem {changed} osob bylo označeno jako odjezdovka (místo: {place}).")

    # ZMĚNA: hromadný návrat – neukládá do hlavní historie
    def bulk_return_weekend(self):
        """Nastaví všechny na odjezdovce na přítomné – ukládá pouze do přepravní historie."""
        confirm = messagebox.askyesno("Potvrzení", "Opravdu chcete označit VŠECHNY na odjezdovce jako přítomné (návrat z víkendu)?")
        if not confirm:
            return
        now = datetime.now()
        changed = 0
        for room, persons in rooms.items():
            for person in persons:
                current = self.data.get(person, {}).get("status", "")
                if current == "Nepřítomen - odjezdovka":
                    self.data[person] = {
                        "status": "Přítomen",
                        "time": now.strftime("%H:%M:%S"),
                        "date": now.strftime("%d.%m.%Y"),
                        "place": "V budově"
                    }
                    # save_history se NEPOUŽÍVÁ
                    self._record_transport(person, "Příjezd")
                    changed += 1
        save_data(self.data)
        self.refresh()
        messagebox.showinfo("Hotovo", f"Celkem {changed} osob bylo označeno jako přítomných (návrat).")

    # ---------- Nastavení ----------
    def open_settings(self):
        dialog = tk.Toplevel(self.root)
        dialog.title("Nastavení")
        dialog.geometry("400x250")
        dialog.resizable(False, False)
        dialog.transient(self.root)
        dialog.grab_set()
        rfid_var = tk.BooleanVar(value=self.settings["rfid_enabled"])
        kiosk_var = tk.BooleanVar(value=self.settings["kiosk_enabled"])
        ttk.Label(dialog, text="Nastavení aplikace", font=("Arial", 14)).pack(pady=10)
        cb_rfid = ttk.Checkbutton(dialog, text="Povolit čtečku RFID", variable=rfid_var)
        cb_rfid.pack(pady=5, anchor="w", padx=20)
        cb_kiosk = ttk.Checkbutton(dialog, text="Povolit kioskový režim (druhá obrazovka)", variable=kiosk_var)
        cb_kiosk.pack(pady=5, anchor="w", padx=20)
        ttk.Label(dialog, text="Změny se projeví okamžitě.", foreground="gray").pack(pady=10)
        def save_and_apply():
            new_rfid = rfid_var.get()
            new_kiosk = kiosk_var.get()
            self.settings["rfid_enabled"] = new_rfid
            self.settings["kiosk_enabled"] = new_kiosk
            save_settings(self.settings)
            if not new_kiosk and self.second_window:
                self.destroy_second_screen()
            elif new_kiosk and not self.second_window:
                self.setup_second_screen()
            if not new_rfid:
                self.rfid_buffer = ""
            dialog.destroy()
            messagebox.showinfo("Nastavení", "Změny byly uloženy a aplikovány.")
        btn_save = ttk.Button(dialog, text="Uložit a zavřít", command=save_and_apply)
        btn_save.pack(pady=10)

    def show_help(self):
        help_text = (
            "Nápověda k docházkovému systému\n\n"
            "• Dvojklik na osobu změní její stav (přítomen/nepřítomen).\n"
            "• Pravým tlačítkem na osobě lze upravit jméno, smazat nebo přiřadit čip.\n"
            "• Kliknutím pravým tlačítkem na řádek s pokojem lze přidat novou osobu.\n"
            "• Čip přiložte ke čtečce – pokud je RFID povoleno, systém ho zpracuje.\n"
            "• Historie docházky (Přítomen/Nepřítomen) se ukládá do souboru 'dochazka_history.xlsx'.\n\n"
            "• Kniha návštěv: přidávejte ručně návštěvy (Učebna PC s číslem pokoje a počítače, Návštěva s volitelným číslem pokoje a volitelným místem).\n"
            "  Dvojklikem nebo pravým tlačítkem na řádku zapíšete odchod a uložíte do samostatného Excelu 'navsteva_history.xlsx'.\n\n"
            "• Odjezdovka/Návrat z víkendu – pravým tlačítkem na osobě nastavíte stav 'Nepřítomen - odjezdovka' (nejprve zadáte místo odjezdu).\n"
            "  Osoba s tímto stavem na kiosku neuvidí tlačítka odchodů (pouze se po přiložení čipu vrátí do režimu přítomen a zobrazí se zpráva 'Příjezd zaznamenán').\n"
            "  V menu 'Odjezdy' naleznete hromadné nastavení odjezdovky pro všechny přítomné (s možností zadat společné místo) a hromadný návrat.\n"
            "  **Veškeré odjezdy a příjezdy (víkendové přenosy) se zaznamenávají pouze do samostatného Excel souboru 'odjezdovka_history.xlsx'** (žlutá = odjezd, zelená = příjezd).\n"
            "  Do hlavní docházkové historie se tyto záznamy neukládají, aby v ní nebyl nepořádek.\n\n"
            "• Všechna pomocná data jsou ukládána do složky 'source' (kromě Excel historie).\n"
            "• Nemanipulujte prosím ručně s JSON soubory ve složce 'source'."
        )
        messagebox.showinfo("Nápověda", help_text)

    def show_about(self):
        about_text = (
            "Docházkový systém SLŠ Hranice\n"
            f"Verze {APP_VERSION}\n"
            "Autor: ProstyCZ\n"
            "Rok: 2026\n\n"
            "Systém slouží k evidenci přítomnosti osob pomocí RFID čipů.\n"
            "Kniha návštěv umožňuje ruční záznam návštěv (Učebna PC, Návštěva).\n"
            "Data jsou ukládána lokálně do JSON a Excel souborů.\n"
            "Pomocné soubory jsou ve složce 'source'.\n"
            "Kontakt: matej.trchalik@seznam.cz, https://prostycz.cz"
        )
        messagebox.showinfo("O programu", about_text)

# ---------- Run ----------
if __name__ == "__main__":
    splash = show_splash()
    splash.wait_window()
    root = tk.Tk()
    app = App(root)
    root.mainloop()