"""
Frame Model Plot Module

This module contains the FrameModelPlot class which provides a GUI interface
for plotting and analyzing atmospheric retrieval model results. It handles
both high and low resolution data visualization, contribution plots, and
VMR (Volume Mixing Ratio) analysis.
"""
import os.path
import traceback
from pathlib import Path
import pickle
from tkinter import messagebox
import subprocess

from GUIBRUSHR.GUI.LAYOUT.MyPanel import MyPanel
from GUIBRUSHR.GUI.WIDGET.MyCheckBox import MyCheckBox
from GUIBRUSHR.GUI.WIDGET.MyDropDown import MyDropdown
from GUIBRUSHR.GUI.WIDGET.MyEntry import MyEntry
from GUIBRUSHR.GUI.WIDGET.MyFigure import MyFigure
from GUIBRUSHR.GUI.WIDGET.MyButton import MyButton
from GUIBRUSHR.GUI.WIDGET.MyTextField import MyTextField
from GUIBRUSHR.General_Constants.FunctionsAndConstants.Constant_Variables import ConstantVariables
from GUIBRUSHR.General_Constants.Classes.HelpButton import HelpButton
from GUIBRUSHR.General_Constants.FunctionsAndConstants.general_functions import (
    pre_plot, opacities_contribution_plot, plot_vmr
)


def parse_lh_chi2(value):
    """Parse the extended string format: 'chisq;lh;chi2_reduced;npix_or_dof;nfit'"""
    if isinstance(value, str):
        parts = value.split(";")
        chisq = float(parts[0])
        lh = float(parts[1])
        chi2_r = float(parts[2]) if len(parts) > 2 else None
        npix_or_dof = int(parts[3]) if len(parts) > 3 else None
        nfit = int(parts[4]) if len(parts) > 4 else None
        return chisq, lh, chi2_r, npix_or_dof, nfit
    # Fallback for old format (just float)
    return None, float(value), None, None, None


def format_lh_dict(lh_dict, name):
    if lh_dict is None:
        return f"{name}:\n" + "=" * 50 + "\n  Not used", 0, 0, 0, None

    lines = [f"{name}:", "=" * 50]
    total_lh = 0
    total_chisq = 0
    total_npix_or_dof = 0
    nfit = None

    for key, value in lh_dict.items():
        if isinstance(value, dict):
            lines.append(f"  {key}:")
            subtotal_lh = 0
            subtotal_chisq = 0
            subtotal_npix_or_dof = 0
            for subkey, subval in value.items():
                chisq, lh, chi2_r, npix_or_dof, nfit_val = parse_lh_chi2(subval)
                if nfit is None and nfit_val is not None:
                    nfit = nfit_val
                chi2_str = f"  (χ²r = {chi2_r:.4f})" if chi2_r is not None else ""
                lines.append(f"        {subkey}: {lh:.4f}{chi2_str}")
                subtotal_lh += lh
                if chisq is not None:
                    subtotal_chisq += chisq
                if npix_or_dof is not None:
                    subtotal_npix_or_dof += npix_or_dof
            lines.append(f"    Subtotal LH: {subtotal_lh:.4f}")
            total_lh += subtotal_lh
            total_chisq += subtotal_chisq
            total_npix_or_dof += subtotal_npix_or_dof
        else:
            chisq, lh, chi2_r, npix_or_dof, nfit_val = parse_lh_chi2(value)
            if nfit is None and nfit_val is not None:
                nfit = nfit_val
            chi2_str = f"  (χ²r = {chi2_r:.4f})" if chi2_r is not None else ""
            lines.append(f"  {key}: {lh:.4f}{chi2_str}")
            total_lh += lh
            if chisq is not None:
                total_chisq += chisq
            if npix_or_dof is not None:
                total_npix_or_dof += npix_or_dof

    # Calcola chi2 ridotto totale
    if total_npix_or_dof > 0 and nfit is not None:
        total_dof = total_npix_or_dof - nfit
        total_chi2_r = total_chisq / total_dof if total_dof > 0 else 0
        lines.append(f"\n  Total LH: {total_lh:.4f}  (Total χ²r = {total_chi2_r:.4f})")
    else:
        lines.append(f"\n  Total LH: {total_lh:.4f}")

    return "\n".join(lines), total_lh, total_chisq, total_npix_or_dof, nfit


