# app_shell.py
# Versão: 5.5 (Build com Reset de Hardware via CDLL)
# Data: 18/08/2025 (São Paulo, Brasil)
# Descrição: Versão final com reset de hardware usando ctypes.CDLL para compatibilidade.

import tkinter as tk
from tkinter import ttk, messagebox
import os
import traceback
import time
import ctypes
from tkinter import PhotoImage
from PIL import Image, ImageTk
import json

# Módulos da Aplicação
from .faq_screen import FaqScreen
from .license_module import LicenseScreen, LicenseManager, HardwareManager
from .antenna_module import AntennaModule
from .noise_module import NoiseModule
from .fastsurance_module import RFIDApp
from .threshold_module import ThresholdModule
from .fast_threshold import QuickThresholdModule
from .populacao_module import PopulacaoModule
from .simulador_modulo import SimuladorModule
from .power_constant_module import PowerConstantModule
from .rssi_power_module import RSSIPowerModule
from .Fast_orientation import OrientationTester
from src.core.hardware_controller import get_hardware_controller
from src.core.port_manager import get_port_manager
from . import port_manager
from .i18n import get_translator, t

try:
    from PIL import Image, ImageTk
except ImportError:
    # Importa traduções mesmo em caso de erro
    try:
        from .i18n import get_translator, t
        translator = get_translator()
        messagebox.showerror(t('app_shell.error_missing_library'), t('app_shell.error_missing_library_msg'))
    except:
        messagebox.showerror("Missing Library", "The 'Pillow' library is required. Install with: pip install Pillow")
    import sys
    sys.exit()

# Importa configuração de versão centralizada
try:
    from .version_config import get_software_info
    SOFTWARE_INFO = get_software_info()
    APP_VERSION = SOFTWARE_INFO['version']
except ImportError:
    # Fallback caso o arquivo não exista
    APP_VERSION = "2.0.0"
    SOFTWARE_INFO = {'software': '2.0.0', 'name': 'FastChecker II', 'version': '2.0.0'}

COLOR_BG, COLOR_HEADER_BG, COLOR_HEADER_FG = "#F5F5F5", "#212121", "#FFFFFF"
import os
LICENSE_DB_FILE = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))), "licenses.json")
LICENSE_CONFIG_FILE = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))), "license_config.json")

