# FlightMonitor/map/map_render_manager.py
"""
Manages the background worker thread responsible for rendering map images.
Handles communication (requests, results) with the worker thread.
"""
import threading
import queue
import time
import os
from typing import Optional, Dict, Any, Callable, Tuple, TYPE_CHECKING

try:
    from PIL import Image, ImageTk
    ImageType = Image.Image
    PhotoImageType = ImageTk.PhotoImage
except ImportError:
    ImageType = Any
    PhotoImageType = Any

from flightmonitor.utils.logger import get_logger
from flightmonitor.data import config as app_config

if TYPE_CHECKING:
    from flightmonitor.map.map_canvas_manager import MapCanvasManager

module_logger = get_logger(__name__)

MAP_WORKER_QUEUE_TIMEOUT_S = 0.1
# MODIFICATO: Sostituiti i tipi di richiesta
RENDER_REQUEST_BASE_MAP = "render_base_map"  # Richiesta per mappa di base + overlay
RENDER_REQUEST_OVERLAY = "render_overlay"    # Richiesta per solo overlay
RENDER_REQUEST_TYPE_SHUTDOWN = "shutdown_worker"

# Cartella per il debug delle immagini renderizzate
DEBUG_IMAGE_SAVE_DIR = "map_render_debug"


class MapRenderManager:
    """
    Manages a dedicated worker thread for processing map rendering requests.
    Handles queuing of requests, retrieving rendered results, and worker shutdown.
    """

    def __init__(self):
        self._map_render_request_queue: queue.Queue[Dict[str, Any]] = queue.Queue(maxsize=5)
        self._map_render_result_queue: queue.Queue[Dict[str, Any]] = queue.Queue(maxsize=5)
        self._map_worker_stop_event: threading.Event = threading.Event()
        self._map_worker_thread: Optional[threading.Thread] = None
        self._last_render_request_id: int = 0
        self._expected_render_id_gui: int = 0

        self._render_pipeline_callable: Optional[
            Callable[..., Tuple[Optional[PhotoImageType], Optional[PhotoImageType], Optional[Tuple[float, ...]], Optional[str]]]
        ] = None

        # Crea la cartella di debug se necessario
        if getattr(app_config, "DEBUG_SAVE_RENDERED_IMAGES", False):
            try:
                os.makedirs(DEBUG_IMAGE_SAVE_DIR, exist_ok=True)
                module_logger.info(f"Debug image save directory is '{DEBUG_IMAGE_SAVE_DIR}'")
            except OSError as e:
                module_logger.error(f"Could not create debug image directory: {e}")

        module_logger.debug("MapRenderManager initialized.")

    def set_render_pipeline_callable(
        self,
        render_pipeline_callable: Callable[
            ..., Tuple[Optional[PhotoImageType], Optional[PhotoImageType], Optional[Tuple[float, ...]], Optional[str]]
        ],
    ):
        self._render_pipeline_callable = render_pipeline_callable
        module_logger.debug("MapRenderManager: Render pipeline callable set.")

    def start_worker(self):
        if self._map_worker_thread is not None and self._map_worker_thread.is_alive():
            module_logger.warning("MapRenderManager: Map worker thread already running.")
            return
        self._map_worker_stop_event.clear()
        self._map_worker_thread = threading.Thread(
            target=self._map_render_worker_target,
            name="MapRenderWorker",
            daemon=True,
        )
        self._map_worker_thread.start()
        module_logger.info("MapRenderManager: Map rendering worker thread started successfully.")

    def shutdown_worker(self):
        module_logger.info("MapRenderManager: Shutdown worker requested.")
        self._map_worker_stop_event.set()
        try:
            self._map_render_request_queue.put_nowait(
                {"type": RENDER_REQUEST_TYPE_SHUTDOWN, "request_id": -999}
            )
        except queue.Full:
            pass
        if self._map_worker_thread and self._map_worker_thread.is_alive():
            self._map_worker_thread.join(timeout=2.0)
        self._map_worker_thread = None
        while not self._map_render_request_queue.empty():
            try:
                self._map_render_request_queue.get_nowait()
            except Exception:
                break
        while not self._map_render_result_queue.empty():
            try:
                self._map_render_result_queue.get_nowait()
            except Exception:
                break
        module_logger.info("MapRenderManager: Worker shutdown sequence complete.")

    def _save_debug_image(self, image: ImageType, request_id: int, image_type: str):
        """Helper to save images for debugging."""
        if not getattr(app_config, "DEBUG_SAVE_RENDERED_IMAGES", False) or image is None:
            return
        try:
            filename = f"{DEBUG_IMAGE_SAVE_DIR}/req_{request_id}_{image_type}_{int(time.time())}.png"
            image.save(filename, "PNG")
        except Exception as e:
            module_logger.error(f"Failed to save debug image {filename}: {e}")

    def _map_render_worker_target(self):
        thread_name = threading.current_thread().name
        module_logger.debug(f"{thread_name}: Worker thread target loop started.")
        if not self._render_pipeline_callable:
            module_logger.critical(f"{thread_name}: Render pipeline callable not set. Worker cannot function. Exiting.")
            return

        while not self._map_worker_stop_event.is_set():
            try:
                request_data = self._map_render_request_queue.get(timeout=MAP_WORKER_QUEUE_TIMEOUT_S)
                request_type = request_data.get("type")
                request_id = request_data.get("request_id", -1)

                if request_type == RENDER_REQUEST_TYPE_SHUTDOWN:
                    break

                base_map_pil, overlay_pil, map_bounds_result, error_msg = None, None, None, None
                
                # Unpack common parameters
                render_params = request_data.get("params", {})

                if request_type == RENDER_REQUEST_BASE_MAP:
                    base_map_pil, overlay_pil, map_bounds_result, error_msg = self._render_pipeline_callable(
                        render_params, render_base=True, render_overlay=True
                    )
                    # Now we save the PIL images, which have the .save() method
                    self._save_debug_image(base_map_pil, request_id, "base")
                    self._save_debug_image(overlay_pil, request_id, "overlay_full")

                elif request_type == RENDER_REQUEST_OVERLAY:
                    # For overlay-only, we don't need to re-render the base map
                    _, overlay_pil, _, error_msg = self._render_pipeline_callable(
                        render_params, render_base=False, render_overlay=True
                    )
                    self._save_debug_image(overlay_pil, request_id, "overlay_only")

                else:
                    error_msg = f"Unknown request type: {request_type}"

                if self._map_worker_stop_event.is_set():
                    break

                # Convert to PhotoImage for the GUI thread
                base_map_photo_result = ImageTk.PhotoImage(base_map_pil) if base_map_pil else None
                overlay_photo_result = ImageTk.PhotoImage(overlay_pil) if overlay_pil else None

                result_payload = {
                    "request_id": request_id,
                    "type": request_type,
                    "base_map_photo": base_map_photo_result,
                    "overlay_photo": overlay_photo_result,
                    "map_geo_bounds": map_bounds_result,
                    "error": error_msg,
                }
                self._map_render_result_queue.put_nowait(result_payload)

            except queue.Empty:
                continue
            except Exception as e:
                module_logger.exception(f"{thread_name}: Unhandled exception in worker loop: {e}")
                time.sleep(0.5)

        module_logger.info(f"{thread_name}: Worker thread target loop finished.")

    def put_render_request(self, request_type: str, render_params: Dict[str, Any]) -> Optional[int]:
        self._last_render_request_id += 1
        current_request_id = self._last_render_request_id
        
        request_payload = {
            "request_id": current_request_id,
            "type": request_type,
            "params": render_params
        }
        
        try:
            self._map_render_request_queue.put_nowait(request_payload)
            return current_request_id
        except queue.Full:
            module_logger.warning(f"MapRenderManager: Render request queue FULL. Request ID {current_request_id} was DROPPED.")
            return None

    def get_render_result(self) -> Optional[Dict[str, Any]]:
        try:
            return self._map_render_result_queue.get_nowait()
        except queue.Empty:
            return None

    def get_expected_gui_render_id(self) -> int:
        return self._expected_render_id_gui

    def set_expected_gui_render_id(self, request_id: int):
        self._expected_render_id_gui = request_id

    def is_worker_alive(self) -> bool:
        return self._map_worker_thread is not None and self._map_worker_thread.is_alive()