import threading
import time
import tkinter as tk
from tkinter import ttk, Label
import cv2
import numpy as np
import screeninfo
import queue
import os
import logging

# Configurazione del logging
logging.basicConfig(level=logging.DEBUG,
                    format='%(asctime)s - %(levelname)s - %(module)s - %(funcName)s - %(message)s')

# Costanti
MFD_WIDTH = 484
MFD_HEIGHT = 484
SAR_WIDTH = 2048
SAR_HEIGHT = 2048
SAR_DATA_TYPE = np.uint16
INITIAL_SAR_WIDTH = 1024  # Larghezza iniziale SAR
INITIAL_SAR_HEIGHT = 1024  # Altezza iniziale SAR
MFD_FPS = 25  # FPS target per MFD
TKINTER_MIN_WIDTH = 484
TKINTER_MIN_HEIGHT = 484
# --- Informazioni di georeferenziazione SAR (Esempio) ---
SAR_CENTER_LAT = 40.7128  # Esempio di latitudine del centro dell'immagine SAR
SAR_CENTER_LON = -74.0060  # Esempio di longitudine del centro dell'immagine SAR
SAR_IMAGE_SIZE_KM = 50.0  # Esempio di dimensione dell'immagine SAR (50km x 50km)

# --- Palette colori disponibili ---
COLOR_PALETTES = ["GRAY", "AUTUMN", "BONE", "JET", "WINTER", "RAINBOW", "OCEAN", "SUMMER", "SPRING", "COOL", "HSV",
                  "HOT"]

# --- Percorsi dei file immagine ---
MFD_IMAGE_PATH = "MFD_img8_000000.bmp"  # Sostituisci con il tuo percorso immagine MFD
SAR_IMAGE_PATH = "SAR_geo_img16_000000.tif"  # Sostituisci con il tuo percorso immagine SAR


class Core:
    """
    Contiene le funzionalità principali dell'applicazione, come il caricamento delle immagini,
    la gestione delle code, i parametri SAR e le costanti globali.
    """

    def __init__(self):
        """Inizializza il modulo Core."""
        self.mfd_image_data = None
        self.sar_image_data = None
        self.sar_queue = queue.Queue(maxsize=5)
        self.mouse_queue = queue.Queue(maxsize=20)
        self.sar_contrast = 1.0
        self.sar_brightness = 0
        self.sar_palette = "GRAY"
        self.mfd_x_offset = 0
        self.sar_x_offset = 0
        self.test_mfd_image = np.zeros((MFD_HEIGHT, MFD_WIDTH), dtype=np.uint8)
        self.test_sar_image = np.zeros((SAR_HEIGHT, SAR_HEIGHT), dtype=SAR_DATA_TYPE)

    def load_image(self, path, expected_dtype):
        """Carica un'immagine dal percorso specificato e assicura il tipo di dato corretto."""
        try:
            if not os.path.exists(path):
                error_message = f"File immagine non trovato a {path}"
                logging.error(error_message)
                #self.root.after(0, lambda: self.set_status(error_message)) #UI
                # Crea un'immagine placeholder invece
                if expected_dtype == np.uint8:
                    return np.zeros((MFD_HEIGHT, MFD_WIDTH), dtype=np.uint8)
                else:
                    return np.zeros((SAR_HEIGHT, SAR_HEIGHT), dtype=SAR_DATA_TYPE)

            img = cv2.imread(path, cv2.IMREAD_ANYDEPTH)  # Carica così com'è

            if img is None:
                error_message = f"Impossibile caricare l'immagine a {path}"
                logging.error(error_message)
                #self.root.after(0, lambda: self.set_status(error_message)) #UI
                # Crea un'immagine placeholder invece
                if expected_dtype == np.uint8:
                    return np.zeros((MFD_HEIGHT, MFD_WIDTH), dtype=np.uint8)
                else:
                    return np.zeros((SAR_HEIGHT, SAR_HEIGHT), dtype=SAR_DATA_TYPE)

            if img.dtype != expected_dtype:
                warning_message = f"Conversione dell'immagine a {path} da {img.dtype} a {expected_dtype}"
                logging.warning(warning_message)
                #self.root.after(0, lambda: self.set_status(warning_message)) #UI
                img = img.astype(expected_dtype)
            return img
        except Exception as e:
            logging.exception(f"Errore durante il caricamento dell'immagine a {path}")
            #self.root.after(0, lambda: self.set_status(f"Errore durante il caricamento dell'immagine: {e}")) #UI
            # Restituisce un'immagine placeholder in caso di errore
            if expected_dtype == np.uint8:
                return np.zeros((MFD_HEIGHT, MFD_WIDTH), dtype=np.uint8)
            else:
                return np.zeros((SAR_HEIGHT, SAR_HEIGHT), dtype=SAR_DATA_TYPE)

    def generate_test_images(self):
        """Genera immagini di test casuali per MFD e SAR."""
        try:
            self.test_mfd_image = np.random.randint(0, 256, size=(MFD_HEIGHT, MFD_WIDTH), dtype=np.uint8)
            self.test_sar_image = np.random.randint(0, 65536, size=(SAR_HEIGHT, SAR_HEIGHT), dtype=SAR_DATA_TYPE).astype(
                SAR_DATA_TYPE)
        except Exception as e:
            logging.exception("Errore in generate_test_images")
            #self.root.after(0, lambda: self.set_status(f"Errore: {e}")) #UI