def format_both(lh_HR, lh_LR):
    hr_output, total_lh_HR, total_chisq_HR, total_dof_HR, nfit_HR = format_lh_dict(
        lh_HR, "Likelihood High-Resolution"
    )
    lr_output, total_lh_LR, total_chisq_LR, total_npix_LR, nfit_LR = format_lh_dict(
        lh_LR, "Likelihood Low-Resolution"
    )

    lines = [hr_output, "", lr_output]

    # Calcola il combined total
    combined_lh = total_lh_HR + total_lh_LR
    combined_chisq = total_chisq_HR + total_chisq_LR
    combined_npix_or_dof = total_dof_HR + total_npix_LR

    # Usa nfit da HR o LR (dovrebbe essere lo stesso)
    nfit = nfit_HR if nfit_HR is not None else nfit_LR

    if combined_npix_or_dof > 0 and nfit is not None:
        combined_dof = combined_npix_or_dof - nfit
        combined_chi2_r = combined_chisq / combined_dof if combined_dof > 0 else 0
        lines.extend([
            "",
            "=" * 50,
            f"Combined Total LH: {combined_lh:.4f}  (Combined χ²r = {combined_chi2_r:.4f})"
        ])
    else:
        lines.extend(["", "=" * 50, f"Combined Total LH: {combined_lh:.4f}"])

    return "\n".join(lines)


