#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""
Client WebSocket semplificato per ricevere stream binario da TrueRNG remoto.

- Inserisci manualmente l'ID alfanumerico nella variabile 'codiceaccesso'.
- possibile inserire il proprio codiceaccesso ricevuto facendo la transazione con POLYGON.
- Si connette al websoscket
- Invia l'ID per l'autenticazione.
- Attende risposta testuale dal server:
    - "accettato"  -> inizia a ricevere stream binario
    - "esurito"    -> termina con messaggio
    - "codice errato" -> termina con messaggio
- Riceve dati binari e li salva su 'stream.bin'
  finché non premi 's' o il server si interrompe.

Dipendenze: websockets  (pip install websockets)
"""

import asyncio
import time
import threading
import requests
from pathlib import Path

# =======================
# CONFIGURAZIONE FISSA
# =======================
codiceaccesso = "asdfxqtm606"  # <-- modifica direttamente qui il codiceaccesso ricevuto
PORTA   = 4545
OUTFILE = Path("stream.bin")

# URL del tuo PHP
PHP_URL = "http://www.moscio.it/utility_trng.php"

def get_host_ip():
    try:
        r = requests.post(PHP_URL, data={"type": "ip"}, timeout=10)
        print(repr(r.text))
        r.raise_for_status()
        ip = r.text.strip()
        print(f"[INFO] IP ricevuto dal PHP: {ip}")
        return ip
    except Exception as e:
        print(f"[ERRORE] Impossibile ottenere HOST_IP dal PHP: {e}")
        return None

HOST_IP = get_host_ip()

if not HOST_IP:
    print("[ERRORE] HOST_IP non valido. Uscita.")
    exit(1)

# -----------------------
# Rilevamento tasto 's'
# -----------------------
def start_keypress_watcher(stop_event: threading.Event):
    def _watch():
        try:
            import msvcrt
            while not stop_event.is_set():
                if msvcrt.kbhit():
                    ch = msvcrt.getwch()
                    if ch.lower() == 's':
                        stop_event.set()
                        break
                time.sleep(0.05)
        except ImportError:
            import sys, select, termios, tty
            fd = sys.stdin.fileno()
            old_settings = termios.tcgetattr(fd)
            try:
                tty.setcbreak(fd)
                while not stop_event.is_set():
                    rlist, _, _ = select.select([sys.stdin], [], [], 0.1)
                    if rlist:
                        ch = sys.stdin.read(1)
                        if ch.lower() == 's':
                            stop_event.set()
                            break
            finally:
                termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
    t = threading.Thread(target=_watch, daemon=True)
    t.start()
    return t

# -----------------------
# Client principale
# -----------------------
async def run_client():
    import websockets
    from websockets.exceptions import ConnectionClosed

    uri = f"ws://{HOST_IP}:{PORTA}"
    print(f"[INFO] Connessione a {uri} ...")
    stop_event = threading.Event()
    start_keypress_watcher(stop_event)
    print("[INFO] Premi 's' per interrompere la ricezione.\n")

    total_bytes = 0
    last_print = time.time()

    with OUTFILE.open("ab") as f:
        try:
            async with websockets.connect(uri, max_size=None) as ws:
                # invio autenticazione
                await ws.send(codiceaccesso)
                print(f"[TX] -> {codiceaccesso}")

                # attendo risposta testuale
                resp = await ws.recv()
                print(f"[RX] <- {resp}")

                if resp.lower() == "codice errato":
                    print("[ERRORE] Codice di accesso errato.")
                    return
                elif resp.lower() == "esurito":
                    print("[INFO] Codice valido ma esaurito.")
                    return
                elif resp.lower() != "accettato":
                    print("[ERRORE] Risposta inattesa dal server.")
                    return

                print("[OK] Autenticazione accettata. Inizio ricezione stream binario...")
                print(f"[INFO] Scrittura su file: {OUTFILE.resolve()}\n")

                # ciclo di ricezione
                while not stop_event.is_set():
                    try:
                        msg = await ws.recv()
                    except ConnectionClosed as e:
                        print(f"\n[INFO] Connessione chiusa dal server ({e.code}: {e.reason}).")
                        break

                    if isinstance(msg, (bytes, bytearray)):
                        f.write(msg)
                        total_bytes += len(msg)
                        if total_bytes % (64*1024) == 0:
                            f.flush()
                    else:
                        print(f"[RX testo inatteso] {msg}")

                    now = time.time()
                    if now - last_print >= 1.0:
                        print(f"[PROGRESSO] {total_bytes} byte ricevuti...", end="\r", flush=True)
                        last_print = now

                if stop_event.is_set():
                    print("\n[INFO] Interruzione richiesta dall'utente ('s').")
                print(f"[INFO] Totale ricevuto: {total_bytes} byte.")

                try:
                    await ws.close()
                except Exception:
                    pass

        except OSError as e:
            print(f"[ERRORE] Impossibile connettersi: {e}")

def main():
    asyncio.run(run_client())

if __name__ == "__main__":
    main()