class UI:
    """Gestisce l'interfaccia utente Tkinter."""

    def __init__(self, root, core, processing, display):
        """Inizializza il modulo UI."""
        self.root = root
        self.core = core
        self.processing = processing
        self.display = display
        self.root.title("Grifo E Control Panel")  # Imposta il titolo dell'applicazione
        self.root.minsize(TKINTER_MIN_WIDTH, TKINTER_MIN_HEIGHT)  # Imposta la dimensione minima

        # --- Ottieni informazioni sullo schermo ---
        screen = screeninfo.get_monitors()[0]  # Usa lo schermo principale
        screen_width = screen.width
        screen_height = screen.height

        # --- Posizionamento della finestra (valori iniziali) ---
        self.tkinter_x = 10
        self.tkinter_y = MFD_HEIGHT + 20  # Posiziona la finestra Tkinter sotto MFD
        self.root.geometry(f"+{self.tkinter_x}+{self.tkinter_y}")

        # --- Barra di stato ---
        self.statusbar = ttk.Label(self.root, text="Pronto | MFD FPS: 0 | SAR FPS: N/A", relief=tk.SUNKEN,
                                   anchor=tk.W)
        self.statusbar.pack(side=tk.BOTTOM, fill=tk.X)

        # --- Frame di controllo ---
        self.control_frame = ttk.Frame(self.root, padding=10)
        self.control_frame.pack(side=tk.TOP, fill=tk.X)

        # --- Frame parametri SAR ---
        self.sar_params_frame = ttk.Labelframe(self.control_frame, text="Parametri SAR", padding=10)
        self.sar_params_frame.pack(side=tk.LEFT, padx=10, fill=tk.X)

        # --- Frame informazioni SAR ---
        self.sar_info_frame = ttk.Labelframe(self.control_frame, text="Informazioni SAR", padding=10)
        self.sar_info_frame.pack(side=tk.TOP, padx=10, fill=tk.X)

        # --- Disposizione di tutti i widget dei parametri in una griglia ---
        row = 0

        # --- Checkbox Immagine di test ---
        self.test_image_var = tk.IntVar(value=0)  # Valore iniziale: Immagine statica
        self.test_image_check = ttk.Checkbutton(self.sar_params_frame, text="Immagine di test",
                                                 variable=self.test_image_var, command=self.update_image_mode)
        self.test_image_check.grid(row=row, column=0, columnspan=2, sticky=tk.W, padx=5, pady=2)
        row += 1

        # --- Controllo dimensione SAR ---
        self.sar_size_label = ttk.Label(self.sar_params_frame, text="Dimensione SAR:")
        self.sar_size_label.grid(row=row, column=0, sticky=tk.W, padx=5, pady=2)

        self.sar_size_factors = ["1:1", "1:2", "1:3", "1:5", "1:10"]
        self.sar_size_combo = ttk.Combobox(self.sar_params_frame, values=self.sar_size_factors, state="readonly",
                                            width=5)
        self.sar_size_combo.set("1:2")  # Valore iniziale (1/2 della dimensione originale)
        self.sar_size_combo.grid(row=row, column=1, sticky=tk.E, padx=5, pady=2)
        self.sar_size_combo.bind("<<ComboboxSelected>>", self.update_sar_size)
        row += 1

        # --- Controllo contrasto ---
        self.contrast_label = ttk.Label(self.sar_params_frame, text="Contrasto:")
        self.contrast_label.grid(row=row, column=0, sticky=tk.W, padx=5, pady=2)
        self.contrast_scale = ttk.Scale(self.sar_params_frame, orient=tk.HORIZONTAL, length=200,
                                         from_=0.1, to=3.0, command=self.update_contrast, value=1.0)
        self.contrast_scale.grid(row=row, column=1, sticky=tk.E, padx=5, pady=2)
        row += 1

        # --- Controllo luminosità ---
        self.brightness_label = ttk.Label(self.sar_params_frame, text="Luminosità:")
        self.brightness_label.grid(row=row, column=0, sticky=tk.W, padx=5, pady=2)
        self.brightness_scale = ttk.Scale(self.sar_params_frame, orient=tk.HORIZONTAL, length=200,
                                           from_=-100, to=100, command=self.update_brightness, value=0)
        self.brightness_scale.grid(row=row, column=1, sticky=tk.E, padx=5, pady=2)
        row += 1

        # --- Controllo palette colori ---
        self.palette_label = ttk.Label(self.sar_params_frame, text="Palette:")
        self.palette_label.grid(row=row, column=0, sticky=tk.W, padx=5, pady=2)
        self.palette_combo = ttk.Combobox(self.sar_params_frame, values=COLOR_PALETTES, state="readonly", width=8)
        self.palette_combo.set("GRAY")  # Valore iniziale
        self.palette_combo.grid(row=row, column=1, sticky=tk.E, padx=5, pady=2)
        self.palette_combo.bind("<<ComboboxSelected>>", self.update_sar_palette)
        row += 1

        # --- Label Lat/Lon del centro SAR ---
        self.sar_center_label = ttk.Label(self.sar_info_frame,
                                           text=f"Centro: Lat={SAR_CENTER_LAT:.4f}, Lon={SAR_CENTER_LON:.4f}")
        self.sar_center_label.pack(side=tk.TOP, anchor=tk.W)

        # --- Label Lat/Lon del mouse ---
        self.mouse_latlon_label = ttk.Label(self.sar_info_frame, text="Mouse: Lat=N/A, Lon=N/A")
        self.mouse_latlon_label.pack(side=tk.TOP, anchor=tk.W)
        self.sar_info_label = ttk.Label(self.sar_info_frame, text="Sar Info")
        self.sar_info_label.pack(side=tk.TOP, anchor=tk.W)
        # --- Inizializzazione ---
        self.mfd_fps = 0.0  # FPS iniziale MFD
        self.sar_fps = 0.0  # FPS iniziale SAR

    def update_status(self):
        """Aggiorna la barra di stato con gli FPS di MFD e SAR (e i secondi per immagine per SAR)."""
        status_text = f"Pronto | MFD FPS: {self.mfd_fps:.2f} | SAR FPS: {self.sar_fps:.2f}"

        # Aggiungi i secondi per immagine per SAR
        if self.sar_fps > 0:
            seconds_per_image = 1 / self.sar_fps
            status_text += f" ({seconds_per_image:.2f} s/img)"
        else:
            status_text += " (N/A s/img)"

        self.statusbar.config(text=status_text)

    def set_status(self, message):
        """Aggiorna la barra di stato."""
        self.statusbar.config(text=message)

    def update_image_mode(self):
        """Aggiorna la modalità immagine in base al valore della checkbox."""
        try:
            if self.test_image_var.get() == 1:
                self.core.generate_test_images()  # Rigenera le immagini di test
                self.display.set_initial_sar_image()
            else:
                self.display.set_initial_sar_image()
            self.display.update_mfd()
            self.display.update_sar()
        except Exception as e:
            logging.exception("Errore in update_image_mode")
            self.root.after(0, lambda: self.statusbar.config(text=f"Errore in update_image_mode: {e}"))

    def update_sar_size(self, event=None):
        """Aggiorna la dimensione dell'immagine SAR in base alla selezione della combobox."""
        try:
            selected_size = self.sar_size_combo.get()
            if selected_size == "1:1":
                self.display.sar_display_width = SAR_WIDTH
                self.display.sar_display_height = SAR_HEIGHT
            elif selected_size == "1:2":
                self.display.sar_display_width = INITIAL_SAR_WIDTH
                self.display.sar_display_height = INITIAL_SAR_HEIGHT
            elif selected_size == "1:3":
                self.display.sar_display_width = SAR_WIDTH // 3
                self.display.sar_display_height = SAR_HEIGHT // 3
            elif selected_size == "1:5":
                self.display.sar_display_width = SAR_WIDTH // 5
                self.display.sar_display_height = SAR_HEIGHT // 5
            elif selected_size == "1:10":
                self.display.sar_display_width = SAR_WIDTH // 10
                self.display.sar_display_height = SAR_HEIGHT // 10

            if self.test_image_var.get() == 1:
                self.core.generate_test_images()  # Rigenera le immagini di test
            else:
                self.display.set_initial_sar_image()

            self.display.update_sar()  # Aggiorna la visualizzazione

            # Aggiorna la posizione della finestra SAR nel caso in cui la dimensione sia cambiata
            self.display.sar_x = self.tkinter_x + TKINTER_MIN_WIDTH + 20  # Posiziona la finestra SAR a destra di Tkinter
            self.display.sar_y = 10

        except Exception as e:
            logging.exception("Errore in update_sar_size")
            self.root.after(0, lambda: self.statusbar.config(text=f"Errore in update_sar_size: {e}"))

    def update_contrast(self, value):
        """Aggiorna il contrasto dell'immagine SAR in base al valore dello slider."""
        try:
            self.core.sar_contrast = float(value)
            self.processing.update_brightness_contrast_lut()
            self.processing.update_sar_display()
        except ValueError as e:
            logging.warning(f"Valore di contrasto non valido: {value}")
            self.root.after(0, lambda: self.statusbar.config(text=f"Valore di contrasto non valido: {e}"))
        except Exception as e:
            logging.exception("Errore in update_contrast")
            self.root.after(0, lambda: self.statusbar.config(text=f"Errore in update_contrast: {e}"))

    def update_brightness(self, value):
        """Aggiorna la luminosità dell'immagine SAR in base al valore dello slider."""
        try:
            self.core.sar_brightness = int(float(value))
            self.processing.update_brightness_contrast_lut()
            self.processing.update_sar_display()
        except ValueError as e:
            logging.warning(f"Valore di luminosità non valido: {value}")
            self.root.after(0, lambda: self.statusbar.config(text=f"Valore di luminosità non valido: {e}"))
        except Exception as e:
            logging.exception("Errore in update_brightness")
            self.root.after(0, lambda: self.statusbar.config(text=f"Errore in update_brightness: {e}"))

    def update_sar_palette(self, event=None):
        """Aggiorna la palette colori dell'immagine SAR in base alla selezione della combobox."""
        try:
            self.core.sar_palette = self.palette_combo.get()
            self.processing.update_sar_display()
        except Exception as e:
            logging.exception("Errore in update_sar_palette")
            self.root.after(0, lambda: self.statusbar.config(text=f"Errore in update_sar_palette: {e}"))

    def update_mouse_latlon_label(self, latitude, longitude):
        """Aggiorna la label Tkinter con la latitudine e la longitudine del mouse."""
        self.mouse_latlon_label.config(text=f"Mouse: Lat={latitude:.4f}, Lon={longitude:.4f}")

    def set_sar_info(self, message):
        """Aggiorna la label delle informazioni SAR in Tkinter."""
        self.sar_info_label.config(text=message)


