# radar_data_reader/core/app_controller.py

import multiprocessing as mp
import csv
import json
import os
import subprocess
import sys
import re
from pathlib import Path
from typing import List, Any, Dict, Tuple, Optional, Callable
from tkinter import filedialog, messagebox
import tkinter as tk
import ctypes
import threading
import shutil
import time
from datetime import datetime
import queue

try:
    import pandas as pd
except ImportError:
    pd = None

from ..utils.config_manager import ConfigManager
from .file_reader import run_worker_process
from .cpp_runner import run_cpp_converter
from .data_structures import DataBatch, CtypesStructureBase
from .data_enums import ENUM_REGISTRY, get_enum_name
from .export_profiles import ExportProfile, ExportField
from .export_manager import ExportManager, ExportJob
from .flight_analyzer import FlightAnalyzer
from .out_processor import OutProcessor
from .segment_processor import SegmentProcessor

from ..utils import logger
from ..gui.profile_editor_window import ProfileEditorWindow
from ..gui.rec_config_window import RecConfigWindow
from ..gui.segment_export_config_window import SegmentExportConfigWindow
from ..gui.segment_processor_tab import SegmentProcessorTab


log = logger.get_logger(__name__)


def _get_value_from_path(batch: DataBatch, field: ExportField) -> Any:
    try:
        path = field.data_path
        if path == "batch_id": return batch.batch_id
        parts = re.split(r"\.|\[", path)
        current_obj = batch
        for part in parts:
            if current_obj is None: return "N/A"
            if part.endswith("]"):
                index_str = part[:-1]
                if not index_str.isdigit(): return "N/A"
                try:
                    current_obj = current_obj[int(index_str)]
                except (IndexError, TypeError): return "N/A"
            else:
                current_obj = getattr(current_obj, part, None)
        value = current_obj if current_obj is not None else "N/A"
        if field.translate_with_enum and isinstance(value, int):
            enum_path = re.sub(r"\[\d+\]", "", path)
            enum_class = ENUM_REGISTRY.get(enum_path)
            if enum_class: return get_enum_name(enum_class, value)
        return value
    except Exception as e:
        log.warning(f"Could not resolve path '{field.data_path}': {e}")
        return "N/A"


def _convert_ctypes_for_json(obj: Any) -> Any:
    if isinstance(obj, (int, float, str, bool)) or obj is None: return obj
    if isinstance(obj, (ctypes._SimpleCData)): return obj.value
    if isinstance(obj, CtypesStructureBase):
        result = {}
        for field_name, _ in obj._fields_:
            if field_name.startswith("_"): continue
            value = getattr(obj, field_name)
            result[field_name] = _convert_ctypes_for_json(value)
        return result
    if isinstance(obj, ctypes.Array): return [_convert_ctypes_for_json(item) for item in obj]
    return obj