class AppShell(tk.Frame):
    def __init__(self, master=None):
        super().__init__(master, bg=COLOR_BG)
        self.master = master
        self.master.title(f"Fastchecker-02 v{APP_VERSION}")
        self.master.geometry("1100x750")
        self.master.minsize(1000, 700)
        self.pack(fill="both", expand=True)

        # Descoberta otimizada: primeiro tenta sem refresh, depois com refresh se necessário
        self.com_port = port_manager.get_com_port_number(refresh=False)
        if self.com_port is None:
            self.com_port = port_manager.get_com_port_number(refresh=True)
        if self.com_port is None:
            # Fallback: varrer portas usando a própria DLL via HardwareManager
            try:
                temp_hw = HardwareManager(com_port=1)
                if temp_hw.try_detect_and_set_port():
                    self.com_port = temp_hw.com_port
            except Exception:
                pass
        if self.com_port is None:
            messagebox.showerror(t('app_shell.error_hardware_not_found'), t('app_shell.error_hardware_not_found_msg'))
            self.master.destroy()
            return
        
        if not self._perform_initial_reset(self.com_port):
            messagebox.showwarning(t('app_shell.warning_hardware_reset'), t('app_shell.warning_hardware_reset_msg'))

        self.license_manager = LicenseManager(LICENSE_DB_FILE)
        self.hardware_manager = HardwareManager(com_port=self.com_port)
        # ... (resto do __init__ igual ao anterior)
        self.license_limits = { 'min_freq': 800, 'max_freq': 1000, 'min_power': 5, 'max_power': 25 }
        self.port_manager = get_port_manager()
        
        # Sistema de traduções
        self.translator = get_translator()
        self.translator.add_language_change_listener(self._on_language_changed)
        
        # CORREÇÃO CRÍTICA: Atualiza a porta do port_manager para a porta REAL detectada
        self.port_manager.com_port = self.com_port
        print(f"[OK] Port_manager atualizado para usar porta COM{self.com_port}")
        
        # NOVO: Detecta se há hardware conectado
        self.hardware_available = self._check_hardware_availability()
        
        # NOVO: Lógica correta para licença ATIVA
        # Licença ATIVA = Licença válida + Hardware conectado
        if self.hardware_available:
            # Hardware conectado: verifica se há licença válida
            self.license_status = self.license_manager.check_any_valid_license()
            if self.license_status:
                # Licença válida + Hardware = Licença ATIVA
                self.active_license_token = self._load_active_license()
                self._activate_first_valid_license()
                print("[OK] Licenca ATIVA: Hardware conectado + Licenca valida")
            else:
                # Hardware conectado mas sem licença válida
                self.active_license_token = None
                print("[AVISO] Hardware conectado mas sem licenca valida - modo browser")
        else:
            # Sem hardware: força modo browser independente da licença
            self.license_status = False
            self.active_license_token = None
            print("[AVISO] Hardware nao detectado - forcando modo browser")
        
        self.is_fullscreen = False
        self.nav_buttons = {}
        self.current_screen_frame = None
        self.test_is_running = False
        self.active_test_module = None
        self._create_header()
        self.content_area = tk.Frame(self, bg=COLOR_BG)
        self.content_area.pack(fill="both", expand=True)
        self.master.bind("<F11>", self.toggle_fullscreen)
        self.master.bind("<Escape>", self.exit_fullscreen)
        # Cache de telas para retenção de estado (ex.: FastSurance)
        self.screen_cache = {}
        self.navigate_to("License")

        # --- RF Safety: Watchdog e handlers de saída ---
        try:
            import atexit
            atexit.register(self._graceful_shutdown)
        except Exception:
            pass
        self.master.protocol("WM_DELETE_WINDOW", self._on_close)
        self._rf_heartbeat_ts = time.time()
        self._rf_watchdog_interval = 2.0  # segundos
        self._rf_watchdog_timeout = 10.0  # segundos sem batimento => STOP/CW_OFF
        self.after(int(self._rf_watchdog_interval * 1000), self._rf_watchdog_tick)

    def _load_active_license(self):
        """Carrega a licença ativa do arquivo de configuração."""
        try:
            print(f"[INFO] Tentando carregar licenca de: {LICENSE_CONFIG_FILE}")
            with open(LICENSE_CONFIG_FILE, 'r') as f:
                config = json.load(f)
                token = config.get('active_license_token')
                print(f"[INFO] Token carregado: {token[:30] if token else None}...")
                return token
        except (FileNotFoundError, json.JSONDecodeError) as e:
            print(f"[ERRO] Erro ao carregar licenca: {e}")
            return None

    def _save_active_license(self, token):
        """Salva a licença ativa no arquivo de configuração."""
        try:
            config = {'active_license_token': token}
            with open(LICENSE_CONFIG_FILE, 'w') as f:
                json.dump(config, f, indent=4)
        except Exception as e:
            print(f"Erro ao salvar licença ativa: {e}")

    def _perform_initial_reset(self, com_port):
        print(f"--- EXECUTANDO FACTORY RESET NA PORTA COM{com_port} ---")
        try:
            dll_name = "UHFRFID.dll"
            dll_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), dll_name)
            if not os.path.exists(dll_path):
                 dll_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), '..', 'core', dll_name)

            # MODIFICADO: Verificação rápida se DLL existe
            if not os.path.exists(dll_path):
                print("[AVISO] DLL UHFRFID.dll nao encontrada - pulando factory reset")
                return True  # Retorna True para não bloquear a inicialização

            ### ALTERAÇÃO CRÍTICA: Revertido para CDLL ###
            rfid_sdk = ctypes.CDLL(dll_path)
            
            rfid_sdk.UHF_RFID_Open.argtypes = [ctypes.c_ubyte, ctypes.c_int]
            rfid_sdk.UHF_RFID_Open.restype = ctypes.c_int
            rfid_sdk.UHF_RFID_Close.argtypes = [ctypes.c_ubyte]
            rfid_sdk.UHF_RFID_Close.restype = ctypes.c_int
            rfid_sdk.UHF_RFID_Set.argtypes = [ctypes.c_int, ctypes.c_char_p, ctypes.c_uint, ctypes.c_char_p, ctypes.POINTER(ctypes.c_uint)]
            rfid_sdk.UHF_RFID_Set.restype = ctypes.c_int
            
            # MODIFICADO: Tentativa rápida de conexão com timeout
            if rfid_sdk.UHF_RFID_Open(com_port, 115200) != 0:
                print(f"[AVISO] Falha ao abrir a porta COM{com_port} - pulando factory reset")
                return True  # Retorna True para não bloquear a inicialização
            
            print("[OK] Porta aberta para o reset.")
            
            # CORRIGIDO: Usa Soft Reset (0x68) ao invés de Factory Reset - SOFT RESET FAZ BIP!
            RFID_CMD_SET_SOFTRESET = 0x68
            out_buf = ctypes.create_string_buffer(64)
            out_len = ctypes.c_uint(0)
            # Segurança: STOP e CW_OFF preventivos antes do reset
            try:
                rfid_sdk.UHF_RFID_Set(0x8C, None, 0, out_buf, ctypes.byref(out_len))  # STOP INVENTORY
                rfid_sdk.UHF_RFID_Set(0x24, ctypes.c_char_p(b'\x00'), 1, out_buf, ctypes.byref(out_len))  # CW OFF
            except Exception:
                pass
            status = rfid_sdk.UHF_RFID_Set(RFID_CMD_SET_SOFTRESET, None, 0, out_buf, ctypes.byref(out_len))
            
            if status == 0:
                print("[OK] Comando de Soft Reset enviado com sucesso - BIP EMITIDO!")
            else:
                print(f"[AVISO] Falha ao enviar comando de Soft Reset. Código de erro: {status}")

            rfid_sdk.UHF_RFID_Close(com_port)
            print("[OK] Porta fechada após o reset.")
            
            # MODIFICADO: Delay reduzido de 1 segundo para 100ms
            print("...Aguardando 100ms para o hardware processar o reset...")
            time.sleep(0.1)
            print("--- RESET INICIAL CONCLUÍDO ---")
            return True
        except Exception as e:
            print(f"[ERRO] ERRO CRITICO durante a rotina de reset: {e}")
            return True  # Retorna True para não bloquear a inicialização

    # O resto do arquivo app_shell.py continua o mesmo
    def _create_header(self):
        self.header_frame = tk.Frame(self, bg="#222", height=60)
        self.header_frame.pack(side="top", fill="x")
        self.header_frame.pack_propagate(False)

        # Logo - carrega imagem embutida, senão mostra texto
        try:
            # Carrega o logo do módulo embedded_images
            logo_image = get_logo_image()
            
            if logo_image is not None:
                try:
                    # Redimensiona mantendo proporção
                    original_width, original_height = logo_image.size
                    aspect_ratio = original_width / original_height
                    new_height = 35
                    new_width = int(new_height * aspect_ratio)
                    logo_image = logo_image.resize((new_width, new_height), Image.Resampling.LANCZOS)
                    logo_photo = ImageTk.PhotoImage(logo_image)
                    logo_label = tk.Label(self.header_frame, image=logo_photo, bg="#222")
                    logo_label.image = logo_photo  # Mantém referência
                    logo_label.pack(side="left", padx=8, pady=2)
                    print("✅ Logo carregado do módulo embedded_images")
                except Exception as e:
                    print(f"Erro ao processar logo embutido: {e}")
                    # Fallback para texto
                    logo_label = tk.Label(self.header_frame, text="FastChecker II", font=("Arial", 18, "bold"), bg="#222", fg="#fff")
                    logo_label.pack(side="left", padx=8, pady=2)
            else:
                # Fallback para texto
                logo_label = tk.Label(self.header_frame, text="FastChecker II", font=("Arial", 18, "bold"), bg="#222", fg="#fff")
                logo_label.pack(side="left", padx=8, pady=2)
        except Exception as e:
            # Fallback para texto em caso de erro
            logo_label = tk.Label(self.header_frame, text="FastChecker II", font=("Arial", 18, "bold"), bg="#222", fg="#fff")
            logo_label.pack(side="left", padx=8, pady=2)

        nav_button_names = [
            "Population",
            "Simulator",
            "Threshold",
            "FastThreshold",
            "Fast Orientation",
            "Constant Power",
            "RSSI x Power",
            "FastSurance",
            "Antenna Check",
            "Noise Check",
            "License",
            "FAQ / Help",
        ]
        for btn_text in nav_button_names:
            btn = tk.Button(self.header_frame, text=btn_text, relief=tk.FLAT, font=("Segoe UI", 9), borderwidth=0, padx=10, command=lambda t=btn_text: self.navigate_to(t))
            btn.pack(side="left", padx=1, pady=7)
            self.nav_buttons[btn_text] = btn

        # Seletor de idioma (canto direito)
        self._create_language_selector()

        tk.Label(self.header_frame, text="Press F11 to FullScreen", bg=COLOR_HEADER_BG, fg=COLOR_HEADER_FG, font=("Segoe UI", 9)).pack(side="right", padx=20)
    
    def _create_language_selector(self):
        """Cria o seletor de idioma no header (canto direito)"""
        lang_frame = tk.Frame(self.header_frame, bg=COLOR_HEADER_BG)
        lang_frame.pack(side="right", padx=(0, 10))
        
        # Label ou botão mostrando idioma atual
        current_lang = self.translator.get_language().upper()
        self.lang_button = tk.Menubutton(
            lang_frame,
            text=f"🌐 {current_lang}",
            relief=tk.FLAT,
            bg=COLOR_HEADER_BG,
            fg=COLOR_HEADER_FG,
            font=("Segoe UI", 9),
            activebackground="#444",
            activeforeground=COLOR_HEADER_FG,
            borderwidth=0,
            padx=8,
            pady=4
        )
        self.lang_button.pack(side="right")
        
        # Menu dropdown
        lang_menu = tk.Menu(self.lang_button, tearoff=0, bg="#333", fg="#fff", font=("Segoe UI", 9))
        self.lang_button.config(menu=lang_menu)
        
        lang_menu.add_command(
            label="Português",
            command=lambda: self._change_language('pt')
        )
        lang_menu.add_command(
            label="English",
            command=lambda: self._change_language('en')
        )
    
    def _change_language(self, language):
        """Altera o idioma e atualiza a interface"""
        print(f"🔄 _change_language chamado com: {language}")
        old_lang = self.translator.get_language()
        self.translator.set_language(language)
        print(f"✅ Idioma alterado de {old_lang} para {language}")
        # O listener _on_language_changed será chamado automaticamente
    
    def _on_language_changed(self, old_lang, new_lang):
        """Callback chamado quando o idioma é alterado"""
        print(f"🔔 _on_language_changed chamado: {old_lang} -> {new_lang}")
        # Atualiza o botão de idioma
        if hasattr(self, 'lang_button'):
            self.lang_button.config(text=f"🌐 {new_lang.upper()}")
            print(f"✅ Botão de idioma atualizado para: {new_lang.upper()}")
        
        # Notifica todos os módulos para atualizarem seus textos
        # Primeiro, atualiza o módulo atual se existir
        if hasattr(self, 'current_screen_frame') and self.current_screen_frame:
            module_name = type(self.current_screen_frame).__name__
            if hasattr(self.current_screen_frame, 'refresh_language'):
                try:
                    print(f"🔄 Atualizando idioma no módulo: {module_name}")
                    self.current_screen_frame.refresh_language()
                    print(f"✅ Idioma atualizado com sucesso em {module_name}")
                except Exception as e:
                    print(f"⚠️ Erro ao atualizar idioma do módulo {module_name}: {e}")
                    import traceback
                    traceback.print_exc()
            else:
                # Módulo ainda não traduzido - não é um erro, apenas informativo
                print(f"ℹ️ Módulo {module_name} ainda não possui tradução (dual-language)")
        
    def _activate_first_valid_license(self):
        try:
            # Se já temos uma licença ativa salva, tenta usá-la primeiro
            if self.active_license_token:
                lic_data, msg = self.license_manager.parse_and_validate_token(self.active_license_token)
                if lic_data:
                    print(f"[OK] Licenca ativa restaurada: {lic_data.get('licName', 'N/A')}")
                    self.license_status = True
                    # CORREÇÃO: Atualiza license_limits quando licença é restaurada
                    self._update_license_limits_from_active_license()
                    return
            # Se não tem licença ativa ou ela é inválida, procura a primeira válida
            for uid, tokens in self.license_manager.licenses.items():
                for token in tokens:
                    lic_data, msg = self.license_manager.parse_and_validate_token(token)
                    if lic_data:
                        print(f"[OK] Ativando licenca: {lic_data.get('licName', 'N/A')}")
                        self.active_license_token = token
                        self._save_active_license(token)
                        self.license_status = True
                        # CORREÇÃO: Atualiza license_limits quando licença é ativada
                        self._update_license_limits_from_active_license()
                        return
            print("[ERRO] Nenhuma licenca valida encontrada para ativacao automatica")
        except Exception as e:
            print(f"[ERRO] Erro ao ativar licenca automaticamente: {e}")

    # --- RF Safety: Watchdog/Shutdown Handlers ---
    def _rf_watchdog_tick(self):
        try:
            # Atualiza batimento enquanto um teste está marcado como rodando
            if getattr(self, 'test_is_running', False):
                self._rf_heartbeat_ts = time.time()
            # Se ficou tempo demais sem batimento e está marcado como running, envia STOP/CW_OFF
            if getattr(self, 'test_is_running', False):
                if time.time() - self._rf_heartbeat_ts > self._rf_watchdog_timeout:
                    try:
                        print("[RF-SAFETY] Watchdog: Sem batimento recente. Enviando STOP/CW_OFF preventivo...")
                        self.port_manager.force_reset()
                    except Exception:
                        pass
                    # Não altera flags de teste; apenas segurança
            # Reagenda
            self.after(int(self._rf_watchdog_interval * 1000), self._rf_watchdog_tick)
        except Exception:
            # Garante reagendamento mesmo com erros esporádicos
            try:
                self.after(int(self._rf_watchdog_interval * 1000), self._rf_watchdog_tick)
            except Exception:
                pass

    def _graceful_shutdown(self):
        try:
            print("[RF-SAFETY] Shutdown gracioso: enviando STOP/CW_OFF...")
            self.port_manager.force_reset()
        except Exception:
            pass

    def _on_close(self):
        try:
            self._graceful_shutdown()
        finally:
            try:
                self.master.destroy()
            except Exception:
                pass

    def set_active_license(self, token):
        self.active_license_token = token
        self._save_active_license(token)
        self.license_status = self.license_manager.check_any_valid_license()
        # CORREÇÃO: Atualiza license_limits quando licença é alterada
        self._update_license_limits_from_active_license()
        
        # NOVO: Notifica todos os módulos sobre a mudança de licença
        self._notify_modules_license_changed()
    
    def set_active_project(self, project_id):
        self.active_project_id = project_id

    def set_project_data(self, project_data):
        self.active_project_data = project_data

    def get_active_project_data(self):
        return getattr(self, 'active_project_data', None)

    def set_test_running(self, is_running, module_name=None):
        self.test_is_running = is_running
        self.active_test_module = module_name if is_running else None
        self._update_nav_buttons_state()
        
    def is_test_running(self):
        return self.test_is_running
        
    def get_active_test_module(self):
        return self.active_test_module
        
    def _update_nav_buttons_state(self):
        if self.test_is_running:
            for name, button in self.nav_buttons.items():
                if name == self.active_test_module:
                    button.config(state='normal', bg="#FFD700", fg="black", 
                                font=("Segoe UI", 9, "bold"), relief=tk.RAISED)
                else:
                    button.config(state='disabled', bg="#CCCCCC", fg="#666666", 
                                font=("Segoe UI", 9), relief=tk.FLAT)
        else:
            for name, button in self.nav_buttons.items():
                button.config(state='normal', bg="#F5F5F5", fg="black", 
                            font=("Segoe UI", 9), relief=tk.FLAT)
            if hasattr(self, 'current_screen_name') and self.current_screen_name in self.nav_buttons:
                button = self.nav_buttons[self.current_screen_name]
                button.config(font=("Segoe UI", 9, "bold"), relief=tk.RAISED, bg="#90EE90", fg="black")
    
    def _notify_modules_license_changed(self):
        """NOVO: Notifica todos os módulos sobre mudança no status da licença"""
        try:
            print(f"[INFO] AppShell: Notificando modulos sobre mudanca de licenca - status: {self.license_status}")
            
            # CORREÇÃO: Verifica se há módulo atual antes de notificar
            if hasattr(self, 'current_screen_frame') and self.current_screen_frame:
                self._update_current_module_license()
            
            # Notifica módulos em cache (ex: FastSurance)
            if hasattr(self, 'screen_cache'):
                for module_name, cached_module in self.screen_cache.items():
                    if cached_module and cached_module.winfo_exists():
                        self._update_module_license(cached_module, module_name)
                        
        except Exception as e:
            print(f"[ERRO] Erro ao notificar modulos sobre mudanca de licenca: {e}")
    
    def _update_current_module_license(self):
        """NOVO: Atualiza a licença do módulo atual"""
        if not hasattr(self, 'current_screen_frame') or not self.current_screen_frame:
            return
            
        module_name = getattr(self.current_screen_frame, 'module_name', 'Unknown')
        
        # CORREÇÃO: Para o Threshold, usa update_license_status se disponível
        if module_name == "Threshold" and hasattr(self.current_screen_frame, 'update_license_status'):
            print(f"[INFO] AppShell: Atualizando Threshold via update_license_status")
            self.current_screen_frame.update_license_status(self.license_status)
        else:
            self._update_module_license(self.current_screen_frame, module_name)
    
    def _update_module_license(self, module, module_name):
        """NOVO: Atualiza a licença de um módulo específico"""
        try:
            # Módulos que usam app_shell (Threshold, FastSurance, RSSI x Power, Constant Power)
            if hasattr(module, '_update_license_limits_from_app_shell'):
                print(f"[INFO] AppShell: Atualizando {module_name} via _update_license_limits_from_app_shell")
                module._update_license_limits_from_app_shell()
                
            # Módulos que usam is_licensed (Noise, Antenna, etc.)
            elif hasattr(module, 'update_license_status'):
                print(f"[INFO] AppShell: Atualizando {module_name} via update_license_status")
                module.update_license_status(self.license_status)
                
            # Módulos que usam license_limits
            elif hasattr(module, 'license_limits'):
                print(f"[INFO] AppShell: Atualizando {module_name} via license_limits")
                if module_name == "Antenna Check":
                    new_limits = self._calculate_license_limits(["AntennaCheck", "FastChecker"])
                    module.license_limits = new_limits
                    # CORREÇÃO: Atualiza as variáveis de limite de frequência
                    if hasattr(module, 'min_freq_lic'):
                        module.min_freq_lic = new_limits.get('min_freq', 800)
                        module.max_freq_lic = new_limits.get('max_freq', 1000)
                        module.min_power_lic = new_limits.get('min_power', 5)
                        module.max_power_lic = new_limits.get('max_power', 25)
                        print(f"[OK] AppShell: Limites de frequência atualizados - {module.min_freq_lic}-{module.max_freq_lic}MHz")
                    if hasattr(module, 'update_ui_state'):
                        module.update_ui_state()
                        
        except Exception as e:
            print(f"[AVISO] Erro ao atualizar licença do módulo {module_name}: {e}")
    
    def _update_license_limits_from_active_license(self):
        """CORREÇÃO: Atualiza license_limits baseado na licença ativa"""
        if not self.active_license_token:
            self.license_limits = {'min_freq': 800, 'max_freq': 1000, 'min_power': 5, 'max_power': 25, 'is_licensed': False}
            return
        
        lic_data, _ = self.license_manager.parse_and_validate_token(self.active_license_token)
        if lic_data:
            # Atualiza com os limites da licença ativa
            self.license_limits = {'min_freq': 800, 'max_freq': 1000, 'min_power': 5, 'max_power': 25, 'is_licensed': True}
            
            try:
                if 'freqRange' in lic_data:
                    freq_ranges = lic_data['freqRange'].split(';')
                    for freq_range in freq_ranges:
                        if '@' in freq_range:
                            f_min, f_max = map(int, freq_range.split('@'))
                            if f_min < self.license_limits['min_freq']:
                                self.license_limits['min_freq'] = f_min
                            if f_max > self.license_limits['max_freq']:
                                self.license_limits['max_freq'] = f_max
                
                if 'powRange' in lic_data:
                    p_min, p_max = map(int, lic_data['powRange'].split('@'))
                    self.license_limits['min_power'] = p_min
                    self.license_limits['max_power'] = p_max
                
                print(f"[OK] License limits atualizados: Freq {self.license_limits['min_freq']}-{self.license_limits['max_freq']}MHz, Power {self.license_limits['min_power']}-{self.license_limits['max_power']}dBm")
                
                # NOVO: Notifica módulos sobre mudança de licença
                self._notify_modules_license_changed()

                # NOVO: Log consolidado dos limites ativos (faixas e gaps)
                try:
                    calc = self._calculate_license_limits(["FastChecker", "AntennaCheck", "Threshold"])
                    freq_ranges = calc.get('freq_ranges', [])
                    excluded = calc.get('excluded_ranges', []) if 'excluded_ranges' in calc else []
                    print(f"[INFO] Licença ativa: {calc.get('license_name')} | Faixas: {freq_ranges} | Gaps: {excluded} | Potência: {calc.get('min_power')}-{calc.get('max_power')} dBm")
                except Exception as _e:
                    print(f"[AVISO] Falha ao imprimir limites detalhados da licença: {_e}")
                
            except Exception as e:
                print(f"[ERRO] Erro ao processar limites da licença: {e}")
                self.license_limits['is_licensed'] = False
        else:
            self.license_limits['is_licensed'] = False

    def _calculate_license_limits(self, valid_license_names):
        limits = {
            'min_freq': 800, 'max_freq': 1000, 'min_power': 5, 'max_power': 25,
            'is_licensed': False, 'freq_ranges': [], 'license_name': None
        }
        if not self.active_license_token: 
            print(f"[INFO] _calculate_license_limits: Nenhuma licença ativa")
            return limits
        lic_data, _ = self.license_manager.parse_and_validate_token(self.active_license_token)
        
        # CORREÇÃO: Log detalhado para diagnóstico
        if lic_data:
            license_name = lic_data.get("licName", "N/A")
            print(f"[INFO] _calculate_license_limits: Licença encontrada: '{license_name}', nomes válidos: {valid_license_names}")
        else:
            print(f"[INFO] _calculate_license_limits: Licença inválida ou token corrompido")
            return limits
            
        # CORREÇÃO: Aceita qualquer licença válida para módulos genéricos como Threshold
        # Se a lista contém "Threshold" ou "FastChecker", aceita qualquer licença válida
        accepts_any_license = any(name in ["Threshold", "FastChecker"] for name in valid_license_names)
        
        if accepts_any_license or lic_data.get("licName") in valid_license_names:
            limits['is_licensed'] = True
            limits['license_name'] = lic_data.get("licName")
            print(f"[OK] _calculate_license_limits: Licença aceita para {valid_license_names} - '{limits['license_name']}'.")
            try:
                if 'freqRange' in lic_data:
                    print(f"[INFO] _calculate_license_limits: freqRange raw: '{lic_data['freqRange']}'")
                    freq_ranges = lic_data['freqRange'].split(';')
                    print(f"[INFO] _calculate_license_limits: freq_ranges split: {freq_ranges}")
                    for freq_range in freq_ranges:
                        if '@' in freq_range:
                            # Aceita decimais e vírgulas nas faixas (ex.: 915,5)
                            f_min_str, f_max_str = freq_range.split('@')
                            f_min = float(f_min_str.replace(',', '.'))
                            f_max = float(f_max_str.replace(',', '.'))
                            limits['freq_ranges'].append((f_min, f_max))
                            print(f"[OK] _calculate_license_limits: Adicionada faixa: {f_min}-{f_max} MHz")
                    if not limits['freq_ranges'] and '@' in lic_data['freqRange']:
                        f_min_str, f_max_str = lic_data['freqRange'].split('@')
                        f_min = float(f_min_str.replace(',', '.'))
                        f_max = float(f_max_str.replace(',', '.'))
                        limits['freq_ranges'].append((f_min, f_max))
                        print(f"[OK] _calculate_license_limits: Adicionada faixa única: {f_min}-{f_max} MHz")
                    if limits['freq_ranges']:
                        all_freqs = [f for r in limits['freq_ranges'] for f in r]
                        limits['min_freq'] = min(all_freqs)
                        limits['max_freq'] = max(all_freqs)
                        print(f"[INFO] _calculate_license_limits: freq_ranges final: {limits['freq_ranges']}")
                        print(f"[INFO] _calculate_license_limits: min_freq: {limits['min_freq']}, max_freq: {limits['max_freq']}")
                    else:
                        print(f"[AVISO] _calculate_license_limits: Nenhuma faixa válida encontrada!")
                
                # CORREÇÃO CRÍTICA: Processa freqGap para criar faixas excluídas
                if 'freqGap' in lic_data and lic_data['freqGap']:
                    print(f"[INFO] _calculate_license_limits: freqGap raw: '{lic_data['freqGap']}'")
                    try:
                        gap_ranges = lic_data['freqGap'].split(';')
                        for gap_range in gap_ranges:
                            if '@' in gap_range:
                                # CORREÇÃO: Converte vírgulas para pontos antes de fazer float
                                gap_parts = gap_range.split('@')
                                gap_min_str = gap_parts[0].replace(',', '.')
                                gap_max_str = gap_parts[1].replace(',', '.')
                                gap_min, gap_max = float(gap_min_str), float(gap_max_str)
                                print(f"[INFO] _calculate_license_limits: Gap detectado: {gap_min}-{gap_max} MHz")
                                # Adiciona o gap como uma faixa excluída
                                limits['excluded_ranges'] = limits.get('excluded_ranges', [])
                                limits['excluded_ranges'].append((gap_min, gap_max))
                                print(f"[OK] _calculate_license_limits: Faixa excluída adicionada: {gap_min}-{gap_max} MHz")
                    except (ValueError, IndexError) as e:
                        print(f"[AVISO] _calculate_license_limits: Erro ao processar freqGap: {e}")
                
                if 'powRange' in lic_data:
                    p_min, p_max = map(int, lic_data['powRange'].split('@'))
                    limits['min_power'] = p_min
                    limits['max_power'] = p_max
            except (ValueError, IndexError):
                limits['is_licensed'] = False
        else:
            print(f"[ERRO] _calculate_license_limits: Licença '{lic_data.get('licName', 'N/A')}' não aceita para {valid_license_names}")
        return limits

    def navigate_to(self, screen_name):
        if self.test_is_running and screen_name != self.active_test_module:
            messagebox.showwarning(t('app_shell.warning_test_in_progress'), t('app_shell.warning_test_in_progress_msg'), parent=self.master)
            return
            
        # CORREÇÃO: Calcula o status da licença baseado na licença ativa
        if self.active_license_token:
            lic_data, _ = self.license_manager.parse_and_validate_token(self.active_license_token)
            self.license_status = lic_data is not None
        else:
            self.license_status = False
            
        if self.current_screen_frame:
            # CORREÇÃO: Remove o módulo anterior da tela antes de mostrar o novo
            current_name = getattr(self, 'current_screen_name', None)
            if current_name in ["FastSurance", "Noise Check", "Threshold", "FastThreshold", "Fast Orientation", "Population", "Simulator"]:
                try:
                    # Salva no cache ANTES de remover da tela
                    self.screen_cache[current_name] = self.current_screen_frame
                    
                    # CORREÇÃO: Tenta AMBOS os métodos para garantir que seja removido
                    # Alguns módulos podem usar pack internamente mas serem adicionados com grid
                    removed = False
                    try:
                        self.current_screen_frame.pack_forget()
                        removed = True
                        print(f"[OK] {current_name}: Removido da tela (pack_forget)")
                    except:
                        pass
                    
                    if not removed:
                        try:
                            self.current_screen_frame.grid_forget()
                            removed = True
                            print(f"[OK] {current_name}: Removido da tela (grid_forget)")
                        except:
                            pass
                    
                    if not removed:
                        print(f"[AVISO] {current_name}: Não foi possível remover da tela com pack/grid_forget")
                    
                    print(f"[OK] {current_name}: Instância preservada no cache")
                except Exception as e:
                    print(f"[ERRO] {current_name}: Erro ao cachear: {e}")
                    try:
                        self.current_screen_frame.destroy()
                    except:
                        pass
                    self.screen_cache.pop(current_name, None)
                    print(f"[AVISO] {current_name}: Fallback - instância destruída")
            else:
                # Módulos que não são cacheados são destruídos
                try:
                    self.current_screen_frame.destroy()
                except:
                    pass
            self.current_screen_frame = None

        # Barreira de segurança: garantir que a COM esteja livre antes de criar/entrar no próximo módulo
        try:
            pm = self.port_manager if hasattr(self, 'port_manager') else get_port_manager()
            # Pequeno reset preventivo; evita estados zumbis ao trocar rápido de telas
            pm.force_reset()
            time.sleep(0.05)
        except Exception:
            pass
        
        self.current_screen_name = screen_name
        
        if not self.test_is_running:
            for name, button in self.nav_buttons.items():
                button.config(font=("Segoe UI", 9), relief=tk.FLAT, bg="#F5F5F5", fg="black")
            if screen_name in self.nav_buttons:
                self.nav_buttons[screen_name].config(font=("Segoe UI", 9, "bold"), relief=tk.RAISED, bg="#90EE90", fg="black")
            
        try:
            if screen_name == "Threshold":
                print(f"[INFO] AppShell: Navegando para Threshold...")
                # Reutiliza a instância existente para reter dados e gráficos
                cached = self.screen_cache.get('Threshold')
                print(f"[INFO] AppShell: Cache Threshold existe: {cached is not None}")
                if cached and cached.winfo_exists():
                    print(f"[INFO] AppShell: Reutilizando instância cached do Threshold")
                    self.current_screen_frame = cached
                    # Força redesenho completo dos gráficos ao voltar para a aba
                    if hasattr(self.current_screen_frame, '_force_graph_redraw'):
                        print("[INFO] Threshold: Forçando redesenho dos gráficos...")
                        self.current_screen_frame._force_graph_redraw()
                        print("[OK] Threshold: Gráficos redesenhados")
                else:
                    print(f"[INFO] AppShell: Criando nova instância do Threshold")
                    self.current_screen_frame = ThresholdModule(self.content_area, app_shell=self, com_port=self.com_port)
                    self.screen_cache['Threshold'] = self.current_screen_frame
                    print(f"[INFO] AppShell: ThresholdModule criado e armazenado no cache")
            elif screen_name == "FastThreshold":
                print(f"[INFO] AppShell: Navegando para FastThreshold...")
                # Reutiliza a instância existente para reter dados e gráficos
                cached = self.screen_cache.get('FastThreshold')
                print(f"[INFO] AppShell: Cache FastThreshold existe: {cached is not None}")
                if cached and cached.winfo_exists():
                    print(f"[INFO] AppShell: Reutilizando instância cached do FastThreshold")
                    self.current_screen_frame = cached
                else:
                    print(f"[INFO] AppShell: Criando nova instância do FastThreshold")
                    self.current_screen_frame = QuickThresholdModule(self.content_area, app_shell=self, com_port=self.com_port)
                    self.screen_cache['FastThreshold'] = self.current_screen_frame
                    print(f"[INFO] AppShell: QuickThresholdModule criado e armazenado no cache")
            elif screen_name == "Fast Orientation":
                print(f"[INFO] AppShell: Navegando para Fast Orientation...")
                cached = self.screen_cache.get('Fast Orientation')
                print(f"[INFO] AppShell: Cache Fast Orientation existe: {cached is not None}")
                if cached and cached.winfo_exists():
                    print(f"[INFO] AppShell: Reutilizando instância cached do Fast Orientation")
                    self.current_screen_frame = cached
                else:
                    print(f"[INFO] AppShell: Criando nova instância do Fast Orientation")
                    self.current_screen_frame = OrientationTester(self.content_area, app_shell=self)
                    self.screen_cache['Fast Orientation'] = self.current_screen_frame
                    print(f"[INFO] AppShell: OrientationTester criado e armazenado no cache")
            elif screen_name == "Population":
                print(f"[INFO] AppShell: Navegando para Population...")
                # Reutiliza a instância existente para reter dados e estado
                cached = self.screen_cache.get('Population')
                print(f"[INFO] AppShell: Cache Population existe: {cached is not None}")
                if cached and cached.winfo_exists():
                    print(f"[INFO] AppShell: Reutilizando instância cached do Population")
                    self.current_screen_frame = cached
                    # Atualiza a lista de tags para refletir qualquer mudança
                    if hasattr(self.current_screen_frame, 'update_tags_list'):
                        print("[INFO] Population: Atualizando lista de tags...")
                        self.current_screen_frame.update_tags_list()
                        print("[OK] Population: Lista de tags atualizada")
                else:
                    print(f"[INFO] AppShell: Criando nova instância do Population")
                    try:
                        self.current_screen_frame = PopulacaoModule(self.content_area, app_shell=self, com_port=self.com_port)
                        self.screen_cache['Population'] = self.current_screen_frame
                        print(f"[INFO] AppShell: PopulacaoModule criado e armazenado no cache")
                    except Exception as e:
                        print(f"[ERRO] AppShell: Erro ao criar PopulacaoModule: {e}")
                        import traceback
                        traceback.print_exc()
            elif screen_name == "Simulator":
                print(f"[INFO] AppShell: Criando SimuladorModule")
                try:
                    # Simulator pode receber app_shell para acesso a port_manager e license
                    self.current_screen_frame = SimuladorModule(self.content_area, app_shell=self)
                    print(f"[INFO] AppShell: SimuladorModule criado")
                except Exception as e:
                    print(f"[ERRO] AppShell: Erro ao criar SimuladorModule: {e}")
                    import traceback
                    traceback.print_exc()
                    messagebox.showerror(t('app_shell.error_module_load'), t('app_shell.error_module_load_msg').format(module='Simulator', error=str(e)), parent=self.master)
                    return
            elif screen_name == "Constant Power":
                print(f"[INFO] AppShell: Criando módulo Constant Power...")
                print(f"[INFO] AppShell: License_status atual: {self.license_status}")
                print(f"[INFO] AppShell: License_limits atual: {self.license_limits}")
                self.current_screen_frame = PowerConstantModule(self.content_area, app_shell=self, com_port=self.com_port)
                print(f"[INFO] AppShell: Módulo Constant Power criado")
                # NOVO: Atualiza limites da licença após criar o módulo
                if hasattr(self.current_screen_frame, '_update_license_limits_from_app_shell'):
                    print(f"[INFO] AppShell: Chamando _update_license_limits_from_app_shell...")
                    self.current_screen_frame._update_license_limits_from_app_shell()
                    print(f"[INFO] AppShell: _update_license_limits_from_app_shell concluído")
                else:
                    print(f"[AVISO] AppShell: Módulo Constant Power não tem _update_license_limits_from_app_shell")
            elif screen_name == "RSSI x Power":
                self.current_screen_frame = RSSIPowerModule(self.content_area, app_shell=self, com_port=self.com_port)
                # NOVO: Atualiza limites da licença após criar o módulo
                if hasattr(self.current_screen_frame, '_update_license_limits_from_app_shell'):
                    self.current_screen_frame._update_license_limits_from_app_shell()
            elif screen_name == "FastSurance":
                print(f"[INFO] AppShell: Navegando para FastSurance...")
                # Reutiliza a instância existente para reter dados
                cached = self.screen_cache.get('FastSurance')
                print(f"[INFO] AppShell: Cache FastSurance existe: {cached is not None}")
                if cached and cached.winfo_exists():
                    print(f"[INFO] AppShell: Reutilizando instância cached do FastSurance")
                    self.current_screen_frame = cached
                    # NOVO: Restaura dados de threshold ao voltar para a aba
                    print("[INFO] FastSurance: Restaurando dados de threshold ao trocar de aba...")
                    if hasattr(self.current_screen_frame, '_restore_threshold_data_from_history'):
                        print("[INFO] AppShell: Método _restore_threshold_data_from_history encontrado, chamando...")
                        # Chama a restauração diretamente
                        self.current_screen_frame._restore_threshold_data_from_history()
                        print("[OK] FastSurance: Dados de threshold restaurados ao trocar de aba")
                    else:
                        print("[AVISO] FastSurance: Método _restore_threshold_data_from_history não encontrado")
                else:
                    print(f"[INFO] AppShell: Criando nova instância do FastSurance")
                    hardware_controller = get_hardware_controller()
                    self.current_screen_frame = RFIDApp(self.content_area, hardware_controller=hardware_controller, com_port=self.com_port, app_shell=self)
                    self.screen_cache['FastSurance'] = self.current_screen_frame
                if hasattr(self.current_screen_frame, '_update_license_limits_from_app_shell'):
                    self.current_screen_frame._update_license_limits_from_app_shell()
            elif screen_name == "Antenna Check":
                # Reutiliza a instância existente para reter dados e configurações
                cached = self.screen_cache.get('Antenna Check')
                if cached and cached.winfo_exists():
                    self.current_screen_frame = cached
                    print(f"[OK] Antenna Check: Instância reutilizada do cache")
                    # CORREÇÃO: Atualiza os limites da licença na instância existente
                    if hasattr(self.current_screen_frame, 'update_license_status'):
                        print(f"[INFO] AppShell: Atualizando Antenna Check via update_license_status")
                        self.current_screen_frame.update_license_status(self.license_status)
                else:
                    print(f"[INFO] AppShell: Criando nova instância do Antenna Check")
                    license_limits = self._calculate_license_limits(["AntennaCheck", "FastChecker"])
                    print(f"[INFO] AppShell: Antenna Check - license_limits calculados: {license_limits}")
                    print(f"[INFO] AppShell: Antenna Check - is_licensed: {license_limits.get('is_licensed', False)}")
                    self.current_screen_frame = AntennaModule(self.content_area, license_limits=license_limits, app_shell=self, com_port=self.com_port)
                    self.screen_cache['Antenna Check'] = self.current_screen_frame
                    print(f"[INFO] AppShell: Antenna Check criado e adicionado ao cache")
            elif screen_name == "Noise Check":
                # Reutiliza a instância existente para reter dados e testes em andamento
                cached = self.screen_cache.get('Noise Check')
                if cached and cached.winfo_exists():
                    self.current_screen_frame = cached
                    print(f"[OK] Noise Check: Instância reutilizada do cache")
                    # CORREÇÃO: Atualiza a licença da instância reutilizada
                    if hasattr(self.current_screen_frame, 'update_license_status'):
                        self.current_screen_frame.update_license_status(self.license_status)
                else:
                    print(f"[INFO] AppShell: Criando NoiseModule com license_status: {self.license_status}")
                    # CORREÇÃO: Calcula limites de licença específicos para Noise Check
                    license_limits = self._calculate_license_limits(["NoiseCheck", "FastChecker"])
                    print(f"[INFO] AppShell: Noise Check - license_limits calculados: {license_limits}")
                    self.current_screen_frame = NoiseModule(self.content_area, is_licensed=self.license_status, app_shell=self, com_port=self.com_port)
                    # CORREÇÃO: Força atualização da licença após criação
                    if hasattr(self.current_screen_frame, 'update_license_status'):
                        self.current_screen_frame.update_license_status(self.license_status)
                    self.screen_cache['Noise Check'] = self.current_screen_frame
                    print(f"[INFO] AppShell: NoiseModule criado e adicionado ao cache, module_name: {getattr(self.current_screen_frame, 'module_name', 'N/A')}")
            elif screen_name == "License":
                self.current_screen_frame = LicenseScreen(self.content_area, self.hardware_manager, self.license_manager, app_shell=self)
                if hasattr(self.current_screen_frame, 'start_auto_load'):
                    self.current_screen_frame.start_auto_load()
            elif screen_name == "FAQ / Help":
                self.current_screen_frame = FaqScreen(self.content_area)

            if self.current_screen_frame:
                # CORREÇÃO: Remove todos os widgets do content_area antes de adicionar o novo módulo
                # Isso garante que apenas um módulo fique visível por vez
                try:
                    for widget in self.content_area.winfo_children():
                        try:
                            widget.pack_forget()
                        except:
                            pass
                        try:
                            widget.grid_forget()
                        except:
                            pass
                except:
                    pass
                
                # Configura geometry manager baseado no módulo
                if screen_name in ["Threshold", "Simulator", "Population"]:
                    # Esses módulos usam grid() internamente
                    try:
                        # Configura content_area para usar grid
                        self.content_area.grid_rowconfigure(0, weight=1)
                        self.content_area.grid_columnconfigure(0, weight=1)
                        # Adiciona o módulo com grid
                        self.current_screen_frame.grid(row=0, column=0, sticky="nsew")
                        print(f"[OK] {screen_name}: Adicionado à tela com grid()")
                    except Exception as e:
                        # Fallback para pack se grid falhar
                        print(f"[AVISO] Grid falhou para {screen_name}, tentando pack: {e}")
                        try:
                            self.current_screen_frame.pack(fill="both", expand=True)
                            print(f"[OK] {screen_name}: Adicionado à tela com pack()")
                        except:
                            pass
                else:
                    # Outros módulos usam pack()
                    try:
                        self.current_screen_frame.pack(fill="both", expand=True)
                        print(f"[OK] {screen_name}: Adicionado à tela com pack()")
                    except Exception as pack_err:
                        # Fallback para grid se pack falhar
                        print(f"[AVISO] Pack falhou para {screen_name}, tentando grid: {pack_err}")
                        try:
                            self.content_area.grid_rowconfigure(0, weight=1)
                            self.content_area.grid_columnconfigure(0, weight=1)
                            self.current_screen_frame.grid(row=0, column=0, sticky="nsew")
                            print(f"[OK] {screen_name}: Adicionado à tela com grid()")
                        except:
                            pass
                
                self.current_screen_name = screen_name
                
                # FORÇA atualização da interface para garantir que o módulo apareça
                self.content_area.update_idletasks()

        except Exception as e:
            import traceback
            error_info = traceback.format_exc()
            messagebox.showerror(t('app_shell.error_module_debug'), t('app_shell.error_module_debug_msg').format(module=screen_name, traceback=error_info), parent=self.master)
    
    def toggle_fullscreen(self, event=None):
        self.is_fullscreen = not self.is_fullscreen
        self.master.attributes("-fullscreen", self.is_fullscreen)

    def exit_fullscreen(self, event=None):
        if self.is_fullscreen:
            self.toggle_fullscreen()

    def _check_hardware_availability(self):
        """NOVO: Verifica se o hardware (Silicon Labs CP210x) está realmente disponível e conectado"""
        try:
            # Tenta abrir a porta COM detectada para verificar se o hardware está realmente conectado
            if self.com_port is None:
                print("[AVISO] Nenhuma porta COM detectada - hardware não disponível")
                return False
            
            # Tenta abrir a porta COM para verificar se o hardware responde
            dll_name = "UHFRFID.dll"
            dll_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), dll_name)
            if not os.path.exists(dll_path):
                dll_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), '..', 'core', dll_name)
            
            if not os.path.exists(dll_path):
                print("[AVISO] DLL UHFRFID.dll não encontrada - hardware não disponível")
                return False
            
            # Tenta conectar com o hardware
            rfid_sdk = ctypes.CDLL(dll_path)
            rfid_sdk.UHF_RFID_Open.argtypes = [ctypes.c_ubyte, ctypes.c_int]
            rfid_sdk.UHF_RFID_Open.restype = ctypes.c_int
            rfid_sdk.UHF_RFID_Close.argtypes = [ctypes.c_ubyte]
            rfid_sdk.UHF_RFID_Close.restype = ctypes.c_int
            
            # Tenta abrir a porta COM
            result = rfid_sdk.UHF_RFID_Open(self.com_port, 115200)
            if result == 0:
                # Hardware respondeu - fecha a conexão de teste
                rfid_sdk.UHF_RFID_Close(self.com_port)
                print(f"[OK] Hardware detectado e respondendo na porta COM{self.com_port}")
                return True
            else:
                print(f"[AVISO] Hardware nao responde na porta COM{self.com_port} - modo browser")
                return False
                
        except Exception as e:
            print(f"[AVISO] Erro ao verificar hardware: {e} - modo browser")
            return False


def run_app():
    root = tk.Tk()
    try:
        app = AppShell(master=root)
        # Verifica se a aplicação foi inicializada corretamente
        if hasattr(app, 'com_port'):
            print(f"[OK] Aplicacao inicializada com porta COM{app.com_port}")
            app.mainloop()
        else:
            print("[AVISO] Aplicacao nao inicializada corretamente, mas mantendo janela aberta")
            # Mantém a janela aberta mesmo com erro
            root.mainloop()
    except Exception as e:
        print(f"[ERRO] Erro na inicializacao da aplicacao: {e}")
        import traceback
        traceback.print_exc()
        # Cria uma janela de erro simples
        error_label = tk.Label(root, text=f"Erro na inicialização:\n{str(e)}", fg="red")
        error_label.pack(pady=20)
        root.mainloop()


if __name__ == '__main__':
    run_app()