class Processing:
    """Contiene le funzioni di elaborazione delle immagini SAR."""

    def __init__(self, core, display):
        """Inizializza il modulo Processing."""
        self.core = core
        self.display = display
        #self.set_status = set_status_func
        self.brightness_contrast_lut = None  # Inizializza LUT

    def update_brightness_contrast_lut(self):
        """Calcola la Look-Up Table (LUT) per la regolazione di luminosità e contrasto."""
        try:
            self.brightness_contrast_lut = np.array([
                np.clip(i * self.core.sar_contrast + self.core.sar_brightness, 0, 255).astype(np.uint8) for i in range(256)
            ])
        except Exception as e:
            logging.exception("Errore in update_brightness_contrast_lut")
            self.display.root.after(0, lambda: self.display.ui.statusbar.config(text=f"Errore in update_brightness_contrast_lut: {e}"))

    def apply_color_palette(self, image, palette):
        """Applica la palette colori specificata all'immagine."""
        try:
            colormap = getattr(cv2, f"COLORMAP_{palette.upper()}")
            colorized_image = cv2.applyColorMap(image, colormap)
            return colorized_image
        except AttributeError as e:
            error_message = f"Colormap '{palette}' non trovata in OpenCV."
            logging.error(error_message)
            self.display.root.after(0, lambda: self.display.ui.statusbar.config(text=error_message))
            return image  # Restituisce l'immagine originale se la colormap non è valida
        except Exception as e:
            logging.exception("Errore durante l'applicazione della colormap")
            self.display.root.after(0, lambda: self.display.ui.statusbar.config(text=f"Errore durante l'applicazione della colormap: {e}"))
            return image

    def pixel_to_georeference(self, x, y):
        """Converte le coordinate dei pixel in latitudine e longitudine."""
        try:
            # Converti le coordinate del pixel in coordinate normalizzate (0.0 - 1.0)
            normalized_x = x / self.display.sar_display_width
            normalized_y = y / self.display.sar_display_height

            # Converti le coordinate normalizzate in chilometri dal centro
            km_x = (normalized_x - 0.5) * SAR_IMAGE_SIZE_KM
            km_y = (0.5 - normalized_y) * SAR_IMAGE_SIZE_KM  # L'asse Y è invertito

            # Converti i chilometri in offset di latitudine e longitudine
            lat_offset = km_y / 111.0  # Approssimazione da km a latitudine
            lon_offset = km_x / (111.0 * np.cos(np.radians(SAR_CENTER_LAT)))  # Approssimazione da km a longitudine

            # Calcola la latitudine e la longitudine
            latitude = SAR_CENTER_LAT + lat_offset
            longitude = SAR_CENTER_LON + lon_offset

            return latitude, longitude
        except Exception as e:
            logging.exception("Errore in pixel_to_georeference")
            self.display.root.after(0, lambda: self.display.ui.statusbar.config(text=f"Errore in pixel_to_georeference: {e}"))
            return None, None

    def update_sar_display(self):
        """Applica le regolazioni di contrasto, luminosità e palette all'immagine SAR e attiva l'aggiornamento SAR."""
        try:
            # Applica contrasto e luminosità usando LUT
            #if self.ui.test_image_var.get() == 1:
            if True:
                if self.core.test_sar_image is not None:
                    normalized_image = cv2.normalize(self.core.test_sar_image, None, 0, 255, cv2.NORM_MINMAX, cv2.CV_8U)
                    adjusted_image = cv2.LUT(normalized_image, self.brightness_contrast_lut)
                else:
                    adjusted_image = np.zeros_like(self.display.resized_sar)
            else:
                if self.display.current_sar is not None:
                    adjusted_image = cv2.LUT(self.display.current_sar, self.brightness_contrast_lut)
                else:
                    adjusted_image = np.zeros_like(self.display.resized_sar)

            # Applica la palette colori
            if self.core.sar_palette != "GRAY":
                adjusted_image = self.apply_color_palette(adjusted_image, self.core.sar_palette)

            self.display.adjusted_sar = adjusted_image.copy()  # Memorizza l'immagine regolata

            # Ridimensiona l'immagine regolata
            self.display.resized_sar = cv2.resize(self.display.adjusted_sar, (self.display.sar_display_width, self.display.sar_display_height),
                                           interpolation=cv2.INTER_AREA)

            # Forza l'aggiornamento della visualizzazione dell'immagine
            self.display.put_sar_queue(self.display.resized_sar)
        except Exception as e:
            logging.exception("Errore in update_sar_display")
            self.display.root.after(0, lambda: self.display.ui.statusbar.config(text=f"Errore in update_sar_display: {e}"))


