import socket
import threading
import tkinter as tk
from tkinter import messagebox, ttk
import subprocess
import re
import platform
from concurrent.futures import ThreadPoolExecutor

stop_scanning = False


def get_local_ip():
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    try:
        s.connect(('8.8.8.8', 80))
        ip = s.getsockname()[0]
    except:
        ip = '127.0.0.1'
    finally:
        s.close()
    return ip


def get_subnet(ip):
    return '.'.join(ip.split('.')[:3]) + '.'


def ping_ip(ip):
    try:
        param = '-n' if platform.system().lower() == 'windows' else '-c'
        command = ['ping', param, '1', ip]
        result = subprocess.run(
            command,
            stdout=subprocess.PIPE,
            stderr=subprocess.DEVNULL,
            creationflags=subprocess.CREATE_NO_WINDOW if platform.system().lower() == 'windows' else 0,
            text=True
        )
        return 'TTL=' in result.stdout
    except Exception:
        return False


def get_hostname(ip):
    try:
        return socket.gethostbyaddr(ip)[0]
    except:
        return "brak nazwy hosta"


class NetworkScannerApp:
    def __init__(self, root):
        self.root = root
        self.root.title("Skaner urządzeń w sieci LAN")
        self.root.geometry("500x350")

        button_frame = tk.Frame(root)
        button_frame.pack(pady=5)

        self.scan_button = tk.Button(button_frame, text="Skanuj sieć", command=self.start_scan, width=15)
        self.scan_button.pack(side=tk.LEFT, padx=5)

        self.stop_button = tk.Button(button_frame, text="Zatrzymaj skanowanie", command=self.stop_scan, state=tk.DISABLED, width=20)
        self.stop_button.pack(side=tk.LEFT, padx=5)

        self.manage_button = tk.Button(button_frame, text="Zarządzaj urządzeniem", command=self.open_manage_window)
        self.manage_button.pack(side=tk.LEFT, padx=5)

        self.listbox = tk.Listbox(root, width=80, height=20)
        self.listbox.pack(pady=10)

        self.scanning_thread = None
        self.devices = []

    def log(self, text):
        self.listbox.insert(tk.END, text)
        self.listbox.see(tk.END)

    def start_scan(self):
        global stop_scanning
        stop_scanning = False
        self.scan_button.config(state=tk.DISABLED)
        self.stop_button.config(state=tk.NORMAL)
        self.listbox.delete(0, tk.END)
        self.devices.clear()
        self.scanning_thread = threading.Thread(target=self.scan_network)
        self.scanning_thread.start()

    def stop_scan(self):
        global stop_scanning
        stop_scanning = True
        self.stop_button.config(state=tk.DISABLED)
        self.scan_button.config(state=tk.NORMAL)
        self.log("Skanowanie zostało zatrzymane przez użytkownika.")

    def scan_network(self):
        local_ip = get_local_ip()
        subnet = get_subnet(local_ip)
        self.log(f"Twoje IP: {local_ip}")
        self.log(f"Skanuję podsieć: {subnet}0/24")

        def scan_ip(i):
            if stop_scanning:
                return
            ip = subnet + str(i)
            if ping_ip(ip):
                hostname = get_hostname(ip)
                entry = f"{ip} ({hostname})"
                self.devices.append((ip, hostname))
                self.root.after(0, lambda: self.log(f"Znaleziono: {entry}"))

        with ThreadPoolExecutor(max_workers=50) as executor:
            executor.map(scan_ip, range(1, 255))

        if not self.devices:
            self.log("Nie znaleziono urządzeń w sieci.")

        self.stop_button.config(state=tk.DISABLED)
        self.scan_button.config(state=tk.NORMAL)

    def open_manage_window(self):
        self.manage_win = tk.Toplevel(self.root)
        self.manage_win.title("Zarządzanie urządzeniem")
        self.manage_win.geometry("400x250")  # trochę wyższe okno

        label_ip = tk.Label(self.manage_win, text="Adres IP urządzenia:")
        label_ip.pack(pady=5)

        self.ip_entry = tk.Entry(self.manage_win, width=30)
        self.ip_entry.pack(pady=5)

        label_user = tk.Label(self.manage_win, text="Nazwa użytkownika:")
        label_user.pack(pady=5)

        self.user_entry = tk.Entry(self.manage_win, width=30)
        self.user_entry.pack(pady=5)

        label_pass = tk.Label(self.manage_win, text="Hasło (opcjonalne):")
        label_pass.pack(pady=5)

        self.pass_entry = tk.Entry(self.manage_win, show='*', width=30)
        self.pass_entry.pack(pady=5)

        self.action_var = tk.StringVar()
        self.action_combobox = ttk.Combobox(self.manage_win, textvariable=self.action_var, state="readonly", width=34)
        self.action_combobox['values'] = ("wyłącz urządzenie", "uruchom ponownie urządzenie")
        self.action_combobox.pack(pady=10)

        ok_button = tk.Button(self.manage_win, text="OK", command=self.execute_action)
        ok_button.pack(pady=10)

    def execute_action(self):
        ip = self.ip_entry.get().strip()
        username = self.user_entry.get().strip()
        password = self.pass_entry.get().strip()
        action = self.action_var.get()

        if not re.match(r"^\d{1,3}(\.\d{1,3}){3}$", ip):
            messagebox.showerror("Błąd", "Niepoprawny adres IP.")
            return

        if action == "wyłącz urządzenie":
            shutdown_cmd = f"shutdown /m \\\\{ip} /s /t 0"
            action_str = "wyłączone"
        elif action == "uruchom ponownie urządzenie":
            shutdown_cmd = f"shutdown /m \\\\{ip} /r /t 0"
            action_str = "zrestartowane"
        else:
            messagebox.showerror("Błąd", "Nieznana akcja.")
            return

        try:
            # Jeśli podano login (i opcjonalnie hasło), łączymy się przez net use najpierw
            if username:
                # Jeśli hasło jest puste, to podajemy pusty ciąg
                password_part = password if password else ""
                net_use_cmd = f'net use \\\\{ip} /user:{username} {password_part}'
                net_use_result = subprocess.run(net_use_cmd, shell=True, capture_output=True, text=True)
                if net_use_result.returncode != 0:
                    err = net_use_result.stderr if net_use_result.stderr else net_use_result.stdout
                    messagebox.showerror("Błąd", f"Błąd logowania:\n{err}")
                    return

            # Wykonanie polecenia shutdown
            result = subprocess.run(shutdown_cmd, shell=True, capture_output=True, text=True)

            # Odłączamy udostępnienie (net use /delete)
            if username:
                subprocess.run(f'net use \\\\{ip} /delete', shell=True, capture_output=True, text=True)

            if result.returncode == 0:
                messagebox.showinfo("Sukces", f"Urządzenie {ip} zostało {action_str}.")
                self.manage_win.destroy()
            else:
                err = result.stderr if result.stderr else result.stdout if result.stdout else "(brak danych)"
                messagebox.showerror("Błąd", f"Błąd podczas wykonywania polecenia:\n{err}")

        except Exception as e:
            messagebox.showerror("Błąd", f"Wystąpił wyjątek:\n{e}")


if __name__ == "__main__":
    root = tk.Tk()
    app = NetworkScannerApp(root)
    root.mainloop()
