# radar_data_reader/core/file_analyzer.py

"""
Provides tools for analyzing the structural integrity of .out files.
This module implements a sequential, marker-to-marker reading logic with
validation of declared block sizes and extraction of key metadata like batch_counter.
"""

from pathlib import Path
from typing import List, Dict, Any, Optional
import numpy as np
from collections import defaultdict
import multiprocessing as mp

from ..utils import logger
from .data_structures import BLOCK_TYPE_MAP, LEGACY_BLOCK_MARKER, FW_BLOCK_MARKER
# --- NUOVO IMPORT -- -
from .struct_utils import extract_field_from_block, BLOCK_STRUCTURE_REGISTRY
from .structures.ge_header_structures import GeHeader


log = logger.get_logger(__name__)

# Constants for header offsets (in 32-bit words)
LEGACY_NAME_OFFSET = 17
LEGACY_SIZE_OFFSET = 5
FW_NAME_OFFSET = 2
FW_SIZE_OFFSET = 5

class FileStructureAnalyzer:
    """
    Analyzes the block structure of a .out file, extracting metadata for debugging.
    """
    def __init__(self, file_path: Path, config: Dict[str, Any]):
        self.file_path = file_path
        self.config = config
        self.data_vector: Optional[np.ndarray] = None
        self.report: List[Dict[str, Any]] = []
        self.stats: Dict[str, Any] = {
            "total_blocks_found": 0, "size_mismatches": 0,
            "block_type_counts": defaultdict(int),
        }
        self.all_marker_positions: Optional[np.ndarray] = None
        self.last_header_fields: Dict[str, Any] = {}

    def analyze(self, result_queue: mp.Queue) -> List[Dict[str, Any]]:
        if not self._load_and_find_all_markers(result_queue):
            return self.report
        self._scan_marker_to_marker(result_queue)
        self.stats["block_type_counts"] = dict(self.stats["block_type_counts"])
        return self.report

    def _load_and_find_all_markers(self, result_queue: mp.Queue) -> bool:
        try:
            log.info(f"[Analyzer] Loading data from {self.file_path}...")
            self.data_vector = np.fromfile(str(self.file_path), dtype="<u4")
            log.info(f"[Analyzer] Loaded {self.data_vector.size} 32-bit words.")

            legacy_indices = np.where(self.data_vector == LEGACY_BLOCK_MARKER)[0]
            fw_indices = np.where(self.data_vector == FW_BLOCK_MARKER)[0]
            confirmed_legacy = legacy_indices[np.where(np.diff(legacy_indices) == 1)[0]]
            confirmed_fw = fw_indices[np.where(np.diff(fw_indices) == 1)[0]]
            self.all_marker_positions = np.sort(np.concatenate((confirmed_legacy, confirmed_fw)))
            
            if self.all_marker_positions.size == 0:
                log.error("[Analyzer] No valid block markers found in the file.")
                self.report.append({"type": "critical", "message": "No valid block markers found."})
                return False
            
            log.info(f"[Analyzer] Found {self.all_marker_positions.size} block start markers.")
            result_queue.put({"type": "start", "total": self.all_marker_positions.size})
            return True
        except Exception as e:
            log.error(f"[Analyzer] Failed to load file: {e}", exc_info=True)
            self.report.append({"type": "error", "message": f"Failed to load file: {e}"})
            return False

    def _scan_marker_to_marker(self, result_queue: mp.Queue):
        if self.all_marker_positions is None or self.data_vector is None: return

        num_markers = self.all_marker_positions.size
        for i in range(num_markers):
            current_pos = self.all_marker_positions[i]
            
            if i > 0 and i % 1000 == 0:
                result_queue.put({"type": "analysis_progress", "current": i, "total": num_markers})

            header_info = self._read_block_header(current_pos)
            if not header_info:
                self.report.append({"type": "error", "offset": hex(current_pos * 4), "message": "Marker found, but header is invalid."})
                continue

            self.stats["total_blocks_found"] += 1
            self.stats["block_type_counts"][header_info['name']] += 1

            next_marker_pos = self.all_marker_positions[i+1] if i + 1 < num_markers else self.data_vector.size
            real_block_size_bytes = (next_marker_pos - current_pos) * 4
            
            entry = { "type": "block", "offset": hex(current_pos * 4), "real_block_size_bytes": real_block_size_bytes, "mismatch": False, **header_info }

            if header_info.get('name') == 'DSPHDRIN':
                self.last_header_fields = {
                    k: v for k, v in header_info.items()
                    if k in ['batch_counter', 'master_mode', 'operation_mode', 'waveform', 'range_scale']
                }
            
            if header_info.get('name') == 'DSPS':
                entry.update(self.last_header_fields)

            if header_info['marker_type'] == LEGACY_BLOCK_MARKER and header_info['size_bytes'] > real_block_size_bytes:
                entry["mismatch"] = True
                entry["message"] = f"Declared payload ({header_info['size_bytes']} B) > real size ({real_block_size_bytes} B)."
                self.stats["size_mismatches"] += 1
            
            self.report.append(entry)

    def _read_block_header(self, pos: int) -> Optional[Dict[str, Any]]:
        if self.data_vector is None or pos + LEGACY_NAME_OFFSET + 1 >= self.data_vector.size:
            return None
        
        try:
            marker_type = self.data_vector[pos]
            header_info = {"name": "UNKNOWN", "size_bytes": 0, "marker_type": marker_type}

            if marker_type == LEGACY_BLOCK_MARKER:
                name_id = self.data_vector[pos + LEGACY_NAME_OFFSET]
                discriminator = self.data_vector[pos + LEGACY_NAME_OFFSET + 1]
                header_info["size_bytes"] = int(self.data_vector[pos + LEGACY_SIZE_OFFSET])
                
                block_name = "UNKNOWN"
                if name_id == 1599362131 and discriminator == 4276556: block_name = "STT"
                elif name_id == 1599362125 and discriminator == 4276556: block_name = "MTT"
                elif name_id == 1213223748 and discriminator == 1431261764: block_name = "DSPHDROUT"
                else: block_name = BLOCK_TYPE_MAP.get(name_id, f"UNKNOWN_ID_{name_id}")
                header_info["name"] = block_name

                # --- MODIFIED LOGIC ---
                registry_entry = BLOCK_STRUCTURE_REGISTRY.get(block_name)
                if block_name == "DSPHDRIN" and registry_entry and registry_entry[0] == GeHeader:
                    block_data = self.data_vector[pos:]
                    
                    fields_to_extract = {
                        "batch_counter": "signal.bacth_counter",
                        "master_mode": "mode.master_mode",
                        "operation_mode": "mode.operation_mode",
                        "waveform": "mode.waveform",
                        "range_scale": "mode.range_scale"
                    }
                    
                    for key, path in fields_to_extract.items():
                        value = extract_field_from_block(block_data, block_name, path)
                        if value is not None:
                            header_info[key] = value

            elif marker_type == FW_BLOCK_MARKER:
                name_id = self.data_vector[pos + FW_NAME_OFFSET]
                header_info["size_bytes"] = int(self.data_vector[pos + FW_SIZE_OFFSET])
                header_info["name"] = BLOCK_TYPE_MAP.get(name_id, f"UNKNOWN_ID_{name_id}")
            else:
                return None

            return header_info
        except IndexError:
            return None