class Display:
    """Gestisce la visualizzazione delle immagini MFD e SAR."""

    def __init__(self, root, core, processing, ui):
        """Inizializza il modulo Display."""
        self.root = root
        self.core = core
        self.processing = processing
        self.ui = ui
        #self.set_status = set_status_func
        self.mfd_window_initialized = False
        self.sar_window_initialized = False
        self.sar_mouse_callback_set = False
        self.current_sar = None
        self.adjusted_sar = None
        self.mfd_start_time = time.time()
        self.sar_update_time = time.time()
        self.mfd_frame_count = 0
        self.sar_frame_count = 0
        self.sar_display_width = INITIAL_SAR_WIDTH
        self.sar_display_height = INITIAL_SAR_HEIGHT
        self.resized_sar = np.zeros((self.sar_display_height, self.sar_display_width),dtype=np.uint8)
        self.mfd_x = 10
        self.mfd_y = 10
        self.sar_x = 514
        self.sar_y = 10
        self.last_mouse_update = time.time()
        #self.mfd_fps = mfd_fps
        #self.sar_fps = sar_fps

    def update_mfd(self):
        """Aggiorna l'immagine MFD con la traslazione e calcola gli FPS, limitando a MFD_FPS."""
        try:
            start_time = time.time()
            delay = max(1, int(1000 / MFD_FPS))  # Imposta un valore di ritardo predefinito

            #if self.ui.test_image_var.get() == 1:
                # Usa l'immagine di test e simula la traslazione
            if self.core.test_mfd_image is not None and self.core.test_mfd_image.size > 0:
                self.core.mfd_x_offset = (self.core.mfd_x_offset + 1) % MFD_WIDTH
                translated_image = np.roll(self.core.test_mfd_image, -self.core.mfd_x_offset, axis=1)
            else:
                translated_image = np.zeros((MFD_HEIGHT, MFD_WIDTH), dtype=np.uint8)  # Immagine nera
            #else:
                # Usa l'immagine caricata
                #if self.core.mfd_image_data is not None and self.core.mfd_image_data.size > 0:
                    #translated_image = self.core.mfd_image_data
                    #delay = 1  # Aggiorna il più velocemente possibile
                #else:
                    #translated_image = np.zeros((MFD_HEIGHT, MFD_WIDTH), dtype=np.uint8)  # Immagine nera

            # Crea e sposta la finestra solo una volta
            if not self.mfd_window_initialized:
                cv2.imshow("MFD", translated_image)
                try:
                    cv2.moveWindow("MFD", self.mfd_x, self.mfd_y)
                    self.mfd_window_initialized = True
                except cv2.error as e:
                    logging.error(f"Errore durante lo spostamento della finestra MFD all'inizializzazione: {e}")
                    self.root.after(0, lambda: self.ui.statusbar.config(text=f"Errore durante lo spostamento della finestra MFD: {e}"))
            else:
                cv2.imshow("MFD", translated_image)  # Aggiorna solo l'immagine

            # Calcola gli FPS
            self.mfd_frame_count += 1
            elapsed_time = time.time() - self.mfd_start_time
            if elapsed_time >= 1.0:  # Aggiorna gli FPS ogni secondo
                self.mfd_fps = self.mfd_frame_count / elapsed_time
                self.mfd_start_time = time.time()
                self.mfd_frame_count = 0
                self.ui.update_status()  # Aggiorna la barra di stato

            self.root.after(delay, self.update_mfd)  # Pianifica il prossimo aggiornamento

        except Exception as e:
            logging.exception("Errore in update_mfd")
            self.root.after(0, lambda: self.ui.statusbar.config(text=f"Errore in update_mfd: {e}"))

    def update_sar(self):
        """Aggiorna l'immagine SAR con la traslazione."""
        try:
            start_time = time.time()

            # Ottieni l'immagine corretta in base alla modalità di test
            #if self.ui.test_image_var.get() == 1:
            if True:
                if self.resized_sar is not None and self.resized_sar.size > 0:
                    image_to_display = self.resized_sar.copy()
                    self.core.sar_x_offset = (self.core.sar_x_offset + 1) % self.sar_display_width
                    image_to_display = np.roll(image_to_display, -self.core.sar_x_offset, axis=1)
            else:
                image_to_display = np.zeros((self.sar_display_height, self.sar_display_width),
                                                 dtype=np.uint8)  # Immagine nera
            #else:
                #if self.resized_sar is not None and self.resized_sar.size > 0:
                    #image_to_display = self.resized_sar.copy()
                #else:
                    #image_to_display = np.zeros((self.sar_display_height, self.sar_display_width),
                                                     #dtype=np.uint8)  # Immagine nera

            # Crea e sposta la finestra solo una volta
            if not self.sar_window_initialized:
                cv2.imshow("SAR", image_to_display)
                try:
                    cv2.moveWindow("SAR", self.sar_x, self.sar_y)
                    self.sar_window_initialized = True
                except cv2.error as e:
                    logging.error(f"Errore durante lo spostamento della finestra SAR all'inizializzazione: {e}")
                    self.root.after(0, lambda: self.ui.statusbar.config(text=f"Errore durante lo spostamento della finestra SAR: {e}"))
                # Imposta la callback del mouse *dopo* che la finestra è stata creata
                cv2.setMouseCallback("SAR", self.sar_mouse_callback)
                self.sar_mouse_callback_set = True
            else:
                cv2.imshow("SAR", image_to_display)  # Aggiorna solo l'immagine

            # Calcola gli FPS SAR
            self.sar_frame_count += 1
            elapsed_time = time.time() - self.sar_update_time
            if elapsed_time >= 5.0:  # Aggiorna gli FPS ogni 5 secondi
                self.sar_fps = self.sar_frame_count / elapsed_time
                self.sar_update_time = time.time()
                self.sar_frame_count = 0
                self.ui.update_status()  # Aggiorna la barra di stato

            processing_time = time.time() - start_time
            delay = max(1, int(5000) - int(processing_time))
            self.root.after(delay, self.update_sar)

        except Exception as e:
            logging.exception("Errore in update_sar")
            self.root.after(0, lambda: self.ui.statusbar.config(text=f"Errore in update_sar: {e}"))

    def set_initial_sar_image(self):
        """Imposta l'immagine SAR iniziale per la visualizzazione."""
        try:
            self.current_sar = cv2.normalize(self.core.sar_image_data, None, 0, 255, cv2.NORM_MINMAX,
                                              cv2.CV_8U)  # SAR corrente per la visualizzazione
            self.adjusted_sar = self.current_sar.copy()  # Crea una copia per le regolazioni di contrasto/luminosità
            self.resized_sar = cv2.resize(self.adjusted_sar, (self.sar_display_width, self.sar_display_height),
                                           interpolation=cv2.INTER_AREA)
        except Exception as e:
            logging.exception("Errore in set_initial_sar_image")
            # Crea un'immagine nera come fallback
            self.resized_sar = np.zeros((self.sar_display_height, self.sar_display_width), dtype=np.uint8)
            self.root.after(0, lambda: self.ui.statusbar.config(text=f"Errore: {e}"))

    def sar_mouse_callback(self, event, x, y, flags, param):
        """Funzione di callback per gli eventi del mouse sulla finestra SAR."""
        try:
            if event == cv2.EVENT_MOUSEMOVE:
                # Converti le coordinate del pixel in latitudine e longitudine
                latitude, longitude = self.processing.pixel_to_georeference(x, y)
                # Aggiorna la label Tkinter con le nuove coordinate
                self.ui.update_mouse_latlon_label(latitude, longitude)

        except Exception as e:
            logging.exception("Errore in sar_mouse_callback")
            self.root.after(0, lambda: self.ui.statusbar.config(text=f"Errore in sar_mouse_callback: {e}"))

    def put_sar_queue(self, image):
        """Funzione per mettere l'immagine nella coda."""
        try:
            self.core.sar_queue.put(image, block=False)
        except queue.Full:
            logging.warning("La coda SAR è piena, frame scartato")
            pass
        except Exception as e:
            logging.exception("Errore in put_sar_queue")
            self.root.after(0, lambda: self.ui.statusbar.config(text=f"Errore in put_sar_queue: {e}"))

    def process_sar_queue(self):
        """Questa funzione ottiene la nuova immagine Sar dalla coda e la visualizza."""
        try:
            try:
                image = self.core.sar_queue.get(block=False)
                cv2.imshow("SAR", image)
            except queue.Empty:
                pass
        except Exception as e:
            logging.exception("Errore in process_sar_queue")
            self.root.after(0, lambda: self.ui.statusbar.config(text=f"Errore in process_sar_queue: {e}"))
        finally:
            self.root.after(1, self.process_sar_queue)  # Loop nel thread principale

    def process_mouse_queue(self):
        """Elabora la coda delle coordinate del mouse e aggiorna la label Tkinter."""
        try:
            try:
                latitude, longitude = self.core.mouse_queue.get(block=False)
                # Limita la velocità di aggiornamento a 10 volte al secondo (intervallo di 100 ms)
                current_time = time.time()
                if current_time - self.last_mouse_update >= 0.1:
                    self.ui.update_mouse_latlon_label(latitude, longitude)
                    self.last_mouse_update = current_time
            except queue.Empty:
                pass
        except Exception as e:
            logging.exception("Errore in process_mouse_queue")
            self.root.after(0, lambda: self.ui.statusbar.config(text=f"Errore in process_mouse_queue: {e}"))
        finally:
            self.root.after(1, self.process_mouse_queue)


