# pip3 install pygetwindow pyautogui python-socketio pillow requests websocket-client
# python3 -m pip install pyautogui pygetwindow python-socketio pillow requests websocket-client
# winget install ffmpeg
# Verificar :   python3 -c "import pyautogui, socketio, PIL, requests, websocket; print('✅ Todas as dependências estão instaladas')"
# Testar se a porta 3000 está acessível
# curl -v https://173.249.31.98:3000
# cd /iot
# 1. Se não existir, instalar
# curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py
# python get-pip.py

# 2. Instalar as dependências do projeto
# Instalar python via homebrew // se nao tiver instalado
# brew install python3
# O pip será instalado automaticamente

# se nao tiver instalado :   /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
# ls -la /opt/homebrew/bin/brew

# echo 'eval "$(/opt/homebrew/bin/brew shellenv)"' >> ~/.zshrc eval "$(/opt/homebrew/bin/brew shellenv)"

# brew install ffmpeg // instalar no make
# winget install ffmpeg  // instalar no windows

# python maquina.py 1..2..3. quantas forem
# instalar os arquivos acima 3 atraz ai burro
# testar porta  :  curl -v https://173.249.31.98:3000  /  https://www.xkbitcoin.com/iot/  https://www.xkbitcoin.com:3000

# Testar se o caminho /iot/ está funcionando
# curl -v https://www.xkbitcoin.com/iot/

# Testar se o domínio com porta 3000 está funcionando
# curl -v https://www.xkbitcoin.com:3000


#!/usr/bin/env python3
import pyautogui
pyautogui.FAILSAFE = False
import sys
import subprocess
import threading
import socketio
import base64
import io
import time
import platform
import os
import requests
import json
import traceback
import serial
from PIL import Image  # Adicionado para o redimensionamento inteligente

# Tenta importar a biblioteca de teclado (se disponível)
try:
    import keyboard
    TECLADO_DISPONIVEL = True
except ImportError:
    TECLADO_DISPONIVEL = False
    print('⚠️ Biblioteca "keyboard" não instalada. Para capturar teclas, instale: pip install keyboard')

# ==========================================
# CONFIGURAÇÕES
# ==========================================
if len(sys.argv) < 2:
    sys.exit(1)

MAQUINA_ID = sys.argv[1]
SERVER_URL = 'https://xkbitcoin.com'
FPS = 7

# --- OTIMIZAÇÃO DE VÍDEO ---
QUALIDADE_JPEG = 35  
LARGURA_ALVO = 850   

MAPA_TECLAS = {
    "a": "btn_aposta",
    "s": "btn_jogar",
    "n": "btn_ver_cartoes",
    "c": "btn_cartelas",
    "e": "btn_extra",
    "h": "btn_ajuda",
}
COMANDO_PARA_STRING = {
    "btn_aposta": "a",
    "btn_jogar": "s",
    "btn_ver_cartoes": "n",
    "btn_cartelas": "c",
    "btn_extra": "e",
    "btn_ajuda": "h",
}

# Variáveis globais
processo_audio = None
conectado_com_sucesso = False
ultima_comunicacao = time.time()
threads_ativas = []

# ==========================================
# CONFIGURAÇÃO DO ARDUINO / ESP32 (SERIAL)
# ==========================================
PORTA_SERIAL = 'COM3' 
BAUD_RATE = 115200
arduino = None

try:
    arduino = serial.Serial(PORTA_SERIAL, BAUD_RATE, timeout=1)
    print(f'🔌 Conectado ao hardware PS2 na porta {PORTA_SERIAL}')
except Exception as e:
    print(f'⚠️ Aviso: Hardware PS2 não encontrado na porta {PORTA_SERIAL}. Erro: {e}')

# ==========================================
# DETECTAR SISTEMA OPERACIONAL
# ==========================================
SISTEMA = platform.system()
print(f'🖥️ Sistema detectado: {SISTEMA}')

# ==========================================
# BUSCAR TOKEN DA MÁQUINA (via API pública)
# ==========================================
def obter_token(maquina_id):
    try:
        url_token = f'{SERVER_URL}:3000/api/maquina/token/{maquina_id}'
        response = requests.get(url_token, timeout=10)
        print(f'🔍 Status nuvem: {response.status_code}')
        
        if response.status_code == 200:
            token_data = response.json()
            return token_data.get('token')
        else:
            print(f'❌ Erro {response.status_code} ao buscar token: {response.text}')
            return None
    except Exception as e:
        print(f'❌ Erro de requisição ao buscar na nuvem: {e}')
        return None

TOKEN = obter_token(MAQUINA_ID)
if not TOKEN:
    print(f'❌ maquina  não encontrado {MAQUINA_ID}. Abortando.')
    sys.exit(1)