class FrameModelPlot(MyPanel):
    """
    A GUI panel for plotting and analyzing atmospheric retrieval model results.
    
    This class provides functionality to:
    - Generate model plots for high and low resolution data
    - Create contribution plots for opacity analysis
    - Display VMR profiles and statistical information
    - Handle both equilibrium and non-equilibrium chemistry modes
    """

    def __init__(self, parent, color, row, column, path_default, frame_input, 
                 width, height, window, **kwargs):
        """
        Initialize the FrameModelPlot panel.
        
        Sets up all GUI widgets including input fields, checkboxes, dropdowns,
        buttons, and the main plot area. Initializes instance variables for
        data processing and plot management.
        """
        super().__init__(parent, color, row, column, **kwargs)
        
        # Core configuration
        self.path_folder_targets = frame_input.frame_input.frame_path.path_folder_targets.get_text()
        self.window = window
        self.path_default = path_default
        self.frame_input = frame_input
        
        # Processing state variables
        self.folder_retrieval = None
        self.p = None  # Subprocess handle
        self.filepng = None  # Current plot file path
        self.lista_parametri = None  # Parameter list (unused but kept for compatibility)
        self.path_imm = None  # Image path (unused but kept for compatibility)
        self.last_plot_reduced = None  # Last reduced plot (unused but kept for compatibility)

        # Create control panel (plots open in standalone windows)
        self.panel_controls = MyPanel(self, color, 0, 0)
        self._create_controls_panel(color)
        # Information panel (next to controls)
        self.panel_info = MyPanel(self, color, 0, 1)
        self._create_info_panel(color)
        
        # Analysis parameters
        self.chemistry = None
        self.format_temperature = None  # Unused but kept for compatibility
        self.scattering = None  # Unused but kept for compatibility
        self.ecc = None  # Unused but kept for compatibility
        self.resolution = None
        self.global_exc = 1  # Global exception counter

    def _create_controls_panel(self, color):
        """Create control panel with elements organized in fewer rows and more columns."""
        # Row 0: Help, Lbl sampling HR, Lbl sampling LR, checkboxes
        self._create_help_button(color)

        self.textfield_lbl_hr = MyTextField(
            self.panel_controls, 0, 1, "4", "Lbl sampling HR:",
            color=color, columnspan=2, width=12
        )

        self.textfield_lbl_lr = MyTextField(
            self.panel_controls, 0, 3, "4", "Lbl sampling LR:",
            color=color, columnspan=2, width=12
        )

        self.checkbox_overplot_orders = MyCheckBox(
            self.panel_controls, 0, 5, "Overplot Orders"
        )
        self.checkbox_remove_chain = MyCheckBox(
            self.panel_controls, 0, 6, "RM bad chain(s)", initial_value=1
        )
        self.checkbox_contribution = MyCheckBox(
            self.panel_controls, 0, 7, "Contribution plot"
        )

        # Row 1: Wavelength ranges (LR and HR)
        self.entry_min_wl_lr = MyEntry(
            self.panel_controls, 1, 1, "0.3", "Min wl LR:", color=color, columnspan=2
        )
        self.entry_max_wl_lr = MyEntry(
            self.panel_controls, 1, 3, "12", "Max wl LR:", color=color, columnspan=2
        )
        self.entry_min_wl_hr = MyEntry(
            self.panel_controls, 1, 5, "0.3", "Min wl HR:", color=color, columnspan=2
        )
        self.entry_max_wl_hr = MyEntry(
            self.panel_controls, 1, 7, "2.5", "Max wl HR:", color=color, columnspan=2
        )

        # Row 2: Pressure and layer configuration
        self.textfield_min_pressures = MyTextField(
            self.panel_controls, 2, 1, "-9", "Min pressure (Eq, 10^):",
            color=color, columnspan=2, width=15
        )
        self.textfield_max_pressures = MyTextField(
            self.panel_controls, 2, 3, "2", "Max pressure (Eq, 10^):",
            color=color, columnspan=2, width=15
        )
        self.textfield_layers = MyTextField(
            self.panel_controls, 2, 5, "100", "N. Layer:",
            color=color, columnspan=2, width=15
        )

        # Row 3: Dropdowns for plot styling
        self.dropdown_plot_opacity = MyDropdown(
            self.panel_controls, 3, 1, ["No", "plot", "fill_between"], "Opacity contribution:",
            color=color, columnspan=2, width=15
        )
        self.dropdown_wl_scale_hr = MyDropdown(
            self.panel_controls, 3, 3, ["linear", "log"], "Wavelength scale HR:",
            color=color, columnspan=2, width=15
        )
        self.dropdown_wl_scale_lr = MyDropdown(
            self.panel_controls, 3, 5, ["log", "linear"], "Wavelength scale LR:",
            color=color, columnspan=2, width=15
        )

        self.checkbox_invert_y_axis = MyCheckBox(
            self.panel_controls, 2, 6, "Invert Y axis Opacities"
        )

        # Row 4: Best parameters, Invert Y axis, and action buttons
        self.dropdown_best_params = MyDropdown(
            self.panel_controls, 4, 1, ["Max LH", "Median", "KDE", "Max from param", "From sampler"], "Best parameters extraction:",
            color=color, columnspan=2, width=15
        )
        self.button_plot = MyButton(
            self.panel_controls, 4, 3, 'PLOT', '#a366ff', self.plot, color_panel=color
        )
        self.button_show = MyButton(
            self.panel_controls, 4, 4, 'SHOW LAST PLOT', '#ffcc00', self.show, color_panel=color
        )
        self.checkbox_use_taskset = MyCheckBox(
            self.panel_controls, 4, 5, "Use taskset", initial_value=1
        )

    def _create_info_panel(self, color):
        """Create information panel with statistical fields only."""
        # Statistical information fields
        self.textfield_last_max_likelihood = MyTextField(
            self.panel_info, 0, 1, "0", "Last Max LH:",
            color=color, columnspan=6, width=40
        )
        self.textfield_current_max_likelihood = MyTextField(
            self.panel_info, 1, 1, "0", "Current Max LH:",
            color=color, columnspan=6, width=40
        )
        self.textfield_aic = MyTextField(
            self.panel_info, 2, 1, "0", "Aic:",
            color=color, columnspan=4, width=15
        )
        self.textfield_bad_chains = MyTextField(
            self.panel_info, 3, 1, "0", "Bad Chains:",
            color=color, columnspan=4, width=15
        )

        # Results display area
        self.textfield_medians_errors = MyTextField(
            self.panel_info, 4, 1, "", 'Medians\nand vmr (EQ)',
            color=color, columnspan=7, rowspan=8, height=15
        )

    def _create_help_button(self, color):
        """Create help button with widget descriptions."""
        help_text = {
            "Lbl sampling HR field": "Line-by-line opacity sampling factor for high-resolution petitRADTRANS calculations. Higher values (e.g., 4-10) reduce computation time by sampling fewer wavelength points but may miss narrow spectral features. Lower values (1-3) provide higher accuracy at the cost of longer computation.",

            "Lbl sampling LR field": "Line-by-line opacity sampling factor for low-resolution petitRADTRANS calculations. Higher values (e.g., 4-10) reduce computation time by sampling fewer wavelength points but may miss narrow spectral features. Lower values (1-3) provide higher accuracy at the cost of longer computation.",

            "Overplot Orders checkbox": "When enabled, overlays all high-resolution spectral orders on a single plot for comparison. When disabled, plots each order separately.",

            "RM bad chain(s) checkbox": "Remove poorly converged MCMC chains before plotting the best-fit model. Uses the same chain selection as the Corner Plot panel.",

            "Contribution plot checkbox": "Generate opacity contribution plots showing which atmospheric layers and molecular species contribute most to the observed spectrum at each wavelength.",

            "Min wl LR field": "Minimum wavelength in microns for low-resolution spectroscopy plots.",

            "Max wl LR field": "Maximum wavelength in microns for low-resolution spectroscopy plots.",

            "Min wl HR field": "Minimum wavelength in microns for high-resolution spectroscopy plots.",

            "Max wl HR field": "Maximum wavelength in microns for high-resolution spectroscopy plots.",

            "Opacity contribution dropdown": "Plotting style for opacity contribution plots. 'plot' shows line plots for each opacity source. 'fill_between' uses filled regions stacked vertically.",

            "Wavelength scale HR dropdown": "X-axis scale for high-resolution plots. 'linear' uses uniform wavelength spacing. 'log' uses logarithmic spacing, useful for wide wavelength ranges.",

            "Wavelength scale LR dropdown": "X-axis scale for low-resolution plots. 'log' is default for wide wavelength coverage. 'linear' for narrower ranges.",

            "Invert Y axis Opacities checkbox": "Invert the Y-axis (pressure) in opacity contribution plots. Useful for displaying pressure increasing downward (higher altitudes at top).",

            "PLOT button": "Generate model spectrum plots using the best-fit parameters from the selected retrieval. Creates both high-resolution and low-resolution plots depending on the retrieval configuration.",

            "SHOW LAST PLOT button": "Display the most recently generated model plot without recomputing.",

            "Min pressure (Eq, 10^) field": "Minimum pressure (log10 bar) for equilibrium chemistry pressure grid. Only affects equilibrium chemistry retrievals.",

            "Max pressure (Eq, 10^) field": "Maximum pressure (log10 bar) for equilibrium chemistry pressure grid. Only affects equilibrium chemistry retrievals.",

            "N. Layer field": "Number of atmospheric layers for equilibrium chemistry calculations. More layers increase accuracy but slow computation.",

            "Last Max LH field": "Maximum log-likelihood from the last MCMC run.",

            "Current Max LH field": "Maximum log-likelihood after removing bad chains.",

            "Aic field": "Akaike Information Criterion for model comparison.",

            "Bad Chains field": "Number of MCMC chains identified as poorly converged.",

            "Medians and vmr (EQ) field": "Statistical summary of fitted parameters. For equilibrium chemistry, also shows Volume Mixing Ratios (VMR) of major molecular species as a function of pressure."
        }

        self.help_button = HelpButton(
            self.panel_controls, 0, 0,
            "Model Plot Panel Help",
            help_text,
            columnspan=1
        )

    def _get_target_and_values(self):
        """Extract target and array values from the input frame."""
        target = self.frame_input.frame_filter_table.frame_filter.dropdown_target.get_value()
        arr_values = self.frame_input.frame_filter_table.frame_table.table.get_arr_values()
        return target, arr_values

    def _update_chemistry_and_resolution(self, arr_values):
        """Update chemistry and resolution from array values."""
        self.chemistry = arr_values[ConstantVariables.DICT_FILTER_TABLE["#modes"]["index"]]
        self.resolution = arr_values[ConstantVariables.DICT_FILTER_TABLE["#resolutions"]["index"]]

    def _build_folder_path(self, target, arr_values):
        """Build the retrieval folder path."""
        return str(Path(
            self.path_folder_targets, target, ConstantVariables.RESULTS_FOLDER,
            arr_values[ConstantVariables.DICT_FILTER_TABLE["ID"]["index"]]
        ))

    def _update_display_fields(self, number_bad_chains, last_max_likelihood, 
                              current_max_likelihood, aic_var, stringa_med):
        """Update all display fields with computed values."""
        self.textfield_bad_chains.insert_text(str(number_bad_chains))
        self.textfield_last_max_likelihood.insert_text(str(last_max_likelihood))
        self.textfield_current_max_likelihood.insert_text(str(current_max_likelihood))
        self.textfield_aic.insert_text(str(aic_var))
        self.textfield_medians_errors.insert_text(stringa_med)

    def _save_plot_parameters(self, best_fit_parameters, contribution, aic_var,
                             current_max_likelihood, number_bad_chains, 
                             stringa_med, last_max_likelihood):
        """Save all plot parameters to pickle file for external processing."""
        output_file = str(Path(self.folder_retrieval, "output_data_contribution.pkl"))
        
        output_data_contribution_dictionary = {
            "best_fit_parameters": best_fit_parameters,
            "contribution": contribution,
            "lbl_sampling_hr": int(self.textfield_lbl_hr.get_text()),
            "lbl_sampling_lr": int(self.textfield_lbl_lr.get_text()),
            "min_pressures": int(self.textfield_min_pressures.get_text()),
            "max_pressures": int(self.textfield_max_pressures.get_text()),
            "layers": int(self.textfield_layers.get_text()),
            "aic_var": aic_var,
            "overplot_orders": int(self.checkbox_overplot_orders.get_value()),
            "current_max_likelihood": current_max_likelihood,
            "number_bad_chains": number_bad_chains,
            "stringa_med": stringa_med,
            "last_max_likelihood": last_max_likelihood,
            "min_wl_lr": float(self.entry_min_wl_lr.get_value()),
            "max_wl_lr": float(self.entry_max_wl_lr.get_value()),
            "min_wl_hr": float(self.entry_min_wl_hr.get_value()),
            "max_wl_hr": float(self.entry_max_wl_hr.get_value()),
            "wl_scale_hr": self.dropdown_wl_scale_hr.get_value(),
            "wl_scale_lr": self.dropdown_wl_scale_lr.get_value(),
            "plot_opacity": self.dropdown_plot_opacity.get_value() != "No",
        }
        
        with open(output_file, "wb") as fo:
            pickle.dump(output_data_contribution_dictionary, fo)

    def _determine_plot_filename(self):
        """Determine the appropriate plot filename based on contribution setting."""
        if self.checkbox_contribution.get_value():
            return str(Path(self.folder_retrieval, 'contribution.png'))
        else:
            return str(Path(self.folder_retrieval, 'model_high_low_res.png'))

    def _start_external_plotting(self, target):
        """Start external plotting subprocess with optional taskset on dedicated core."""
        import sys
        from GUIBRUSHR.General_Constants.FunctionsAndConstants.Constant_Variables import ConstantVariables

        plot_script_path = str(Path(
            self.path_default, "GUIBRUSHR", "GUI", "Input_Output_Panels", "Input_Panels",
            "TabPanels", "Frame_Retrieval_Analysis", "Tab_Analysis", "Frame_Model_Plot",
            "plot_contribution.py"
        ))

        # Build command based on checkbox value
        if self.checkbox_use_taskset.get_value():
            # Use taskset to run model plot on core ncore+2 with priority 19
            model_core = ConstantVariables.ncore + 2
            command = [
                "taskset",
                "-c",
                str(model_core),
                "nice",
                "-n",
                "19",
                sys.executable,
                plot_script_path,
                self.folder_retrieval,
                target,
                self.path_default
            ]
        else:
            # Run without taskset
            command = [
                sys.executable,
                plot_script_path,
                self.folder_retrieval,
                target,
                self.path_default
            ]

        self.p = subprocess.Popen(
            command,
            stderr=subprocess.STDOUT,
            universal_newlines=True
        )

    def plot(self):
        """
        Generate model plots and contribution analysis.
        
        This method orchestrates the entire plotting process:
        1. Extracts parameters from GUI
        2. Runs pre-processing analysis
        3. Updates display fields
        4. Saves parameters for external processing
        5. Starts external plotting subprocess
        """
        try:

            contribution = self.checkbox_contribution.get_value()
            if contribution and self.dropdown_best_params.get_value() == "From sampler":
                messagebox.showerror("Contribution analysis not available", "Contribution analysis not available for 'From sample' best fit parameters.")
                return

            # Extract input parameters
            target, arr_values = self._get_target_and_values()
            self._update_chemistry_and_resolution(arr_values)
            self.folder_retrieval = self._build_folder_path(target, arr_values)
            
            # Run pre-processing analysis
            (number_bad_chains, aic_var, last_max_likelihood, current_max_likelihood,
             _, _, _, best_fit_parameters, _, _, stringa_med, _, _, _) = pre_plot(
                self.folder_retrieval, arr_values, self.checkbox_remove_chain.get_value(), False,
                self.dropdown_best_params.get_value()
            )
            
            # Update display fields
            self._update_display_fields(
                number_bad_chains, last_max_likelihood, current_max_likelihood, 
                aic_var, stringa_med
            )

            # Prepare contribution string
            contribution = ", Contribution" if contribution else ""
            
            # Save parameters for external processing
            self._save_plot_parameters(
                best_fit_parameters, contribution, aic_var, current_max_likelihood,
                number_bad_chains, stringa_med, last_max_likelihood
            )
            
            # Set plot filename
            self.filepng = self._determine_plot_filename()
            
            # Disable buttons during processing
            self.button_plot.button.configure(state="disabled")
            self.button_show.button.configure(state="disabled")
            
            # Start external plotting process
            self._start_external_plotting(target)
            
            # Schedule process checking
            self.window.after(500, self.check_proc)
            
        except Exception as e:
            print("Generic error: " + str(e))
            print(traceback.format_exc())
            messagebox.showerror("Error during the files import", "Error during the files import")

    def _load_error_log(self):
        """Load and check for errors from external processing."""
        error_file = str(Path(self.folder_retrieval, "error_log_model.pkl"))
        with open(error_file, "rb") as fo:
            return pickle.load(fo)

    def _load_vmr_data(self):
        """Load VMR profile data."""
        vmr_file = str(Path(self.folder_retrieval, "vmr_profile.pkl"))
        with open(vmr_file, "rb") as fo:
            vmr_profile_data = pickle.load(fo)
            vmr_dict = vmr_profile_data["vmr_dict"]
            pressures = vmr_profile_data["pressures"]
        return vmr_dict, pressures

    def _determine_plot_paths(self):
        """Determine plot file paths based on contribution setting and resolution."""
        if self.checkbox_contribution.get_value():
            filepng = str(Path(self.folder_retrieval, "contribution", 'contribution_HR.png'))
            path_imm = str(Path(self.folder_retrieval, "contribution", "contribution_LR.png"))
            path_imm_norm = None
        else:
            filepng = str(Path(self.folder_retrieval, 'model_high_low_res', 'high_res.png'))
            path_imm = str(Path(self.folder_retrieval, "model_high_low_res", "low_res.png"))
            path_imm_norm = str(Path(self.folder_retrieval, "model_high_low_res", "low_res_normalized.png"))
        
        # Use white PNG if only LR and not HR
        if "H" not in self.resolution:
            filepng = str(Path.joinpath(
                self.path_default, "GUIBRUSHR", "Files", "Test", "test.png"
            ))
        
        return filepng, path_imm, path_imm_norm

    def _load_distribution_data(self):
        """Load distribution data for opacity contribution analysis."""
        distribution_file = str(Path(self.folder_retrieval, "distribution.pkl"))
        with open(distribution_file, "rb") as fo:
            distribution_data = pickle.load(fo)
            not_retrieval_obj = distribution_data["not_retrieval_obj"]
            _ = distribution_data["instruments_LR"]  # Skip instruments_LR if not needed
        return not_retrieval_obj

    def _process_high_resolution_data(self, not_retrieval_obj):
        """Process high resolution opacity contribution data."""
        if ("H" in self.resolution and 
            not_retrieval_obj.opacity_contribution_HR is not None):
            opacities_contribution_plot(
                self.folder_retrieval, not_retrieval_obj.opacity_contribution_HR, "HR",
                self.dropdown_plot_opacity.get_value(), 
                self.dropdown_wl_scale_hr.get_value(),
                self.checkbox_invert_y_axis.get_value(),
                instruments_LR=None,
                offsetLR_arr=None,
                rp=not_retrieval_obj.rp,
                rs=not_retrieval_obj.stellar_radius,
                rad_mode=not_retrieval_obj.rad_mode
            )

    def _process_low_resolution_data(self, not_retrieval_obj, path_imm, path_imm_norm):
        """Process low resolution opacity contribution data."""
        if "L" in self.resolution:
            if os.path.exists(path_imm):
                MyFigure("Low Resolution data", type_data="Image", image_path=path_imm)
            if os.path.exists(path_imm_norm):
                MyFigure("Low Resolution data normalized flux", type_data="Image", image_path=path_imm_norm)
            if not_retrieval_obj.opacity_contribution_LR is not None:
                opacities_contribution_plot(
                    self.folder_retrieval, not_retrieval_obj.opacity_contribution_LR, "LR",
                    self.dropdown_plot_opacity.get_value(),
                    self.dropdown_wl_scale_lr.get_value(),
                    self.checkbox_invert_y_axis.get_value(),
                    instruments_LR=not_retrieval_obj.instruments_LR,
                    offsetLR_arr=not_retrieval_obj.offsetLRarr,
                    rp=not_retrieval_obj.rp,
                    rs=not_retrieval_obj.stellar_radius,
                    rad_mode=not_retrieval_obj.rad_mode
                )

    def _handle_equilibrium_chemistry(self):
        """Handle additional processing for equilibrium chemistry."""
        if self.chemistry == "Equilibrium":
            vmr_file = str(Path(self.folder_retrieval, "vmr.pkl"))
            with open(vmr_file, "rb") as fo:
                vmr = pickle.load(fo)
            string_med = self.textfield_medians_errors.get_text()
            self.textfield_medians_errors.insert_text(string_med + "\n" + str(vmr["string"]))

    def check_proc(self):
        """
        Check the status of the external plotting process.
        
        This method is called periodically to monitor the subprocess.
        When the process completes, it loads the results and updates the GUI.
        """
        if self.p.poll() is not None:
            # Process completed - re-enable buttons
            self.button_plot.button.configure(state="normal")
            self.button_show.button.configure(state="normal")
            
            # Check for errors
            error = self._load_error_log()
            if error is not None:
                MyFigure(title="Error in model plot", message=error)
                return
            
            # Load VMR data
            vmr_dict, pressures = self._load_vmr_data()
            
            # Determine plot paths
            self.filepng, path_imm, path_imm_norm = self._determine_plot_paths()
            
            # Display high resolution plot in standalone window
            MyFigure(title="High Resolution Model", type_data="Image", image_path=self.filepng)
            
            # Load distribution data for opacity analysis
            not_retrieval_obj = self._load_distribution_data()
            
            # Process high and low resolution data
            self._process_high_resolution_data(not_retrieval_obj)
            self._process_low_resolution_data(not_retrieval_obj, path_imm, path_imm_norm)

            # Output LH contributions
            output_LH = format_both(not_retrieval_obj.lh_HR, not_retrieval_obj.lh_LR)
            MyFigure(title="Likelihood contribution from instruments/nights", message=output_LH)
            
            # Generate VMR plot
            plot_vmr(pressures, vmr_dict, self.folder_retrieval)
            
            # Handle equilibrium chemistry specific processing
            self._handle_equilibrium_chemistry()
            
            print('Plot Finished')
        else:
            # Process still running - check again later
            self.window.after(500, self.check_proc)

    def _load_saved_parameters(self):
        """Load previously saved plot parameters from pickle file."""
        output_file = str(Path(self.folder_retrieval, "output_data_contribution.pkl"))
        
        with open(output_file, "rb") as fo:
            contribution_data = pickle.load(fo)
            
            aic_var = contribution_data["aic_var"]
            current_max_likelihood = contribution_data["current_max_likelihood"]
            number_bad_chains = contribution_data["number_bad_chains"]
            stringa_med = contribution_data["stringa_med"]
            last_max_likelihood = contribution_data["last_max_likelihood"]
            
        return aic_var, current_max_likelihood, number_bad_chains, stringa_med, last_max_likelihood

    def show(self):
        """
        Display the last generated plot without regenerating it.
        
        This method loads and displays previously generated plots and
        associated data without running the full analysis pipeline.
        """
        try:
            # Extract basic parameters
            target, arr_values = self._get_target_and_values()
            self._update_chemistry_and_resolution(arr_values)
            self.folder_retrieval = self._build_folder_path(target, arr_values)
            
            # Determine plot paths
            self.filepng, path_imm, path_imm_norm = self._determine_plot_paths()
            
            # Display the main plot in standalone window
            MyFigure(title="High Resolution Model", type_data="Image", image_path=self.filepng)
            
            # Show low resolution plot if available
            if "L" in self.resolution:
                if os.path.exists(path_imm):
                    MyFigure("Low Resolution data", type_data="Image", image_path=path_imm)
                if os.path.exists(path_imm_norm):
                    MyFigure("Low Resolution data normalized flux", type_data="Image", image_path=path_imm_norm)
            
            # Load and display saved parameters
            (aic_var, current_max_likelihood, number_bad_chains, 
             stringa_med, last_max_likelihood) = self._load_saved_parameters()
            
            # Update display fields
            self._update_display_fields(
                number_bad_chains, last_max_likelihood, current_max_likelihood,
                aic_var, stringa_med
            )
            
            # Handle equilibrium chemistry display
            if self.chemistry == "Equilibrium":
                vmr_file = str(Path(self.folder_retrieval, "vmr.pkl"))
                with open(vmr_file, "rb") as fo:
                    vmr = pickle.load(fo)
                self.textfield_medians_errors.insert_text(stringa_med + "\n" + vmr["string"])
                
        except Exception as e:
            error_msg = str(e) + "\n" + str(traceback.format_exc())
            MyFigure("Err in display low res", message=error_msg)
            print(error_msg)