class App:
    def __init__(self, root):
        """Inizializza l'applicazione principale."""
        self.root = root

        # --- Inizializza FPS ---
        self.mfd_fps = 0.0
        self.sar_fps = 0.0

        # --- Crea istanze dei moduli ---
        self.core = Core()
        self.display = Display(root, self.core, None, None)  # Processing e UI vengono passati dopo
        self.processing = Processing(self.core, self.display)
        self.ui = UI(root, self.core, self.processing, self.display)

        # Completa l'inizializzazione di Display e Processing
        self.display.processing = self.processing
        self.display.ui = self.ui

        # --- Inizializzazione ---
        self.core.mfd_image_data = self.core.load_image(MFD_IMAGE_PATH, expected_dtype=np.uint8)
        self.core.sar_image_data = self.core.load_image(SAR_IMAGE_PATH, expected_dtype=SAR_DATA_TYPE)
        self.core.generate_test_images()
        self.display.set_initial_sar_image()

        # --- Avvia visualizzazione immagini ---
        self.update_mfd()
        self.update_sar()
        self.process_sar_queue()
        self.process_mouse_queue()

        # Avvia il thread di caricamento immagini
        self.image_loading_thread = threading.Thread(target=self.load_images_and_generate_test_data, daemon=True)
        self.image_loading_thread.start()

    def load_images_and_generate_test_data(self):
        """Carica immagini e genera dati di test in un thread separato."""
        self.ui.set_status("Caricamento immagini...")
        try:
            self.core.mfd_image_data = self.core.load_image(MFD_IMAGE_PATH, expected_dtype=np.uint8)
            self.core.sar_image_data = self.core.load_image(SAR_IMAGE_PATH, expected_dtype=SAR_DATA_TYPE)
            self.core.generate_test_images()  # Genera immagini di test iniziali
            self.display.set_initial_sar_image()  # Imposta l'immagine SAR iniziale
            self.ui.set_status("Pronto")  # Torna allo stato pronto
        except Exception as e:
            logging.exception("Errore in load_images_and_generate_test_data")
            self.root.after(0, lambda: self.ui.statusbar.config(text=f"Errore: {e}"))

    def update_mfd(self):
        self.display.update_mfd()
        self.root.after(1, self.update_mfd)

    def update_sar(self):
        self.display.update_sar()
        self.root.after(1, self.update_sar)

    def process_sar_queue(self):
        self.display.process_sar_queue()
        self.root.after(1, self.process_sar_queue)

    def process_mouse_queue(self):
        self.display.process_mouse_queue()
        self.root.after(1, self.process_mouse_queue)


    #def set_status(self, message):
        #"""Aggiorna la barra di stato."""
        #self.ui.set_status(message)


if __name__ == "__main__":
    root = tk.Tk()
    app = App(root)
    app.ui.set_status("Partenza")

    root.mainloop()
    cv2.destroyAllWindows()   