class AppController:
    """The main controller of the application."""

    def __init__(self, config_manager: ConfigManager):
        self.config_manager = config_manager
        self.view: Optional[tk.Tk] = None
        self.is_processing = False
        self.worker_process: Optional[mp.Process] = None
        self.command_queue = mp.Queue()
        self.result_queue = mp.Queue()
        
        self.export_manager = ExportManager(self.result_queue)
        self.flight_analyzer = FlightAnalyzer(config_manager, self.result_queue)
        self.out_processor = OutProcessor(self.command_queue, self.result_queue)
        self.segment_processor: Optional[SegmentProcessor] = None
        
        self.active_export_profiles: Dict[str, ExportProfile] = {}
        self.output_file_handles: Dict[str, Any] = {}
        self.csv_writers: Dict[str, Any] = {}
        self.json_data_buffer: List[Dict[str, Any]] = []
        self.last_generated_out_file: Optional[Path] = None
        
        self.last_flight_summary_df: Optional["pd.DataFrame"] = None
        
        self.total_files_for_analysis: int = 0
        self.total_segments_for_export: int = 0

    def bind_view(self, view: Any):
        self.view = view
        self._load_initial_config()
        self.export_manager.start()

    def shutdown(self):
        log.info("Controller shutting down.")
        self.export_manager.stop()
        if self.is_processing:
            self.stop_processing()
        
        if self.worker_process and self.worker_process.is_alive():
            log.info(f"Shutting down worker process PID: {self.worker_process.pid}")
            self.worker_process.join(timeout=1.0)
            if self.worker_process.is_alive():
                self.worker_process.terminate()
                
        self._close_all_files()
        logger.shutdown_logging_system()

    def _load_initial_config(self):
        if last_file := self.config_manager.get("last_opened_out_file"):
            if Path(last_file).is_file(): self.view.out_filepath_var.set(last_file)
        self.on_out_config_changed()
        if last_dir := self.config_manager.get("last_out_output_dir"):
            self.view.out_output_dir_var.set(last_dir)
        if last_file := self.config_manager.get("last_opened_rec_file"):
            if Path(last_file).is_file(): self.view.rec_filepath_var.set(last_file)
        self.on_rec_config_changed()
        if last_dir := self.config_manager.get("last_rec_output_dir"):
            self.view.rec_output_dir_var.set(last_dir)
            
        if last_workspace_parent := self.config_manager.get("last_flight_workspace_parent_dir"):
            self.view.analyzer_workspace_dir_var.set(last_workspace_parent)
            
        profiles = self.config_manager.get_export_profiles()
        self.view.update_export_profiles(
            profiles=profiles, 
            active_out_profile=self.config_manager.get("active_out_export_profile_name")
        )
        
        sp_config = self.config_manager.get("segment_processor_config", {})
        self.view.segment_processor_tab.output_dir_var.set(sp_config.get("last_output_dir", ""))
        self.view.segment_processor_tab.create_separate_folders_var.set(sp_config.get("create_separate_folders", True))
        
        fa_options = self.config_manager.get("flight_analysis_options", {})
        self.view.aggregate_by_scale_var.set(fa_options.get("aggregate_by_scale", True))
        self.view.aggregate_by_waveform_var.set(fa_options.get("aggregate_by_waveform", True))

    def on_out_config_changed(self, *args):
        filepath_str = self.view.out_filepath_var.get()
        if not filepath_str: return
        p = Path(filepath_str)
        if not self.view.out_output_dir_var.get(): self.view.out_output_dir_var.set(str(p.parent))
        self.view.out_basename_var.set(p.stem)

    def on_rec_config_changed(self, *args):
        try:
            file_count = self.view.rec_file_count_var.get()
        except (tk.TclError, ValueError): return
        filepath_str = self.view.rec_filepath_var.get()
        if not filepath_str: return
        p = Path(filepath_str)
        if not self.view.rec_output_dir_var.get(): self.view.rec_output_dir_var.set(str(p.parent))
        base_stem = Path(p.stem).stem
        new_stem = f"{base_stem}-n{file_count}"
        self.view.rec_basename_var.set(new_stem)

    def select_output_dir(self, dir_var: tk.StringVar):
        initial_dir = dir_var.get() if dir_var.get() else None
        if new_dir := filedialog.askdirectory(initialdir=initial_dir, title="Select Output Directory"):
            dir_var.set(new_dir)

    def _select_file(self, path_var: tk.StringVar, file_types: List[Tuple[str, str]]):
        initial_dir = (Path(path_var.get()).parent if path_var.get() and Path(path_var.get()).exists() else None)
        if filepath := filedialog.askopenfilename(initialdir=initial_dir, filetypes=file_types):
            path_var.set(filepath)

    def select_out_file(self):
        self._select_file(self.view.out_filepath_var, [("Radar Output", "*.out"), ("All files", "*.*")])

    def select_rec_file(self):
        self._select_file(self.view.rec_filepath_var, [("Recorder Data", "*.rec"), ("All files", "*.*")])

    def _prepare_out_processor_files(self) -> bool:
        self.output_file_handles.clear()
        self.csv_writers.clear()
        self.active_export_profiles.clear()
        self.json_data_buffer.clear()
        try:
            output_dir, basename, profiles = Path(self.view.out_output_dir_var.get()), self.view.out_basename_var.get(), self.config_manager.get_export_profiles()
            use_full_path = self.view.out_use_full_path_var.get()
            
            if self.view.out_output_csv_var.get():
                profile = next((p for p in profiles if p.name == self.view.out_csv_profile_var.get()), None)
                if not profile: raise ValueError(f"CSV profile '{self.view.out_csv_profile_var.get()}' not found.")
                self.active_export_profiles["csv"] = profile
                
                csv_filename = f"{basename}_{profile.name}.csv"
                path = output_dir / csv_filename
                delimiter = "\t" if self.view.out_csv_use_tab_var.get() else ","
                
                fh = open(path, "w", encoding="utf-8", newline="")
                header = [field.data_path if use_full_path else field.column_name for field in profile.fields]
                csv_writer = csv.writer(fh, delimiter=delimiter)
                csv_writer.writerow(header)
                self.output_file_handles["csv"] = fh
                self.csv_writers["csv"] = csv_writer

            if self.view.out_output_json_var.get():
                profile = next((p for p in profiles if p.name == self.view.out_json_profile_var.get()), None)
                if not profile: raise ValueError(f"JSON profile '{self.view.out_json_profile_var.get()}' not found.")
                self.active_export_profiles["json"] = profile
            
            return True
        except (IOError, ValueError) as e:
            log.error(f"Failed to prepare output files: {e}")
            self._close_all_files()
            return False

    def start_out_processing(self):
        if self.is_processing: log.warning("Processing already in progress."); return
        if not all([self.view.out_filepath_var.get(), self.view.out_output_dir_var.get(), self.view.out_basename_var.get()]):
            log.error("Please set input file, output directory, and base filename."); return
        if not any([self.view.out_output_csv_var.get(), self.view.out_output_json_var.get()]):
            log.error("Please select at least one output format (CSV or JSON)."); return
        if not self._prepare_out_processor_files(): return

        self.is_processing = True
        self.view.start_processing_ui()
        
        filepath_str = self.view.out_filepath_var.get()
        self.config_manager.set("last_opened_out_file", filepath_str)
        self.config_manager.set("last_out_output_dir", self.view.out_output_dir_var.get())
        self.config_manager.set("active_out_export_profile_name", self.view.out_csv_profile_var.get())
        self.config_manager.save_config()

        active_profile = self.active_export_profiles.get("csv") or self.active_export_profiles.get("json")
        if not active_profile:
             log.error("No active export profile found for processing.")
             self.is_processing = False; self.view.update_ui_for_processing_state(False)
             return
        
        cpp_config = self.config_manager.get_cpp_converter_config()
        enable_profiling = cpp_config.get("enable_python_worker_profiling", False)
        
        self.out_processor.start_processing(Path(filepath_str), active_profile, enable_profiling)
        self.worker_process = self.out_processor.get_worker()
        self.view.poll_result_queue()

    def _build_cpp_command_list(self) -> List[str]:
        config = self.config_manager.get_cpp_converter_config()
        exe_path = config.get("cpp_executable_path")
        if not exe_path or not Path(exe_path).is_file(): raise ValueError("g_reconvert.exe path is not set or is invalid.")
        rec_file, output_dir, out_basename = self.view.rec_filepath_var.get(), self.view.rec_output_dir_var.get(), self.view.rec_basename_var.get()
        if not all([rec_file, output_dir, out_basename]): raise ValueError("Missing required paths for C++ converter (REC file or Output).")
        output_file_path = Path(output_dir) / f"{out_basename}.out"
        self.last_generated_out_file = output_file_path
        command = [exe_path, rec_file, f"/o={str(output_file_path)}", f"/n={self.view.rec_file_count_var.get()}"]
        if config.get("post_process"): command.append(f"/p={config.get('post_process_level', '1')}")
        if config.get("video_show"): command.append("/vshow")
        if config.get("video_save"): command.append("/vsave")
        if config.get("gps_save_track"): command.append("/gps")
        if config.get("silent_overwrite"): command.append("//o")
        log.info(f"Assembled C++ command: {' '.join(command)}")
        return command

    def start_rec_conversion(self):
        if self.is_processing: log.warning("A process is already running."); return
        try:
            command_list, output_dir = self._build_cpp_command_list(), self.view.rec_output_dir_var.get()
        except ValueError as e:
            log.error(f"Configuration error: {e}"); messagebox.showerror("Configuration Error", str(e), parent=self.view); return
        self.is_processing = True
        self.view.start_processing_ui()
        worker_args = (command_list, self.result_queue, output_dir)
        self._launch_worker(run_cpp_converter, worker_args)

    def _launch_worker(self, target_func, args_tuple: Tuple):
        while not self.command_queue.empty(): self.command_queue.get_nowait()
        while not self.result_queue.empty(): self.result_queue.get_nowait()
        self.worker_process = mp.Process(target=target_func, args=args_tuple, daemon=True)
        self.worker_process.start()
        self.view.poll_result_queue()
        
    def stop_processing(self):
        if not self.is_processing: return
        log.info("Stop requested by user.")
        if self.worker_process and self.worker_process.is_alive():
            self.command_queue.put("STOP")
        if self.segment_processor:
            pass

    def _close_all_files(self):
        for fh in self.output_file_handles.values():
            try: fh.close()
            except Exception as e: log.error(f"Error closing file handle {fh.name}: {e}")
        self.output_file_handles.clear()
        self.csv_writers.clear()

    def handle_data_batch(self, batch: DataBatch):
        use_full_path = self.view.out_use_full_path_var.get()
        if "csv" in self.csv_writers:
            profile = self.active_export_profiles["csv"]
            row_values = [_get_value_from_path(batch, field) for field in profile.fields]
            self.csv_writers["csv"].writerow(row_values)
        if "json" in self.active_export_profiles:
            profile = self.active_export_profiles["json"]
            row_dict = {}
            for field in profile.fields:
                raw_value = _get_value_from_path(batch, field)
                serializable_value = _convert_ctypes_for_json(raw_value)
                key = field.data_path if use_full_path else field.column_name
                row_dict[key] = serializable_value
            self.json_data_buffer.append(row_dict)
        if batch.batch_id % 20 == 0 and "csv" in self.output_file_handles:
            self.output_file_handles["csv"].flush()

    def _write_json_buffer_to_file(self):
        if not self.json_data_buffer:
            if "json" in self.active_export_profiles: log.info("JSON export enabled, but no data. Skipping file creation.")
            return
        try:
            profile = self.active_export_profiles["json"]
            output_dir, basename = Path(self.view.out_output_dir_var.get()), self.view.out_basename_var.get()
            json_filename = f"{basename}_{profile.name}.json"
            path = output_dir / json_filename
            log.info(f"Writing {len(self.json_data_buffer)} records to JSON file: {path}")
            with open(path, "w", encoding="utf-8") as f: json.dump(self.json_data_buffer, f, indent=4)
            log.info("JSON file written successfully.")
        except (IOError, TypeError) as e: log.error(f"Failed to write JSON output file: {e}")
        finally: self.json_data_buffer.clear()

    def open_folder_from_path(self, folder_path_str: str):
        if not folder_path_str: return
        folder_path = Path(folder_path_str)
        if not folder_path.is_dir(): log.error(f"Directory '{folder_path}' does not exist."); return
        log.info(f"Opening folder: {folder_path}")
        try:
            if sys.platform == "win32": os.startfile(folder_path)
            elif sys.platform == "darwin": subprocess.run(["open", folder_path])
            else: subprocess.run(["xdg-open", folder_path])
        except Exception as e: log.error(f"Failed to open folder: {e}")

    def handle_worker_completion(self, msg: Dict[str, Any]):
        status = "Interrupted" if msg.get("interrupted") else "Complete"
        log.info(f"--- Process {status}. ---")
        if self.view.out_output_json_var.get(): self._write_json_buffer_to_file()
        self._close_all_files()
        self.is_processing = False
        self.worker_process = None
        self.segment_processor = None
        self.view.update_ui_for_processing_state(False)
        is_cpp_success = "Conversion process completed successfully" in msg.get("message", "")
        self.view.update_rec_tab_buttons_state(conversion_successful=is_cpp_success)
        if is_cpp_success: log.info(f"C++ converter successfully generated: {self.last_generated_out_file}")
        if stats := msg.get("stats"): self._log_summary(stats)

    def process_last_generated_out(self):
        if self.last_generated_out_file and self.last_generated_out_file.exists():
            log.info(f"Loading generated file '{self.last_generated_out_file.name}' into OUT Processor.")
            self.view.out_filepath_var.set(str(self.last_generated_out_file))
            self.view.notebook.select(self.view.out_processor_tab)
        else:
            log.warning("No recently generated .out file to process.")

    def _log_summary(self, stats: Dict[str, int]):
        log.info("--- .out File Processing Summary ---")
        log.info(f"Total Blocks Scanned: {stats.get('total_blocks_scanned', 0)}")
        log.info(f"Total Batches Found: {stats.get('total_batches_found', 0)}")
        block_types = sorted([k for k in stats if k.startswith("found_")])
        if block_types:
            log.info("Block Types Found:")
            for key in block_types: log.info(f"  - {key.replace('found_', '')}: {stats[key]}")
        if failed := stats.get("failed_to_parse_blocks"): log.error(f"Failed to Parse Blocks: {failed}")
        log.info("------------------------------------")

    def open_profile_editor(self):
        if self.view.profile_editor_window and self.view.profile_editor_window.winfo_exists():
            self.view.profile_editor_window.lift(); return
        profiles = self.config_manager.get_export_profiles()
        self.view.profile_editor_window = ProfileEditorWindow(master=self.view.master, controller=self, profiles=profiles)
        self.view.profile_editor_window.wait_window()
        self._load_initial_config()
        
    def open_rec_config_editor(self):
        if self.view.rec_config_window and self.view.rec_config_window.winfo_exists():
            self.view.rec_config_window.lift(); return
        current_config = self.config_manager.get_cpp_converter_config()
        self.view.rec_config_window = RecConfigWindow(
            master=self.view.master, controller=self, current_config=current_config,
            title="g_reconverter Config (REC to OUT)",
            save_command=self.save_rec_config
        )
        self.view.rec_config_window.wait_window()
        
    def open_export_config_editor(self):
        if self.view.export_config_window and self.view.export_config_window.winfo_exists():
            self.view.export_config_window.lift(); return
        current_config = self.config_manager.get("segment_export_config", {})
        self.view.export_config_window = SegmentExportConfigWindow(
            master=self.view.master, controller=self, current_config=current_config
        )
        self.view.export_config_window.wait_window()

    def save_rec_config(self, new_config: Dict[str, Any]):
        self.config_manager.save_cpp_converter_config(new_config)
        log.info("g_reconverter configuration saved.")
        
    def save_segment_export_config(self, new_config: Dict[str, Any]):
        full_config = self.config_manager.get("segment_export_config", {})
        full_config.update(new_config)
        main_cpp_config = self.config_manager.get_cpp_converter_config()
        if "g_reconverter_options" in full_config:
            full_config["g_reconverter_options"]["cpp_executable_path"] = main_cpp_config.get("cpp_executable_path")
        self.config_manager.set("segment_export_config", full_config)
        self.config_manager.save_config()
        log.info("Segment export configuration saved.")

    def save_export_profiles(self, profiles: List[ExportProfile]):
        self.config_manager.save_export_profiles(profiles)
        
    def open_current_flight_folder(self):
        if self.flight_analyzer.current_flight_folder_path and self.flight_analyzer.current_flight_folder_path.is_dir():
            self.open_folder_from_path(str(self.flight_analyzer.current_flight_folder_path))
        else:
            log.warning("No flight folder available. Please run an analysis first.")
            messagebox.showinfo("No Folder", "No flight folder has been created yet.", parent=self.view)

    def select_and_analyze_flight_folder(self):
        initial_dir = self.config_manager.get("last_flight_folder")
        new_dir_str = filedialog.askdirectory(initialdir=initial_dir, title="Select Folder with Flight Recordings")
        if not new_dir_str:
            return

        new_dir = Path(new_dir_str)
        self.config_manager.set("last_flight_folder", new_dir_str)
        self.view.analyzer_rec_folder_var.set(new_dir_str)

        flight_name = self._generate_flight_name(new_dir)
        self.view.analyzer_flight_name_var.set(flight_name)
        
        if not self.view.analyzer_workspace_dir_var.get():
            self.view.analyzer_workspace_dir_var.set(str(Path.cwd() / "flight_workspace"))

        workspace_parent_dir = Path(self.view.analyzer_workspace_dir_var.get())
        flight_dir = workspace_parent_dir / flight_name
        
        summary_csv = flight_dir / "flight_summary.csv"

        if summary_csv.is_file():
            response = messagebox.askyesno(
                "Previous Analysis Found",
                f"An analysis for flight '{flight_name}' already exists in the specified workspace.\n\nDo you want to load the previous results?",
                parent=self.view
            )
            if response:
                log.info(f"Loading previous analysis from {flight_dir}")
                self.flight_analyzer.current_flight_folder_path = flight_dir
                self._load_previous_analysis(summary_csv)
                return

        self.view.analyzer_info_var.set("Scanning folder, please wait...")
        self.view.start_analysis_button.config(state=tk.DISABLED)
        self.view.update_idletasks()
        
        try:
            rec_files = sorted(list(new_dir.glob("*.rec")))
            if not rec_files:
                self.view.analyzer_info_var.set("No .rec files found."); return
            
            self.total_files_for_analysis = len(rec_files)
            total_size_mb = sum(f.stat().st_size for f in rec_files) / (1024 * 1024)
            info_text = (f"Found {self.total_files_for_analysis} .rec files, Total size: {total_size_mb:.2f} MB. Ready for analysis.")
            
            self.view.analyzer_info_var.set(info_text)
            self.view.start_analysis_button.config(state=tk.NORMAL)
        except Exception as e:
            log.error(f"Error in folder pre-analysis: {e}", exc_info=True)
            self.view.analyzer_info_var.set(f"Error: {e}")

    def _generate_flight_name(self, folder_path: Path) -> str:
        rec_files = sorted([f for f in folder_path.glob("*.rec")])
        if not rec_files: return f"{folder_path.name}_Flight"
        first_file_name = rec_files[0].stem
        match = re.search(r"(\d{2})-(\d{2})-(\d{2})-(\d{2})-(\d{2})-(\d{2})", first_file_name)
        if match:
            yy, mo, dd, hh, mi, ss = match.groups()
            return f"{yy}{mo}{dd}_{hh}{mi}{ss}_Flight"
        return f"{folder_path.name}_Flight"

    def _load_previous_analysis(self, summary_csv_path: Path):
        if pd is None:
            log.error("Cannot load previous analysis: Pandas is not installed."); return
        try:
            summary_df = pd.read_csv(summary_csv_path)
            self.last_flight_summary_df = summary_df
            self.view.populate_timeline_from_dataframe(summary_df)
            self.view.analyzer_info_var.set("Successfully loaded previous analysis.")
            self.view.open_flight_folder_button.config(state=tk.NORMAL)
            self.view.export_segment_button.config(state=tk.NORMAL)
            self.view.start_analysis_button.config(state=tk.NORMAL)
        except Exception as e:
            log.error(f"Failed to load previous analysis from {summary_csv_path}: {e}")
            messagebox.showerror("Load Error", f"Could not load the summary file:\n{e}", parent=self.view)

    def start_flight_analysis(self):
        if self.is_processing:
            log.warning("Analysis already in progress.")
            return
            
        flight_name = self.view.analyzer_flight_name_var.get().strip()
        rec_folder = self.view.analyzer_rec_folder_var.get()
        workspace_parent_dir_str = self.view.analyzer_workspace_dir_var.get().strip()

        if not all([flight_name, rec_folder, workspace_parent_dir_str]):
            messagebox.showerror("Setup Incomplete", "Select a recordings folder, a workspace directory, and provide a flight name.", parent=self.view)
            return

        self.config_manager.set("last_flight_workspace_parent_dir", workspace_parent_dir_str)
        analysis_options = {
            "aggregate_by_scale": self.view.aggregate_by_scale_var.get(),
            "aggregate_by_waveform": self.view.aggregate_by_waveform_var.get()
        }
        self.config_manager.set("flight_analysis_options", analysis_options)
        self.config_manager.save_config()
            
        self.is_processing = True
        self.view.start_processing_ui()
        
        full_workspace_path = Path(workspace_parent_dir_str) / flight_name
        self.view.analyzer_info_var.set(f"Starting analysis for flight: {flight_name}...")
        
        self.flight_analyzer.start_analysis(rec_folder, flight_name, full_workspace_path, analysis_options)
        self.worker_process = self.flight_analyzer.worker_process
        self.view.poll_result_queue()

    def handle_final_analysis_steps(self):
        self.flight_analyzer.handle_final_analysis_steps()

    def handle_analysis_summary_data(self, msg: Dict[str, Any]):
        summary_df = msg.get("data")
        flight_folder_path = msg.get("flight_folder_path")
        if self.view and summary_df is not None:
            self.last_flight_summary_df = summary_df
            self.view.populate_timeline_from_dataframe(summary_df)
        log.info(f"Analysis summary received for flight folder: {flight_folder_path}")

    def start_segment_export(self):
        if self.is_processing:
            messagebox.showwarning("Process Busy", "Cannot start export while another process is running.", parent=self.view); return
        
        selected_item_ids = self.view.flight_timeline_tree.selection()
        if not selected_item_ids:
            messagebox.showinfo("No Selection", "Please select one or more segments to export.", parent=self.view); return
            
        if self.last_flight_summary_df is None:
            messagebox.showerror("Error", "Flight summary data is not available. Cannot proceed with export.")
            return

        self.total_segments_for_export = len(selected_item_ids)
        log.info(f"Queuing {self.total_segments_for_export} segment(s) for export.")
        
        while not self.export_manager.job_queue.empty():
            try: self.export_manager.job_queue.get_nowait()
            except queue.Empty: break

        rec_folder = Path(self.view.analyzer_rec_folder_var.get())
        export_config = self.config_manager.get("segment_export_config")
        folder_template = export_config.get("naming_options", {}).get("folder_name_template", "{Segment}_{StartBatch}-{EndBatch}")
        current_flight_path = self.flight_analyzer.current_flight_folder_path
        if not current_flight_path:
            log.error("Cannot start export, flight analysis folder path is unknown.")
            messagebox.showerror("Error", "Could not determine the flight workspace folder.", parent=self.view)
            return

        for iid in selected_item_ids:
            try:
                row_index = self.view.flight_timeline_tree.index(iid)
                segment_info_dict = self.last_flight_summary_df.iloc[row_index].to_dict()
                
                placeholders = {
                    "{Segment}": str(segment_info_dict.get('Segment (Mode | Scale | WF)', '')),
                    "{StartBatch}": str(segment_info_dict.get('Start Batch', '')),
                    "{EndBatch}": str(segment_info_dict.get('End Batch', '')),
                    "{BatchCount}": str(segment_info_dict.get('Batch Count', '')),
                }
                folder_name = folder_template
                for key, value in placeholders.items():
                    folder_name = folder_name.replace(key, value)
                safe_folder_name = re.sub(r'[\\/*?:"<>|]', "-", folder_name)

                segment_folder = current_flight_path / safe_folder_name
                
                job = ExportJob(segment_info_dict, segment_folder, rec_folder, export_config)
                self.export_manager.submit_job(job)

            except (ValueError, IndexError) as e:
                log.error(f"Could not process selection '{iid}': {e}", exc_info=True)
                continue
            
        self.export_manager.submit_job(None)

        self.is_processing = True
        self.view.start_processing_ui()
        self.view.analyzer_info_var.set(f"Exporting {self.total_segments_for_export} segment(s)...")
        
        self.export_manager.start()
        
        self.view.poll_result_queue()
    
    def load_segments_for_processing(self):
        log.info("Loading segments for batch processing...")
        if self.is_processing:
            messagebox.showwarning("Busy", "Cannot load segments while another process is running.", parent=self.view)
            return

        flight_folder = self.flight_analyzer.current_flight_folder_path
        if not flight_folder or not flight_folder.is_dir():
            messagebox.showerror("No Flight Loaded", "Please run a flight analysis first on the 'Flight Analyzer' tab.", parent=self.view)
            return
            
        if self.last_flight_summary_df is None:
            messagebox.showerror("No Summary Data", "Flight summary data not found. Please re-run the analysis.", parent=self.view)
            return

        try:
            export_config = self.config_manager.get("segment_export_config", {})
            naming_template = export_config.get("naming_options", {}).get("folder_name_template", "{Segment}_{StartBatch}-{EndBatch}")
            verified_segments = SegmentProcessor.scan_for_segments(flight_folder, self.last_flight_summary_df, naming_template)
            
            if not verified_segments:
                log.warning("No segments found or summary is empty.")
            
            self.view.segment_processor_tab.populate_segments(verified_segments)
            log.info(f"Found and verified {len(verified_segments)} segments.")

        except Exception as e:
            log.error(f"Failed to load and verify segments: {e}", exc_info=True)
            messagebox.showerror("Error", f"An error occurred while loading segments:\n{e}", parent=self.view)

    def start_segment_batch_processing(self):
        if self.is_processing:
            messagebox.showwarning("Busy", "Another process is already running.", parent=self.view)
            return

        tab: SegmentProcessorTab = self.view.segment_processor_tab
        selected_segments = tab.get_selected_segments_data()
        
        if not selected_segments:
            messagebox.showinfo("No Selection", "Please select at least one 'Ready' segment to process.", parent=self.view)
            return

        cpp_config = self.config_manager.get_cpp_converter_config()
        enable_profiling = cpp_config.get("enable_python_worker_profiling", False)

        config = {
            "segments_to_process": selected_segments,
            "output_dir": tab.output_dir_var.get(),
            "generate_csv": tab.generate_csv_var.get(),
            "generate_json": tab.generate_json_var.get(),
            "create_separate_folders": tab.create_separate_folders_var.get(),
            "csv_profile_name": tab.csv_profile_var.get(),
            "json_profile_name": tab.json_profile_var.get(),
            "csv_use_tab": tab.csv_use_tab_var.get(),
            "use_full_path_headers": tab.use_full_path_var.get(),
            "profiles": self.config_manager.get_export_profiles(),
            "enable_profiling": enable_profiling,
        }

        if not config["output_dir"]:
            messagebox.showerror("Configuration Error", "Please specify an output directory.", parent=self.view)
            return
        if not config["generate_csv"] and not config["generate_json"]:
            messagebox.showerror("Configuration Error", "Please select at least one output format (CSV or JSON).", parent=self.view)
            return
        
        sp_config = self.config_manager.get("segment_processor_config", {})
        sp_config["last_output_dir"] = config["output_dir"]
        sp_config["create_separate_folders"] = config["create_separate_folders"]
        self.config_manager.set("segment_processor_config", sp_config)
        self.config_manager.save_config()

        self.is_processing = True
        self.view.start_processing_ui()
        
        self.segment_processor = SegmentProcessor(config, self.result_queue, self.command_queue)
        self.segment_processor.start()
        
        self.view.poll_result_queue()