print(f'🔑 Maquina obtido com sucesso: {TOKEN[:15]}...')

# ==========================================
# SOCKET.IO
# ==========================================
sio = socketio.Client(logger=False, engineio_logger=False)

@sio.event
def connect():
    global conectado_com_sucesso
    print(f'✅ Acesso a nuvem conectado! (Máquina {MAQUINA_ID})')
    sio.emit('entrar_maquina', {'maquina_id': MAQUINA_ID}, callback=confirmar_conexao)

def confirmar_conexao(resposta):
    global conectado_com_sucesso
    if resposta and resposta.get('status') == 'success':
        conectado_com_sucesso = True
        print(f'✅ Nuvem máquina: {resposta.get("message")}')
    else:
        print(f'❌ Servidor entrada negada da máquina: {resposta.get("message") if resposta else "Sem resposta"}')

@sio.event
def connect_error(data):
    print(f'❌ Erro de conexão: {data}')

@sio.event
def disconnect():
    print(f'🔴 Desconectado da nuvem (Máquina {MAQUINA_ID})')

# ==========================================
# EVENTOS RECEBIDOS DO SERVIDOR
# ==========================================
@sio.on('executar_clique')
def executar_clique(data):
    try:
        x = data['x']
        y = data['y']
        largura, altura = pyautogui.size()
        x_real = int(x * largura)
        y_real = int(y * altura)
        print(f'🖱️ Clique em ({x_real}, {y_real}) na Máquina {MAQUINA_ID}')
        pyautogui.click(x_real, y_real)
    except Exception as e:
        print(f'❌ Erro ao executar clique: {e}')

@sio.on('executar_js')
def executar_js(data):
    try:
        funcao = data['funcao']
        args = data['args']
        comando_original = args[1] if len(args) > 1 else 'sem comando'
        print(f'🔘 Botão clicado no navegador: {comando_original.upper()}')
        
    except Exception as e:
        print(f'❌ Erro ao executar JS: {e}')

@sio.on('acionar_botao_maquina')
def acionar_botao_maquina(data):
    try:
        comando = data['tecla']
        print(f'📩 [RECEBIDO] acionar_botao_maquina: tecla={comando}')
        print(f'🕹️ Acionando botão físico: {comando}')
        
        if arduino and arduino.is_open:
            arduino.write(comando.encode('utf-8'))
            print(f'✅ [ENVIADO] Comando "{comando}" enviado via Serial para o ESP32')
            
            time.sleep(0.2)
            if arduino.in_waiting > 0:
                resposta = arduino.readline().decode().strip()
                print(f'📩 [RESPOSTA] ESP32 respondeu: "{resposta}"')
                if resposta == "OK":
                    print(f'✅ ESP32 confirmou execução do comando: {comando}')
                else:
                    print(f'⚠️ ESP32 respondeu algo diferente: {resposta}')
            else:
                print(f'⏳ Aguardando resposta do ESP32 para o comando: {comando}')
        else:
            print('⚠️ Comando não enviado: Arduino não conectado via Serial.')
            
    except Exception as e:
        print(f'❌ Erro ao acionar botão físico: {e}')

# ==========================================
# CAPTURA DE VÍDEO COMPACTADA COM REDIMENSIONAMENTO
# ==========================================
def trazer_navegador_para_frente():
    try:
        if SISTEMA == 'Darwin':
            script = '''
            tell application "System Events"
                set frontmost to true
                set visible of process "Safari" to true
                set visible of process "Google Chrome" to true
                set frontmost of process "Safari" to true
                set frontmost of process "Google Chrome" to true
            end tell
            '''
            subprocess.run(['osascript', '-e', script], capture_output=True)
            time.sleep(0.5)
        elif SISTEMA == 'Windows':
            pass
        elif SISTEMA == 'Linux':
            subprocess.run(['wmctrl', '-a', 'SalaVIP'], capture_output=True)
            subprocess.run(['wmctrl', '-a', 'Google Chrome'], capture_output=True)
            subprocess.run(['wmctrl', '-a', 'Firefox'], capture_output=True)
            time.sleep(0.2)
    except Exception as e:
        print(f'⚠️ Não foi possível trazer o navegador: {e}')

def capturar_video():
    global ultima_comunicacao
    print(f'📸 Iniciando captura de vídeo otimizada da Máquina {MAQUINA_ID}...')
    trazer_navegador_para_frente()
    while True:
        try:
            # 1. Captura em Alta Resolução original
            screenshot = pyautogui.screenshot()
            screenshot = screenshot.convert('RGB')
            
            # 2. Redimensiona mantendo a proporção de tela
            largura_orig, altura_orig = screenshot.size
            if largura_orig > LARGURA_ALVO:
                proporcao = altura_orig / largura_orig
                altura_alvo = int(LARGURA_ALVO * proporcao)
                # O filtro BILINEAR é rápido e mantém textos fáceis de ler ao encolher/esticar
                screenshot = screenshot.resize((LARGURA_ALVO, altura_alvo), Image.Resampling.BILINEAR)
            
            # 3. Compactação em JPEG e codificação Base64
            buffer = io.BytesIO()
            screenshot.save(buffer, format='JPEG', quality=QUALIDADE_JPEG)
            img_base64 = base64.b64encode(buffer.getvalue()).decode()
            
            if sio.connected:
                sio.emit('tela_maquina', {
                    'maquina_id': MAQUINA_ID,
                    'imagem': img_base64
                })
                ultima_comunicacao = time.time()
            time.sleep(1 / FPS)
        except Exception as e:
            print(f'❌ Erro no vídeo (Máquina {MAQUINA_ID}):', e)
            time.sleep(1)

# ==========================================
# CAPTURA DE ÁUDIO (FFmpeg)
# ==========================================
def capturar_audio():
    global processo_audio
    ffmpeg_path = 'ffmpeg'
    try:
        subprocess.run([ffmpeg_path, '-version'], capture_output=True, check=True)
    except:
        print('❌ FFmpeg não encontrado. Instale o FFmpeg para usar áudio.')
        return
    
    if SISTEMA == 'Darwin':
        cmd = [ffmpeg_path, '-f', 'avfoundation', '-i', ':0', '-c:a', 'aac', '-b:a', '128k', '-f', 'mp4', '-']
    elif SISTEMA == 'Windows':
        cmd = [ffmpeg_path, '-f', 'dshow', '-i', 'audio="Stereo Mix"', '-c:a', 'aac', '-b:a', '128k', '-f', 'mp4', '-']
    elif SISTEMA == 'Linux':
        cmd = [ffmpeg_path, '-f', 'alsa', '-i', 'default', '-c:a', 'aac', '-b:a', '128k', '-f', 'mp4', '-']
    else:
        return
    
    processo_audio = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL)
    while True:
        try:
            chunk = processo_audio.stdout.read(4096)
            if not chunk:
                break
            chunk_base64 = base64.b64encode(chunk).decode()
            if sio.connected:
                sio.emit('stream_audio_video', {
                    'maquina_id': MAQUINA_ID,
                    'chunk': chunk_base64
                })
        except Exception as e:
            print(f'❌ Erro no áudio (Máquina {MAQUINA_ID}):', e)
            time.sleep(1)

# ==========================================
# CAPTURA DE TECLAS DO TECLADO
# ==========================================
def capturar_teclas():
    """Captura teclas do teclado e envia comandos via Socket.IO"""
    if not TECLADO_DISPONIVEL:
        print('⚠️ Biblioteca "keyboard" não disponível. Captura de teclas desativada.')
        return
    
    def ao_pressionar(e):
        if e.event_type == 'down':
            tecla = e.name.lower()
            if tecla in MAPA_TECLAS:
                comando = MAPA_TECLAS[tecla]
                print(f'⌨️ Tecla {tecla} pressionada -> {comando}')
                if sio.connected:
                    sio.emit('executar_js', {
                        'funcao': 'ClickBtn',
                        'args': [MAQUINA_ID, comando]
                    })
    
    keyboard.hook(ao_pressionar)
    keyboard.wait()

# ==========================================
# INICIAR THREADS E CONEXÃO
# ==========================================
if __name__ == '__main__':
    try:
        print(f'🔄 Tentando conectar a nuvem...')
        sio.connect(f'{SERVER_URL}:3000', auth={'token': TOKEN})
        
        # Inicia as threads
        t_video = threading.Thread(target=capturar_video, daemon=True)
        t_video.start()
        threads_ativas.append(t_video)
        
        t_audio = threading.Thread(target=capturar_audio, daemon=True)
        t_audio.start()
        threads_ativas.append(t_audio)
        
        t_teclas = threading.Thread(target=capturar_teclas, daemon=True)
        t_teclas.start()
        threads_ativas.append(t_teclas)
        
        print(f'✅ Máquina {MAQUINA_ID} pronta para operação!')
        sio.wait()
        
    except (KeyboardInterrupt, SystemExit):
        print("👋 Fechando o sistema de streaming...")
        if sio.connected:
            sio.disconnect()
        if processo_audio:
            processo_audio.terminate()
        if arduino and arduino.is_open:
            arduino.close()
        sys.exit(0)
    except Exception as e:
        print(f'❌ Erro fatal: {e}')
        traceback.print_exc()
        sys.exit(1)