"""
FrameManualModel module provides a GUI panel for manual model creation and visualization.
This module allows users to:
- Configure atmospheric parameters
- Set up molecular compositions
- Generate and visualize spectral models
- Save model results and plots
"""

# Standard library imports
import os
import pickle
import subprocess
import traceback
from pathlib import Path

# Third-party imports
import matplotlib
matplotlib.rcParams['agg.path.chunksize'] = 10000
if not os.environ.get('SPHINX_BUILD'):
    matplotlib.use("TkAgg")
import matplotlib.pyplot as plt
import numpy as np
import tkinter as tk
from tkinter import messagebox
from astropy.io import fits
import yaml

# Local imports
from GUIBRUSHR.GUI.DataInterface.DBSQLite3 import DBSQLite3
from GUIBRUSHR.GUI.Input_Output_Panels.Input_Panels.TabPanels.FrameManualModel.ManualModelObjClass import ManualModelObjClass
from GUIBRUSHR.GUI.Input_Output_Panels.Input_Panels.TabPanels.FrameManualModel.ParamClassForManualPlot import ParamClassForManualPlot
from GUIBRUSHR.Retrieval.ModelCalculation.ModelData import ModelData
from GUIBRUSHR.GUI.WIDGET.MyButton import MyButton
from GUIBRUSHR.GUI.WIDGET.MyDropDown import MyDropdown
from GUIBRUSHR.GUI.WIDGET.MyTextField import MyTextField
from GUIBRUSHR.GUI.WIDGET.MyCheckBox import MyCheckBox
from GUIBRUSHR.GUI.WIDGET.MyEntry import MyEntry
from GUIBRUSHR.GUI.LAYOUT.MyPanel import MyPanel
from GUIBRUSHR.GUI.LAYOUT.MyTabPanel import MyTabPanel
from GUIBRUSHR.GUI.WIDGET.MyLabel import MyLabel
from GUIBRUSHR.GUI.WIDGET.MyFigure import MyFigure
from GUIBRUSHR.GUI.DataInterface.DataInterface import get_target_info, create_path_night, get_target_list
from GUIBRUSHR.General_Constants.FunctionsAndConstants.Constant_Variables import ConstantVariables
from GUIBRUSHR.General_Constants.FunctionsAndConstants.general_functions import get_line_lists, get_condensed_line_list, plot_tp_profile, plot_vmr, download_star_spectrum, get_depth_filename
from GUIBRUSHR.General_Constants.Classes.ValueErrorTP import ValueErrorTP
from GUIBRUSHR.General_Constants.Classes.HelpButton import HelpButton

# noinspection PyTypeChecker,PyUnresolvedReferences


def _create_tab_help_button_manual(panel_idx, parent_panel, has_vmr_button=False, has_hce_button=False,
                                   has_molecule_button=False, has_condensed_button=False, has_hydrogen_button=False):
    """
    Create tab-specific help button with parameter descriptions.

    Args:
        panel_idx: Index of the panel (0-7)
        parent_panel: Parent panel widget
        has_vmr_button: Whether this panel has VMR button
        has_hce_button: Whether this panel has Hybrid Chemical Equilibrium button
        has_molecule_button: Whether this panel has ADD MOLECULE button
        has_condensed_button: Whether this panel has ADD CONDENSED MOLECULE button
        has_hydrogen_button: Whether this panel has UPDATE H-, H, e- button

    Returns:
        HelpButton instance
    """
    # Tab descriptions
    tab_descriptions = [
        "Planet + Data: Parameters related to planetary properties, orbital characteristics, data scaling, and instrumental offsets.",
        "Clouds/Haze: Parameters controlling cloud deck properties, haze opacity, and aerosol scattering in the atmosphere.",
        "Temperatures: Temperature-pressure profile parameters for primary parameterizations (isothermal, Madhu, Guillot).",
        "Temperatures2: Additional temperature profile parameters for alternative parameterizations (personalized profiles, nodal points).",
        "VMR: Volume Mixing Ratio profile parameters for species with altitude-dependent abundances (Gaussian peaks).",
        "Equilibrium: Chemical equilibrium parameters including metallicity, C/O ratio, and elemental abundances.",
        "Molecules: Molecular species and their volume mixing ratios. Includes major atmospheric constituents and trace species.",
        "Condensed elements: Condensed species (cloud particles) parameters including particle properties and sedimentation efficiency."
    ]

    # Collect parameters for this panel
    help_text = {"Tab Description": tab_descriptions[panel_idx]}

    for param_name, param_data in ConstantVariables.PARAMS_DICT.items():
        if param_data["panel"] == panel_idx and param_data["row_manual"] >= 0:
            # Get description from params dict, with fallback
            description = param_data.get("description", f"Parameter {param_name} (description pending)")
            label = param_data.get("label", param_name)

            # Format the help entry
            help_text[f"{param_name} ({label})"] = description

    # Add button-specific help if applicable
    if has_vmr_button:
        help_text["UPDATE VMR PARAMETERS button"] = "Automatically populate VMR peak, pressure, and width parameters for all molecules with present=1 and VMR=0. Creates altitude-dependent Gaussian abundance profiles."

    if has_hce_button:
        help_text["ADD ELEMENT TO HYBRID CHEMICAL EQUILIBRIUM button"] = "Adds the selected element for hybrid chemical equilibrium modeling. Combines chemical equilibrium (for major species) with free retrieval (for selected elements)."

    if has_hydrogen_button:
        help_text["UPDATE H-, H, e- button"] = "Updates the abundances of H- (hydrogen minus), H (atomic hydrogen), and e- (free electrons) based on the H2 and He abundances. Calculates derived abundances for consistency in hot atmosphere models."

    if has_molecule_button:
        help_text["ADD MOLECULE button"] = "Adds the selected molecule with its isotope, sub-isotope, and abundance to the atmospheric composition. Allows manual specification of molecular abundances."

    if has_condensed_button:
        help_text["ADD CONDENSED MOLECULE button"] = "Adds cloud/aerosol species (e.g., Al2O3, MgSiO3, Fe) to the model. Controls cloud particle composition and properties."

    return HelpButton(
        parent_panel, 0, 100,
        f"Tab Parameters Help",
        help_text,
        columnspan=2
    )


# noinspection PyUnresolvedReferences
class FrameManualModel(MyPanel):
    """
    A GUI panel for manual model creation and visualization.
    
    This class provides a comprehensive interface for:
    - Configuring atmospheric parameters and molecular compositions
    - Generating spectral models with different radiative transfer codes
    - Visualizing and comparing model results
    - Saving model outputs and plots
    
    The panel includes controls for:
    - Target selection and basic parameters
    - Temperature-pressure profile configuration
    - Molecular composition setup
    - Model calculation and visualization
    - Results saving and export
    """

    def __init__(self, parent, row, column, color, path_default, frame_input, width, height, window, **kwargs):
        """
        Initialize the manual model frame.
        
        Args:
            parent: Parent widget
            row: Row position in parent
            column: Column position in parent
            color: Background color
            path_default: Default path for file operations
            frame_input: Input frame containing target information
            width: Frame width
            height: Frame height
            window: Main window
            **kwargs: Additional keyword arguments
        """
        # Initialize instance variables
        self.dict_rayleigh_base = {
            "H2":None, "He":None, "H":None, "e-":None, "H-":None
        }
        self.past_string = None
        self.last_rad_mode = None
        self.list_condensed_last_plot = None
        self.dropdown_target = None
        self.current_plot_for_saving = None
        self.current_plot = None
        self.target_information = None
        self.last_max_wl = None
        self.last_min_wl = None
        self.last_min_pressure = None
        self.last_max_pressure = None
        self.last_pressure_layers = None
        self.last_continuum_opacity = None
        self.model_obj = None
        self.path_folder_targets = frame_input.frame_path.path_folder_targets.get_text()
        self.dict_params_plot = None
        self.color = color
        self.wl = None
        self.model = None
        self.auto_image = None
        self.auto_string = None
        self.stop_blink_var = None
        self.current_string = None
        self.p = None
        self.counter_blinks = None
        self.current_image = None
        self.global_exc = 0
        self.first_enter = 1
        self.frame_input = frame_input
        self.path_default = path_default
        self.window = window
        
        # Initialize parent class
        super().__init__(parent, color, row, column, **kwargs)
        
        # Setup paths and directories
        self._setup_paths()
        
        # Initialize UI components
        self._init_ui_components(width, height)
        
        # Setup event handlers
        self._setup_event_handlers()
        
        # Initialize state variables
        self._init_state_variables()

    def _setup_paths(self):
        """Setup necessary paths and directories."""
        username = os.environ.get("USER")
        self.folder_plot = str(Path(self.path_default, "GUIBRUSHR", "Files", "Temp_Files_GUI", username, "Folder_plot_temp"))
        self.path_results = str(Path(self.folder_plot, ConstantVariables.RESULTS_FOLDER))
        os.system("mkdir -p " + self.path_results)
        self.current_plot_for_saving = None
        
        # Connect to database
        target_list = get_target_list(self.path_folder_targets)
        self.target_information = get_target_info(self.path_folder_targets, target_list[0])
        DB = DBSQLite3(self.path_default)
        DB.close_DB()

    def _init_ui_components(self, width, height):
        """Initialize UI components and layout."""
        # Create main frames
        self.frame_manual_model = tk.Frame(
            self.window, highlightbackground="black", highlightthickness=1, padx=5, pady=2
        )
        self.frame_info_params = tk.Frame(
            self.frame_manual_model,
            highlightbackground="black",
            highlightthickness=1,
            padx=5,
            pady=2,
        )
        
        # Create panel rows
        self._create_panel_rows()
        
        # Create parameter controls
        self._create_parameter_controls()
        
        # Create plot area
        self._create_plot_area()

    def _create_panel_rows(self):
        """Create panel rows for organizing UI elements."""
        self.row1 = MyPanel(self, self.color, 0, 1)
        self.row2 = MyPanel(self, self.color, 1, 1)
        self.row_instruments = MyPanel(self.row2, self.color, 0, 2, rowspan=2)
        self.row3 = MyPanel(self, self.color, 2, 1)
        self.row4 = MyPanel(self, self.color, 3, 1)
        self.row5 = MyPanel(self, self.color, 4, 1)

        # Create row for plot area (replaces row_params[-1])
        self.row_plot = MyPanel(self, self.color, 7, 1)

        # Create rowinvisible for hidden parameters
        self.rowinvisible = MyPanel(self, self.color, 99, 1, visible=False)

    def _create_parameter_controls(self):
        """Create parameter control widgets organized by panel (from parameters.yaml)."""
        # Target selection
        self.dropdown_target = MyDropdown(
            self.row1, 0, 1, get_target_list(self.path_folder_targets),
            "Target:", color=self.color, columnspan=2
        )

        # Stellar spectrum
        self.dropdown_stellar_spectrum = MyDropdown(
            self.row1, 0, 3, ["Planck", "Phoenix"],
            "Stellar Spectrum:", initial_value=0,
            color=self.color, columnspan=2
        )

        # Resolution selection
        self.dropdown_resolution = MyDropdown(
            self.row1, 0, 5, ["High"], "Resolution:",
            color=self.color, columnspan=2, initial_value=0
        )

        # Radiation mode
        self.dropdown_rad_mode = MyDropdown(
            self.row1, 0, 7, ConstantVariables.LIST_RAD_MODE,
            "Rad. Transf. Mode:", initial_value=0,
            color=self.color, columnspan=2
        )

        # Chemistry
        self.dropdown_chemistry = MyDropdown(
            self.row3, 0, 3, ConstantVariables.LIST_CHEMISTRY_TABLE, "Chemistry:", initial_value=1,
            color=self.color, columnspan=2
        )

        # T-P profile
        self.dropdown_t_p_profile = MyDropdown(
            self.row3, 0, 5, ConstantVariables.LIST_PT_PROFILE_TABLE, "T-P profile:", color=self.color,
            columnspan=2
        )

        # Scattering
        self.check_scattering = MyCheckBox(
            self.row3, 0, 7, "Scattering power law"
        )

        # continuum opacity
        self.textfield_continuum_opacity = MyTextField(
            self.row3, 0, 8, "H2-H2,H2-He", "Continuum:", color=self.color, columnspan=2, width=20
        )

        # include h-
        self.checkbox_include_h_m = MyCheckBox(
            self.row3, 0, 10, "Include H-"
        )

        # Temp_min
        self.entry_temp_min = MyEntry(
            self.row3, 0, 12, "300", "Temp Min [K]:", color=self.color, columnspan=2
        )

        # Temp_max
        self.entry_temp_max = MyEntry(
            self.row3, 0, 14, "3000", "Temp Max [K]:", color=self.color, columnspan=2
        )

        # Temp_step
        self.entry_temp_step = MyEntry(
            self.row3, 0, 16, "150", "Temp Step [K]:", color=self.color, columnspan=2
        )

        # Radiative transfer code
        self.dropdown_retrieval_mode = MyDropdown(
            self.row4, 0, 1, ConstantVariables.LIST_RETRIEVAL_MODE,
            "Radiative transfer code:", color=self.color, columnspan=2
        )

        # min_pressure
        self.entry_min_pressure = MyEntry(
            self.row4, 0, 3, "-9", "Min pressure (Eq, 10^):", color=self.color, columnspan=2
        )

        # max_pressure
        self.entry_max_pressure = MyEntry(
            self.row4, 0, 6, "2", "Max pressure (Eq, 10^):", color=self.color, columnspan=2
        )

        # Pressure Layers
        self.entry_pressure_layers = MyEntry(
            self.row4, 0, 8, "100", "# Pressure Layers:", color=self.color, columnspan=2
        )

        # lbl sampling
        self.entry_lbl_sampling = MyEntry(
            self.row4, 0, 10, "4", "LBL Sampling:", color=self.color, columnspan=2
        )

        # Nights
        self.textfield_model_name = MyTextField(
            self.row5, 0, 7, "h2o", "Name model:", color=self.color, columnspan=3, width=20
        )

        # min_wl_hr
        self.entry_min_wl_hr = MyEntry(
            self.row5, 0, 10, "0.9", "Min wl HR[um]:", color=self.color, columnspan=2
        )

        # max_wl_hr
        self.entry_max_wl_hr = MyEntry(
            self.row5, 0, 12, "2.6", "Max wl HR[um]:", color=self.color, columnspan=2
        )

        # Help button
        self._create_help_button()

        self.panel_parameters = MyPanel(self, self.color, 5, 1)

        # Global help button for column descriptions
        self._create_global_help_button()

        # Initialize parameter structures
        self.yes_1 = 1
        self.no_0 = 0
        self.normal = "normal"
        self.disabled = "disabled"

        self.params_list = ConstantVariables.params_list
        self.param_array = [None for _ in range(len(self.params_list))]
        self.list_multiple_param = ConstantVariables.LIST_MULTIPLE_PARAM
        self.start_molec = self.get_param_index("H2")

        # Create parameters organized by panel from parameters.yaml
        self._create_parameters_by_panel()

    def _create_parameters_by_panel(self):
        """
        Create parameters organized by their panel grouping from parameters.yaml using tabs.

        Creates 8 tabs (one for each panel 0-7) and displays parameters in table format.
        Parameters with row_manual < 0 are hidden in rowinvisible panel.
        """
        # Create tab panel structure
        self.tab_manager = MyTabPanel(
            parent=self.panel_parameters,
            tab_list=ConstantVariables.list_tab_frame_parameter,
            row=0,
            column=2
        )

        # Create panel frames for each tab with table headers
        color_params = ConstantVariables.COLOR_SUB_NOTEBOOK_2
        self.list_panel = []

        for panel_idx, panel in enumerate(self.tab_manager.tab_list):
            # Create panel frame for this tab
            panel_frame = MyPanel(panel, color_params, 0, 0)
            self.list_panel.append(panel_frame)

        # Track row and column position within each panel (start at 1 since row 0 is headers)
        NUM_PARAMETER_PANELS = 8  # Number of parameter tabs (Planet+Data, Clouds, Temperatures, etc.)
        MAX_ROWS_PER_COLUMN = 4  # Maximum rows before moving to next column
        COLUMNS_PER_PARAMETER_TABLE = 9  # Total columns occupied by one parameter table (7 + 1 spacing)

        panel_rows = {i: 1 for i in range(NUM_PARAMETER_PANELS)}
        panel_columns = {i: 0 for i in range(NUM_PARAMETER_PANELS)}  # Track current column offset
        panel_headers_created = {i: [0] for i in range(NUM_PARAMETER_PANELS)}  # Track which columns have headers

        # Track which panels have special buttons for help text
        panel_has_vmr_button = [False] * NUM_PARAMETER_PANELS
        panel_has_hce_button = [False] * NUM_PARAMETER_PANELS
        panel_has_molecule_button = [False] * NUM_PARAMETER_PANELS
        panel_has_condensed_button = [False] * NUM_PARAMETER_PANELS
        panel_has_hydrogen_button = [False] * NUM_PARAMETER_PANELS

        # Create initial headers for all panels (first column)
        labels = ["Parameter", "Present", "Value", "Type", "Rayleigh"]
        columnspan_values = [3, 1, 2, 1, 1]
        column_positions = [1, 4, 5, 7, 8]
        for panel_idx in range(NUM_PARAMETER_PANELS):
            for label, colpos, colsp in zip(labels, column_positions, columnspan_values):
                MyLabel(
                    self.list_panel[panel_idx], 0, colpos,
                    label_text=label,
                    columnspan=colsp,
                    color=color_params
                )

        # Iterate through params and organize by panel
        for param_name in ConstantVariables.PARAMS_DICT.keys():
            temp_param = ConstantVariables.PARAMS_DICT[param_name]

            # Extract parameter properties
            name = temp_param["name"]
            panel_idx = temp_param["panel"]
            row_manual = temp_param["row_manual"]
            present = temp_param["present"]
            state = temp_param["state"]
            mass = temp_param["mass"]
            if mass == -999:
                mass = None
            rayleigh = temp_param["rayleigh"]
            type_params = temp_param["type"]

            # Get parameter value (handle special cases)
            value = temp_param["value"]
            if value in ["target_kp", "h_ecc", "k_ecc", "radius", "temperature", "omega"]:
                if self.target_information.kp is None:
                    value = -99999
                else:
                    if value == "target_kp":
                        value = self.target_information.kp
                    elif value == "h_ecc":
                        value = np.sqrt(self.target_information.eccentricity) * np.sin(
                            self.target_information.argument_of_periastron)
                    elif value == "k_ecc":
                        value = np.sqrt(self.target_information.eccentricity) * np.cos(
                            self.target_information.argument_of_periastron)
                    elif value == "radius":
                        value = self.target_information.radius
                    elif value == "temperature":
                        value = self.target_information.equilibrium_temperature
                    elif value == "omega":
                        value = 2 * np.pi / self.target_information.period
            elif value == "nothing":
                value = ""
            else:
                value = float(value)

            # Check if parameter should be visible (row_manual >= 0)
            is_visible = row_manual >= 0

            if is_visible:
                # Get target panel
                current_tab_parameter = self.list_panel[panel_idx]

                # Check if we need to move to next column (more than 4 rows)
                if panel_rows[panel_idx] > MAX_ROWS_PER_COLUMN:
                    # Reset row to 1 and move to next column
                    panel_rows[panel_idx] = 1
                    panel_columns[panel_idx] += 1

                    # Create headers for the new column if not already created
                    if panel_columns[panel_idx] not in panel_headers_created[panel_idx]:
                        column_offset = panel_columns[panel_idx] * COLUMNS_PER_PARAMETER_TABLE

                        # Add vertical separator between columns
                        separator = tk.Frame(
                            current_tab_parameter,
                            width=2,
                            bg='gray',
                            highlightbackground='gray',
                            highlightthickness=1
                        )
                        separator.grid(
                            row=0,
                            column=column_offset,
                            rowspan=MAX_ROWS_PER_COLUMN + 2,
                            sticky='ns',
                            padx=10,
                            pady=5
                        )

                        for label, colpos, colsp in zip(labels, column_positions, columnspan_values):
                            MyLabel(
                                current_tab_parameter, 0, colpos + column_offset,
                                label_text=label,
                                columnspan=colsp,
                                color=color_params
                            )
                        panel_headers_created[panel_idx].append(panel_columns[panel_idx])

                # Get row and calculate column offset
                row = panel_rows[panel_idx]
                column_offset = panel_columns[panel_idx] * COLUMNS_PER_PARAMETER_TABLE
                column = 1 + column_offset

                # Create parameter widget
                self.param_array[self.get_param_index(name)] = ParamClassForManualPlot(
                    current_tab_parameter, row, column,
                    name, value, present, state,
                    color=self.color, mass_molec=mass, rayleigh=rayleigh, type_params=type_params
                )

                # Special handling for f_rot parameter - add kernel sampling after the parameter
                if name == "f_rot":
                    # Place kernel sampling to the right of the parameter (after column 6)
                    self.entry_rv_sampling = MyEntry(
                        current_tab_parameter, row, 10 + column_offset, "0.1",
                        "Kernel sampling (km/s):", color=self.color, columnspan=3, entry_width=5
                    )

                # Special handling for width_peak parameter (VMR)
                if name == "width_peak":
                    row += 1
                    panel_rows[panel_idx] += 1
                    panel_has_vmr_button[panel_idx] = True

                    # Create separate panel for VMR widgets
                    self.panel_vmr = MyPanel(current_tab_parameter, color_params, row, 1, columnspan=15)

                    # VMR widgets in separate panel
                    self.textfield_molec_selected = MyTextField(
                        self.panel_vmr, 0, 1, "", "Current molecules:",
                        color=self.color, columnspan=5, width=40
                    )

                    self.button_update_params_vmr = MyButton(
                        self.panel_vmr, 0, 6, 'UPDATE VMR PARAMETERS', "#00AA00",
                        command=self.update_params_vmr_func, columnspan=5, width=25
                    )

                    row += 1
                    panel_rows[panel_idx] += 1

                # Special handling for sio_ratio_linear parameter (equilibrium chemistry elements)
                elif name == "sio_ratio_linear":
                    row += 1
                    panel_rows[panel_idx] += 1
                    panel_has_hce_button[panel_idx] = True

                    # Element selector in separate panel (row 0)
                    elems = ConstantVariables.LIST_ELEMENT_FOR_HYBRID
                    self.elems_list = ParamClassForManualPlot(
                        current_tab_parameter,
                        row,
                        1,
                        "",
                        -5,
                        self.no_0,
                        self.normal,
                        molec_line=self.yes_1,
                        mass_molec=0,
                        color=self.color,
                        molecs=elems,
                        isotopes=None,
                        opacity_line_lists=None,
                        isotope_masses={elem: 0 for elem in elems},
                        type_params="Log10",
                        single_dropdown=True
                    )
                    row += 1

                    # Create separate panel for element selection widgets
                    self.panel_elements = MyPanel(current_tab_parameter, color_params, row, 1, columnspan=15)

                    # Button on row 1 in separate panel
                    self.button_hce = MyButton(
                        self.panel_elements, 1, 1, 'ADD ELEMENT TO HYBRID CHEMICAL EQUILIBRIUM', "#00AA00",
                        columnspan=15, command=self.update_elem_textfield
                    )

                    # Textfield on row 2 in separate panel
                    self.textfield_hce = MyTextField(
                        self.panel_elements, 2, 1, "", label_text="Current elements:",
                        columnspan=15, color=color_params, height=5, width=110
                    )

                    row += 1
                    panel_rows[panel_idx] += 1

                # Special handling for e- parameter (molecules)
                elif name == "e-":
                    row += 1
                    panel_rows[panel_idx] += 1
                    panel_has_hydrogen_button[panel_idx] = True
                    panel_has_molecule_button[panel_idx] = True

                    # Create panel for UPDATE H-, H, e- button
                    self.panel_hydrogen_minus = MyPanel(current_tab_parameter, color_params, row + 1, 10, columnspan=15)
                    self.button_update_hydrogen = MyButton(
                        self.panel_hydrogen_minus, 0, 1, 'UPDATE H-, H, e-',
                        "#00AA00", command=self.update_hydrogen_menus_btn
                    )

                    row = 5
                    panel_rows[panel_idx] += 1

                    # Molecule selector in separate panel (row 0)
                    molecs, isotopes, opacity_line_lists, _, isotope_masses = get_line_lists()
                    self.molecs_list = ParamClassForManualPlot(
                        current_tab_parameter,
                        row,
                        1,
                        "",
                        -5,
                        self.yes_1,
                        self.normal,
                        molec_line=self.yes_1,
                        mass_molec=0,
                        color=self.color,
                        molecs=molecs, isotopes=isotopes, opacity_line_lists=opacity_line_lists,
                        isotope_masses=isotope_masses, type_params="Log10"
                    )

                    row += 1

                    # Create separate panel for molecule selection widgets
                    self.panel_molecules = MyPanel(current_tab_parameter, color_params, row, 1, columnspan=15)

                    # Button on row 1 in separate panel
                    self.button_molecs = MyButton(
                        self.panel_molecules, 1, 1, 'ADD MOLECULE', "#00AA00",
                        columnspan=15, command=self.update_molec_textfield
                    )

                    # Textfield on row 2 in separate panel
                    self.textfield_molecs = MyTextField(
                        self.panel_molecules, 2, 1, "", label_text="Current molecules:",
                        columnspan=15, color=color_params, height=5, width=110
                    )

                    row += 1
                    panel_rows[panel_idx] += 1

                # Special handling for eddy_diff_coeff parameter (condensed molecules)
                elif name == "eddy_diff_coeff":
                    row += 1
                    panel_rows[panel_idx] += 1
                    panel_has_condensed_button[panel_idx] = True

                    # Get condensed molecule data
                    condensed_molecs_list, condensed_names_list, condensed_masses, condensed_visualization_name = get_condensed_line_list()

                    # Condensed molecule selector using ParamClassForManualPlot (row 0 in separate panel)
                    # Use single_dropdown=True for condensed molecules (only one dropdown, not three)
                    self.condensed_molecs_list = ParamClassForManualPlot(
                        current_tab_parameter,
                        row,
                        1,
                        "",
                        -7,
                        self.no_0,
                        self.normal,
                        molec_line=self.yes_1,
                        mass_molec=0,
                        color=self.color,
                        molecs=condensed_visualization_name,
                        isotopes=None,
                        opacity_line_lists=None,
                        isotope_masses={condensed_visualization_name[i]: condensed_masses[i] for i in range(len(condensed_visualization_name))},
                        type_params="Log10",
                        single_dropdown=True
                    )
                    row += 1

                    # Create separate panel for condensed molecule selection widgets
                    self.panel_condensed = MyPanel(current_tab_parameter, color_params, row, 1, columnspan=15)

                    # Store mapping for retrieval
                    self.condensed_molecs_list_data = condensed_molecs_list
                    self.condensed_visualization_name_data = condensed_visualization_name

                    # Button on row 0 in separate panel
                    self.button_condensed_molecs = MyButton(
                        self.panel_condensed, 0, 1, 'ADD CONDENSED MOLECULE', "#00AA00",
                        columnspan=7, command=self.update_condensed_molec_textfield
                    )

                    # Textfield on row 1 in separate panel
                    self.textfield_condensed_molecs = MyTextField(
                        self.panel_condensed, 1, 1, "",
                        label_text="Current condensed molecules:",
                        columnspan=7, color=color_params, height=5, width=110
                    )

                    row += 1
                    panel_rows[panel_idx] += 1

                # Increment row counter for this panel
                panel_rows[panel_idx] += 1

            else:
                # Parameter is hidden (row_manual < 0)
                self.param_array[self.get_param_index(name)] = ParamClassForManualPlot(
                    self.rowinvisible, 0, 1, name, value, present, state,
                    color=self.color, mass_molec=mass, rayleigh=rayleigh, type_params=type_params
                )

        # Pack all panel frames
        for panel in self.list_panel:
            panel.pack(padx=3, pady=3)

        # Create help buttons for each panel
        for panel_idx in range(NUM_PARAMETER_PANELS):
            _create_tab_help_button_manual(
                panel_idx,
                self.list_panel[panel_idx],
                has_vmr_button=panel_has_vmr_button[panel_idx],
                has_hce_button=panel_has_hce_button[panel_idx],
                has_molecule_button=panel_has_molecule_button[panel_idx],
                has_condensed_button=panel_has_condensed_button[panel_idx],
                has_hydrogen_button=panel_has_hydrogen_button[panel_idx]
            )

    def _create_plot_area(self):
        """Create controls and text area (plot opens in standalone window)."""
        # Control buttons
        self.button_plot = MyButton(
            self.row_plot, 0, 1, 'PLOT', '#FF0000',
            self.plot, color_panel=self.color
        )
        self.button_save = MyButton(
            self.row_plot, 1, 1, 'SAVE PLOT + DATA', '#FFFFFF',
            self.save_model, color_panel=self.color
        )

        # Medians text field
        self.textfield_medians_errors = MyTextField(
            self.row_plot, 0, 2, "", 'Values',
            color=self.color, columnspan=3, rowspan=15, height=15
        )

    def _setup_event_handlers(self):
        """Setup event handlers for UI components."""
        self.dropdown_t_p_profile.chosen_var.trace("w", lambda *args: self.update_atmo())
        self.dropdown_chemistry.chosen_var.trace("w", lambda *args: self.update_frame())
        self.dropdown_target.chosen_var.trace("w", lambda *args: self.update_target())
        self.check_scattering.var.trace("w", lambda *args: self.scattering_check())
        self.checkbox_include_h_m.var.trace("w", lambda *args: self.include_h_m())

    def _create_help_button(self):
        """Create help button with documentation for Frame Manual Model."""
        help_text = {
            "Manual Model Overview": "This panel allows you to manually create atmospheric models for exoplanets by configuring radiative transfer parameters, molecular compositions, and temperature-pressure profiles.",
            "Target": "Select the exoplanet to analyze. Target list is populated from folders in the target directory specified in configuration. Target properties (mass, radius, temperature) are automatically loaded.",
            "Stellar Spectrum": "Model for the stellar spectrum. 'Planck' uses a blackbody approximation. 'Phoenix' uses detailed PHOENIX stellar atmosphere models.",
            "Resolution": "Spectral resolution mode for the model. 'HR' = high-resolution only in this manual model interface.",
            "Rad. Transf. Mode": "Radiative transfer mode. 'Transmission' for transmission spectroscopy (transiting planets), 'Emission' for emission spectroscopy (secondary eclipses or direct imaging).",
            "Chemistry": "Atmospheric chemistry model. 'Equilibrium' uses chemical equilibrium calculations. 'Free' allows independent molecular abundances. 'Hybrid' combines both approaches by varying the x/H ratio (where x could be any atom) manually.",
            "T-P profile": "Temperature-pressure profile parameterization. There are several options: 'isot' (which stands for isothermal), 'Guillot' (Guillot (2010) temperature profile based on the three-channel Eddington approximation, as described Line et al. (2013)), 'madhu' (temperature profile from Madhusudhan & Seager (2009)), 'personalized' (that is a simpler representation for inverted profiles), 'FourNodeSpline' (that uses four pressure-temperature anchor points connected by cubic splines. Balances flexibility with parameter efficiency by interpolating between pressure levels), and the possibility (in testing) to include other PT profiles.",
            "Scattering power law": "Enable to include scattering opacity as a power law. Adds scattering parameters (amplitude and slope) to the model.",
            "Continuum": "Continuum opacity sources to include. Default is 'H2-H2,H2-He' for collision-induced absorption. Comma-separated list.",
            "Include H-": "Include H- (hydrogen anion) bound-free and free-free opacity. Important for hot stellar atmospheres and irradiated planets.",
            "UPDATE H-, H, e- button": "Updates the abundances of H- (hydrogen minus), H (atomic hydrogen), and e- (free electrons) based on the H2 and He abundances. Calculates derived abundances for consistency in hot atmosphere models.",
            "Temp Min [K]": "Minimum temperature in Kelvin (required by PyratBay, ongoing).",
            "Temp Max [K]": "Maximum temperature in Kelvin (required by PyratBay, ongoing).",
            "Temp Step [K]": "Temperature step size in Kelvin (required by PyratBay, ongoing).",
            "Radiative transfer code": "Determines which radiative transfer code to use for the model. 'petitRADTRANS' is the default, PyratBay is in development.",
            "Min pressure": "Minimum pressure (log10 bar) for pressure grid. Typically -8 corresponds to ~1e-8 bar at high altitudes.",
            "Max pressure": "Maximum pressure (log10 bar) for pressure grid. Typically 1 corresponds to ~10 bar at deep layers.",
            "Pressure Layers": "Number of atmospheric layers for calculations. More layers increase accuracy but require more computation time.",
            "LBL Sampling": "Line-by-line opacity sampling factor for petitRADTRANS. Higher values reduce computation time by sampling fewer wavelength points.",
            "Name model": "Output name for saving the model results.",
            "Min/Max wl HR": "Wavelength range in micrometers for high-resolution spectrum calculation.",
            "Parameter Tabs": "Eight tabs organize model parameters: Stellar, Orbital, Opacity, Temperature-Pressure, Chemistry, Rotation, VMR profiles, and Additional.",
            "PLOT": "Calculate and display the model spectrum with current parameters. Also generates T-P and VMR profiles.",
            "BLINK": "Toggle between two calculated models for visual comparison. Requires two plots to be generated first.",
            "SAVE PLOT + DATA": "Save the current model spectrum and parameters to FITS and YAML files in the target directory."
        }

        # Create help button in row5 at an available column position
        HelpButton(
            self.row5, 0, 14,
            "Manual Model Help",
            help_text,
            columnspan=2
        )

    def _create_global_help_button(self):
        """Create global help button explaining parameter table column headers."""
        help_text = {
            "Parameter": "Parameter name. This identifier is used to reference the specific atmospheric or instrumental parameter.",

            "Present": "Presence flag. Determines if this parameter is included in the current model configuration. Checkbox indicates active status.",

            "Value": "Current value for the parameter. This is the value used for calculating the model spectrum. Can be numeric values representing physical quantities.",

            "Type": "Parameter type for calculations. 'Linear' for parameters used in linear space. 'Log10' for parameters in log10 space (e.g., pressures, abundances, opacities)."
        }

        self.global_help_button = HelpButton(
            self.panel_parameters, 0, 1,
            "Parameter Table Columns Help",
            help_text,
            columnspan=1
        )

    def _init_state_variables(self):
        """Initialize state variables."""
        self.global_exc = 1
        self.current_plot = 1
        self.last_auto_img = None
        self.last_auto_string = None
        self.curr_auto_imm = None
        self.curr_auto_string = None
        self.last_png_auto = None
        self.last_pkl_auto = None
        self.model_obj = None

    def update_molec_textfield(self):
        """
        Update the molecule text field with the currently selected molecule information.
        Adds a new line with molecule formula, isotope, sub-isotope, presence flag,
        mass, value, VMR flag, and formula.
        """
        if self.molecs_list.checkbox_presence.get_value():
            last_text = self.textfield_molecs.get_text()
            if last_text == "":
                prefix = ""
            else:
                prefix = "\n"
            new_line = (
                f"{prefix}"
                f"{self.molecs_list.dropdown_molec.get_value()},"
                f"{self.molecs_list.dropdown_isotope.get_value()},"
                f"{self.molecs_list.dropdown_opacity_line_list_HR.get_value()},"
                f"{self.molecs_list.checkbox_presence.get_value()},"
                f"{self.molecs_list.mass_molec},"
                f"{self.molecs_list.entry_value.get_value()},"
                f"{int(self.molecs_list.VMR)},"
                f"{self.molecs_list.molec_formula},"
                f"{self.molecs_list.get_rayleigh_value()}\n"
            )
            self.textfield_molecs.insert_text(last_text + new_line)

    def update_elem_textfield(self):
        """
        Update the elements text field with currently selected element information.
        Adds element for hybrid chemical equilibrium.
        """
        if hasattr(self, 'elems_list') and self.elems_list.checkbox_presence.get_value():
            last_text = self.textfield_hce.get_text()
            prefix = "" if last_text == "" else "\n"

            elem_name = self.elems_list.dropdown_molec.get_value()
            new_line = (
                f"{prefix}{elem_name},"
                f"{self.elems_list.checkbox_presence.get_value()},"
                f"{self.elems_list.entry_value.get_value()}\n"
            )
            self.textfield_hce.insert_text(last_text + new_line)

    def update_condensed_molec_textfield(self):
        """
        Update the condensed molecules text field with currently selected molecule information.
        Adds condensed molecule for cloud/aerosol modeling.
        """
        if hasattr(self, 'condensed_molecs_list'):
            last_text = self.textfield_condensed_molecs.get_text()
            prefix = "" if last_text == "" else "\n"

            # Get selected condensed molecule (visualization name from dropdown)
            selected_condsensate_name = self.condensed_molecs_list.dropdown_molec.get_value()

            # Find index in visualization names (convert to list if numpy array)
            condensate_name_list = list(self.condensed_visualization_name_data)
            selected_index = condensate_name_list.index(selected_condsensate_name)
            # Get actual molecule name (internal name)
            molec_name = self.condensed_molecs_list_data[selected_index]

            identifier = ConstantVariables.ALL_CONDENSED_MOLECS_DICT[selected_condsensate_name]["name"]
            new_line = (
                f"{prefix}{identifier},"
                f"{self.condensed_molecs_list.checkbox_presence.get_value()},"
                f"{self.condensed_molecs_list.mass_molec},"
                f"{self.condensed_molecs_list.entry_value.get_value()},"
                f"{molec_name}\n"
            )
            self.textfield_condensed_molecs.insert_text(last_text + new_line)

    def get_param_index(self, param_name):
        """
        Get the index of a parameter in the parameters list.
        
        Args:
            param_name: Name of the parameter to find
            
        Returns:
            int: Index of the parameter in the list
        """
        return int(self.params_list.index(param_name))

    def update_params_vmr_func(self):
        """
        Update VMR (Volume Mixing Ratio) parameters based on selected molecules.
        Collects information about molecules and updates the corresponding parameter fields.
        """
        counter_0 = ""
        counter_1 = ""
        counter_2 = ""
        str_molec = ""
        for elem in self.param_array[self.start_molec:]:
            if elem is not None and elem.is_considered() and not elem.VMR:
                counter_0 += "-3,"
                counter_1 += "-5,"
                counter_2 += "2,"
                _, name_retrieval, _ = elem.get_name()
                str_molec += name_retrieval + ","
        if len(counter_0) > 0:
            counter_0 = counter_0[:-1]
            counter_1 = counter_1[:-1]
            counter_2 = counter_2[:-1]
            str_molec = str_molec[:-1]
        self.param_array[self.get_param_index("vmr_peak")].entry_value.set_value(counter_0)
        self.param_array[self.get_param_index("pressure_peak")].entry_value.set_value(counter_1)
        self.param_array[self.get_param_index("width_peak")].entry_value.set_value(counter_2)
        self.textfield_molec_selected.insert_text(str_molec)

    def update_hydrogen_menus_btn(self):
        """
        Update hydrogen-related parameter values.

        This method calculates and updates the values for H, H-, and e- parameters
        based on the current H2 and He values.
        """
        if self.checkbox_include_h_m.get_value():
            # Get H2 and He values
            H2_value = float(self.param_array[self.get_param_index("H2")].entry_value.get_value())
            He_value = float(self.param_array[self.get_param_index("He")].entry_value.get_value())

            # Calculate derived values
            H_m = He_value + H2_value
            e_m = He_value - H2_value
            H = He_value * H2_value

            # Update parameter values
            self.param_array[self.get_param_index("H")].entry_value.set_value(H)
            self.param_array[self.get_param_index("H-")].entry_value.set_value(H_m)
            self.param_array[self.get_param_index("e-")].entry_value.set_value(e_m)

    def update_LR(self):
        """
        Update low resolution parameters based on resolution selection.
        Enables/disables low resolution offset parameter based on resolution mode.
        """
        if self.global_exc == 0:
            return
        if self.dropdown_resolution.get_value() == ConstantVariables.LIST_RESOLUTION_TABLE[1]:  # High+Low
            self.param_array[self.get_param_index("offsetLR")].parameter_status("normal")
        else:
            self.param_array[self.get_param_index("offsetLR")].parameter_status("disabled")

    def update_frame(self):
        """
        Update the frame based on chemistry selection.
        Enables/disables appropriate parameters based on the selected chemistry mode.
        """
        if self.global_exc == 0:
            return
        self.param_array[self.get_param_index("kp")].parameter_status("normal")
        self.param_array[self.get_param_index("rv")].parameter_status("normal")
        self.param_array[self.get_param_index("sf")].parameter_status("normal")
        if self.dropdown_chemistry.get_value() == ConstantVariables.LIST_CHEMISTRY_TABLE[0]:  # chemcat
            self.param_array[self.get_param_index("Pc")].parameter_status("normal")
            self.param_array[self.get_param_index("gamma")].parameter_status("disabled")
            self.param_array[self.get_param_index("H2")].parameter_status("normal")
            self.param_array[self.get_param_index("He")].parameter_status("normal")
            self.param_array[self.get_param_index("met")].parameter_status("normal")
            self.param_array[self.get_param_index("met")].checkbox_presence.set_value(1)
            self.param_array[self.get_param_index("co_ratio")].parameter_status("normal")
            self.param_array[self.get_param_index("co_ratio")].checkbox_presence.set_value(1)
            self.param_array[self.get_param_index("co_ratio_linear")].parameter_status("normal")
            self.param_array[self.get_param_index("co_ratio_linear")].checkbox_presence.set_value(1)
            self.param_array[self.get_param_index("sio_ratio_linear")].parameter_status("normal")
            self.param_array[self.get_param_index("sio_ratio_linear")].checkbox_presence.set_value(1)
            self.molecs_list.parameter_status("disabled")
            self.molecs_list.disabled = 0
        else:  # Manual
            self.param_array[self.get_param_index("Pc")].parameter_status("normal")
            self.param_array[self.get_param_index("gamma")].parameter_status("disabled")
            self.param_array[self.get_param_index("H2")].parameter_status("normal")
            self.param_array[self.get_param_index("He")].parameter_status("normal")
            self.param_array[self.get_param_index("met")].parameter_status("disabled")
            self.param_array[self.get_param_index("co_ratio")].parameter_status("disabled")
            self.param_array[self.get_param_index("co_ratio_linear")].parameter_status("disabled")
            self.param_array[self.get_param_index("sio_ratio_linear")].parameter_status("disabled")
            self.molecs_list.parameter_status("normal")
        self.update_atmo()
        self.scattering_check()

    def update_target(self):
        """
        Update target-specific parameters based on selected target.
        Updates parameters like Kp, temperature, and radius based on target information.
        """
        if self.global_exc == 0:
            return
        target_id = self.dropdown_target.get_value()
        self.target_information = get_target_info(self.path_folder_targets, target_id, self.dropdown_rad_mode.get_value())
        self.param_array[self.get_param_index("kp")].entry_value.set_value(self.target_information.kp)
        self.param_array[self.get_param_index("T0")].entry_value.set_value(self.target_information.equilibrium_temperature)
        self.param_array[self.get_param_index("T_high")].entry_value.set_value(self.target_information.equilibrium_temperature)
        self.param_array[self.get_param_index("T_low")].entry_value.set_value(self.target_information.equilibrium_temperature)
        self.param_array[self.get_param_index("rp")].entry_value.set_value(self.target_information.radius)
        self.param_array[self.get_param_index("omega")].entry_value.set_value(2 * np.pi / self.target_information.period)

    def update_atmo(self):
        """
        Update atmospheric parameters based on T-P profile selection.
        Enables/disables appropriate temperature and pressure parameters.
        """
        if self.global_exc == 0:
            return
        # Disable all T-P parameters by default
        self.param_array[self.get_param_index("T0")].parameter_status("disabled")
        self.param_array[self.get_param_index("p1")].parameter_status("disabled")
        self.param_array[self.get_param_index("p2")].parameter_status("disabled")
        self.param_array[self.get_param_index("p3")].parameter_status("disabled")
        self.param_array[self.get_param_index("alpha1")].parameter_status("disabled")
        self.param_array[self.get_param_index("alpha2")].parameter_status("disabled")
        self.param_array[self.get_param_index("gamma_g")].parameter_status("disabled")
        self.param_array[self.get_param_index("kappa_IR")].parameter_status("disabled")
        self.param_array[self.get_param_index("T_low")].parameter_status("disabled")
        self.param_array[self.get_param_index("T_high")].parameter_status("disabled")
        self.param_array[self.get_param_index("P_low")].parameter_status("disabled")
        self.param_array[self.get_param_index("P_high")].parameter_status("disabled")
        self.param_array[self.get_param_index("T_int")].parameter_status("disabled")
        self.param_array[self.get_param_index("T0_node")].parameter_status("disabled")
        self.param_array[self.get_param_index("T1_node")].parameter_status("disabled")
        self.param_array[self.get_param_index("T2_node")].parameter_status("disabled")
        self.param_array[self.get_param_index("T3_node")].parameter_status("disabled")
        self.param_array[self.get_param_index("P1_node")].parameter_status("disabled")
        self.param_array[self.get_param_index("P2_node")].parameter_status("disabled")

        # Enable parameters based on selected T-P profile
        if self.dropdown_t_p_profile.get_value() == ConstantVariables.LIST_PT_PROFILE_TABLE[0]:
            self.param_array[self.get_param_index("T0")].parameter_status("normal")
        elif self.dropdown_t_p_profile.get_value() == ConstantVariables.LIST_PT_PROFILE_TABLE[1]:
            self.param_array[self.get_param_index("T0")].parameter_status("normal")
            self.param_array[self.get_param_index("p1")].parameter_status("normal")
            self.param_array[self.get_param_index("p2")].parameter_status("normal")
            self.param_array[self.get_param_index("p3")].parameter_status("normal")
            self.param_array[self.get_param_index("alpha1")].parameter_status("normal")
            self.param_array[self.get_param_index("alpha2")].parameter_status("normal")
        elif self.dropdown_t_p_profile.get_value() == ConstantVariables.LIST_PT_PROFILE_TABLE[2]:
            self.param_array[self.get_param_index("T0")].parameter_status("normal")
            self.param_array[self.get_param_index("kappa_IR")].parameter_status("normal")
            self.param_array[self.get_param_index("gamma_g")].parameter_status("normal")
            self.param_array[self.get_param_index("T_int")].parameter_status("normal")
        elif self.dropdown_t_p_profile.get_value() == ConstantVariables.LIST_PT_PROFILE_TABLE[3]:
            self.param_array[self.get_param_index("T_low")].parameter_status("normal")
            self.param_array[self.get_param_index("T_high")].parameter_status("normal")
            self.param_array[self.get_param_index("P_low")].parameter_status("normal")
            self.param_array[self.get_param_index("P_high")].parameter_status("normal")
        elif self.dropdown_t_p_profile.get_value() == ConstantVariables.LIST_PT_PROFILE_TABLE[4]:
            self.param_array[self.get_param_index("T0_node")].parameter_status("normal")
            self.param_array[self.get_param_index("T1_node")].parameter_status("normal")
            self.param_array[self.get_param_index("T2_node")].parameter_status("normal")
            self.param_array[self.get_param_index("T3_node")].parameter_status("normal")
            self.param_array[self.get_param_index("P1_node")].parameter_status("normal")
            self.param_array[self.get_param_index("P2_node")].parameter_status("normal")

    def scattering_check(self):
        """
        Update scattering parameters based on scattering checkbox.
        Enables/disables scattering-related parameters.
        """
        if self.check_scattering.get_value():
            self.param_array[self.get_param_index("k0")].parameter_status("normal")
            self.param_array[self.get_param_index("gamma")].parameter_status("normal")
        else:
            self.param_array[self.get_param_index("k0")].parameter_status("disabled")
            self.param_array[self.get_param_index("gamma")].parameter_status("disabled")

    def create_plot(self, nome_filef, vmr_mean):
        """
        Create and save a plot of the model spectrum.
        
        Args:
            nome_filef: Base filename for saving plot
            vmr_mean: Volume mixing ratio information to include in plot
            
        Returns:
            tuple: (png_file_path, pickle_file_path, string_values)
        """
        fig, ax = plt.subplots(1, 1, figsize=(10, 6))
        ax.plot(self.wl, self.model, "b", alpha=0.5)
        ax.ticklabel_format(useOffset=False, axis='y')
        ax.set_xlabel("Wavelength (nm)")  # NB: avevi "sey_xlabel", typo
        if self.dropdown_rad_mode.get_value() == "Transmission":
            ylabel = r"1 - Transit depth ($\rm 1 - (R_{P}/R_{S})^2$)"
        else:
            ylabel = r"1 + Emission lines ($\rm 1 + (R_{P}/R_{S})^2$)"
        ax.set_ylabel(ylabel)
        ax.set_title(self.dropdown_rad_mode.get_value() + " spectrum")
        filepng = nome_filef + "_temp.png"
        file_pickl = nome_filef + "_temp.pkl"
        string_val = ""
        
        # Write parameters (excluding molecules)
        for elem in self.dict_params_plot.keys():
            if elem.replace("_", "") not in ConstantVariables.ALL_MOLEC:
                string_val += elem + ": " + str(self.dict_params_plot[elem]) + "\n"
        
        # Add VMR information
        string_val += vmr_mean
        string_val += (
                "\nSt.Dev(model): "
                + str(np.std(self.model))
                + "\nSum(model): "
                + str(np.sum(1 - self.model))
        )
        
        # Save plot and data
        with open(file_pickl, "wb") as fo:
            pickle.dump(string_val, fo)
        plt.savefig(fname=filepng, bbox_inches="tight", pad_inches=0.1)
        plt.gca().annotate(
            filepng,
            xy=(1.6, 1.0),
            xycoords="figure fraction",
            ha="right",
            va="top",
            color="black",
            fontsize=15,
        )
        plt.close(fig)
        return filepng, file_pickl, string_val

    def plot(self):
        """
        Generate and display a plot of the model spectrum.
        Handles model calculation, plot creation, and display updates.
        """
        try:
            self.button_plot.button.configure(state="disabled")
            # self.button_blink.button.configure(state="disabled")
            
            # Calculate model
            not_retrieval_obj, dict_params_plot = self.calc_model()
            
            # Update plot counter
            if self.current_plot == 2:
                self.current_plot = 1
            else:
                self.current_plot = 2
                
            # Create plot
            nome_file = str(Path(self.folder_plot, "plot" + str(self.current_plot)))
            self.model = not_retrieval_obj.depth_full_resolution_HR
            self.wl = not_retrieval_obj.wl_full_resolution_HR
            self.dict_params_plot = dict_params_plot
            filepng, filepkl, string_val = self.create_plot(nome_file, not_retrieval_obj.vmr_mean)
            
            # Update display
            self.current_plot_for_saving = filepng
            MyFigure(title="Manual Model", type_data="Image", image_path=filepng)
            self.textfield_medians_errors.insert_text(string_val)
            
            # Plot T-P profile
            parameters_tp_profile = {
                "T0": self.return_value_tp("T0"),
                "kappa_IR": self.return_value_tp("kappa_IR"),
                "gamma_g": self.return_value_tp("gamma_g"),
                "T_int": self.return_value_tp("T_int"),
                "T_low": self.return_value_tp("T_low"),
                "T_high": self.return_value_tp("T_high"),
                "P_low": self.return_value_tp("P_low"),
                "P_high": self.return_value_tp("P_high"),
                "p1": self.return_value_tp("p1"),
                "p2": self.return_value_tp("p2"),
                "p3": self.return_value_tp("p3"),
                "alpha1": self.return_value_tp("alpha1"),
                "alpha2": self.return_value_tp("alpha2"),
                "T0_node": self.return_value_tp("T0_node"),
                "T1_node": self.return_value_tp("T1_node"),
                "T2_node": self.return_value_tp("T2_node"),
                "T3_node": self.return_value_tp("T3_node"),
                "P1_node": self.return_value_tp("P1_node"),
                "P2_node": self.return_value_tp("P2_node"),
            }
            
            plot_tp_profile(
                int(self.entry_min_pressure.get_value()),
                int(self.entry_max_pressure.get_value()),
                int(self.entry_pressure_layers.get_value()),
                parameters_tp_profile,
                self.target_information.gravity,
                self.dropdown_t_p_profile.get_value(),
                Path(nome_file).parent,
                False,
                None
            )
            
            plot_vmr(
                self.model_obj.atmosphere.pressure_data.pressures,
                not_retrieval_obj.vmr_dict,
                Path(nome_file).parent
            )
            
        except Exception as e:
            MyFigure(title="Error in manual plot", message=str(e) + "\n" + traceback.format_exc())
            print(str(e) + "\n" + str(traceback.format_exc()))
            
        finally:
            self.button_plot.button.configure(state="normal")
            # self.button_blink.button.configure(state="normal")

    def return_value_tp(self, name):
        """
        Get temperature-pressure parameter value with error handling.
        
        Args:
            name: Name of the parameter
            
        Returns:
            ValueErrorTP: Parameter value with error handling
        """
        a = self.param_array[self.get_param_index(name)].entry_value.get_value()
        if a is not None:
            a = float(a)
            if name in ["kappa_IR", "gamma_g"]:
                a = 10 ** a
        return ValueErrorTP(a, 0)

    def blink(self):
        """Toggle between two plots for comparison (currently disabled)."""
        pass

    def stop_blink(self, event):
        """Stop the plot blinking animation."""
        self.stop_blink_var = True

    def display_imm_blink(self):
        """Display blinking animation between plots (currently disabled)."""
        pass

    def display_imm(self):
        """Display plot switching animation (currently disabled)."""
        pass

    def divide_instrument(self, inst_str, resolution):
        """
        Process instrument string based on resolution.
        
        Args:
            inst_str: Instrument string to process
            resolution: Resolution mode (HR/LR)
            
        Returns:
            str: Processed instrument string
        """
        inst_str = inst_str.replace(",", "&")
        inst_str = inst_str.replace(";", "|")
        instr_list = inst_str.split("|")
        for elem in instr_list:
            instr_parts = elem.split("&")
            if resolution == "HR":
                path_sub = (instr_parts[0] + "&" +
                            create_path_night(self.path_folder_targets, self.target_information.name,
                                              self.dropdown_rad_mode.get_value(), instr_parts[0])
                            if "H" in self.dropdown_resolution.get_value() else "")
            else:
                path_sub = (instr_parts[0] +
                            "&" +
                            str(Path(
                                self.path_folder_targets, self.target_information.name, "LR_Instruments",
                                self.dropdown_rad_mode.get_value(), instr_parts[0]))
                            ) if "L" in self.dropdown_resolution.get_value() else ""
            inst_str = inst_str.replace(instr_parts[0], path_sub)
        return inst_str

    def calc_model(self):
        """
        Calculate the model spectrum based on current parameters.
        
        Returns:
            tuple: (not_retrieval_obj, dict_params)
        """
        minwlen_hr = float(self.entry_min_wl_hr.get_value())
        maxwlen_hr = float(self.entry_max_wl_hr.get_value())
        range_min_pressure = int(self.entry_min_pressure.get_value())
        range_max_pressure = int(self.entry_max_pressure.get_value())
        pressure_layers = int(self.entry_pressure_layers.get_value())
        rad_mode = self.dropdown_rad_mode.get_value()
        if rad_mode == "Emission":
            folder_stellar_spc = str(Path(self.path_folder_targets, self.target_information.name, "Star_Spectrum"))
            path_pheonix = download_star_spectrum(self, self.target_information, folder_stellar_spc)
            if path_pheonix is None:
                return None, None
        
        # Initialize parameter dictionaries
        dict_params = dict()
        current_string_molec = self.textfield_molecs.get_text()
        list_condensed_current_plot = []
        best_fit_parameters = []
        mass_vector = []
        additional_opac = False
        
        # Process parameters
        for elem in self.param_array:
            if elem is not None and elem.is_considered():
                name, _, _ = elem.get_name()
                if name in ["k_opac", "k_cond", "lambda0_micron", "omega_scale_micron", "xi"]:
                    additional_opac = True
                dict_params[name] = elem.get_value()
                best_fit_parameters.append(elem.get_value())
                if elem.mass_molec is not None:
                    mass_vector.append(elem.mass_molec)
        
        # Setup paths
        table_output_file = None
        id_process = None
        username = os.environ.get("USER")
        tar_folder = str(Path(self.path_default, "GUIBRUSHR", "Files", "Temp_Files_GUI", username, "tar_unpack"))
        os.system("rm -rf " + tar_folder)
        os.system("mkdir -p " + tar_folder)
        
        # Setup model composition
        manual_model_composition = dict()
        manual_model_composition["H2"] = float(self.param_array[self.get_param_index("H2")].entry_value.get_value())
        manual_model_composition["He"] = float(self.param_array[self.get_param_index("He")].entry_value.get_value())
        
        # Handle H- inclusion
        if self.checkbox_include_h_m.get_value():
            manual_model_composition["H-"] = float(self.param_array[self.get_param_index("H-")].entry_value.get_value())
            manual_model_composition["H"] = float(self.param_array[self.get_param_index("H")].entry_value.get_value())
            manual_model_composition["e-"] = float(self.param_array[self.get_param_index("e-")].entry_value.get_value())
        
        # Process molecule list
        molec_list = self.textfield_molecs.get_text()
        # Process condensate list
        condensed_list = self.textfield_condensed_molecs.get_text()
        # Process hybrid list
        hybrid_list = self.textfield_hce.get_text()

        if molec_list == "":
            messagebox.showerror("NO MOLEC SELECTED", "NO MOLEC SELECTED\nPlease select at least one molecule.\n")
            return None, None

        for molec in molec_list.split("\n"):
            string_comp = molec.split(",")
            molec_formula = string_comp[0]
            name = string_comp[2]
            mass = float(string_comp[4])
            value_during_retrieval = float(string_comp[5])
            mass_vector.append(mass)
            dict_params[molec_formula] = value_during_retrieval
            best_fit_parameters.append(value_during_retrieval)
            manual_model_composition[molec_formula] = value_during_retrieval

        if condensed_list != "":
            for condensed in condensed_list.split("\n"):
                string_cond = condensed.split(",")
                name = string_cond[0]
                mass = float(string_cond[2])
                mass_vector.append(mass)
                value_during_retrieval = float(string_cond[3])
                dict_params[name] = value_during_retrieval
                best_fit_parameters.append(value_during_retrieval)
                list_condensed_current_plot.append(name)
                manual_model_composition[name] = value_during_retrieval

        if hybrid_list != "":
            for hybrid in hybrid_list.split("\n"):
                string_hybrid = hybrid.split(",")
                name = string_hybrid[0]
                value_during_retrieval = float(string_hybrid[2])
                dict_params[name] = value_during_retrieval
                best_fit_parameters.append(value_during_retrieval)
        
        # Create manual model object
        manual_model_obj = ManualModelObjClass(
            self.param_array,
            molec_list,
            condensed_list,
            hybrid_list,
            self.dropdown_resolution.get_value(),
            self.textfield_continuum_opacity.get_text(),
            self.target_information,
            self.dropdown_chemistry.get_value(),
            False,
            self.dropdown_t_p_profile.get_value(),
            table_output_file,
            self.path_results,
            id_process,
            self.path_results,
            mass_vector,
            float(self.entry_temp_min.get_value()),
            float(self.entry_temp_max.get_value()),
            float(self.entry_temp_step.get_value()),
            rad_mode,
            self.dropdown_retrieval_mode.get_value(),
            manual_model_composition,
            float(self.entry_rv_sampling.get_value()),
            stellar_spectrum=self.dropdown_stellar_spectrum.get_value(),
            path_targets=Path(self.path_folder_targets, self.target_information.name),
            additional_opac=additional_opac,
        )
        
        # Create or update model data
        if self.model_obj is None:
            load_new_opacities = True
            self.model_obj = ModelData(
                id_process=None,
                table_output_file=None,
                minwlen_hr=minwlen_hr,
                maxwlen_hr=maxwlen_hr,
                model_type="Manual",
                lbl_sampling_hr=int(self.entry_lbl_sampling.get_value()),
                lbl_sampling_lr=1,
                range_min=range_min_pressure,
                range_max=range_max_pressure,
                nlayers=pressure_layers,
                manual_model_obj=manual_model_obj,
                load_new_opacities=load_new_opacities,
                path_default=self.path_default
            )
        else:
            self.model_obj.initial_param_array = [None for _ in self.model_obj.params_list]
            if self.past_string is None:
                cond1 = True
            else:
                past_lines = {line.split(',')[0]: line for line in self.past_string.strip().split('\n') if line.strip()}
                curr_lines = {line.split(',')[0]: line for line in current_string_molec.strip().split('\n') if line.strip()}
                cond1 = False
                if past_lines.keys() != curr_lines.keys():
                    cond1 = True
                else:
                    for key in past_lines:
                        p = past_lines[key].split(',')
                        c = curr_lines[key].split(',')
                        if p[:3] != c[:3] or p[-1] != c[-1]:
                            cond1 = True
                            break
            cond2 = (
                    "Low" in self.dropdown_resolution.get_value()
                    and not self.model_obj.atmosphere.resolution_obj.low_resolution()
            )
            cond3 = (
                    "High" in self.dropdown_resolution.get_value()
                    and not self.model_obj.atmosphere.resolution_obj.high_resolution()
            )
            cond4 = self.last_min_wl != minwlen_hr or self.last_max_wl != maxwlen_hr
            cond5 = (self.last_min_pressure != range_min_pressure or
                     self.last_max_pressure != range_max_pressure or
                     self.last_pressure_layers != pressure_layers)
            cond6 = list_condensed_current_plot != self.list_condensed_last_plot
            cond7 = rad_mode != self.last_rad_mode
            cond9 = self.textfield_continuum_opacity.get_text() != self.last_continuum_opacity
            cond8 = False
            for molec_x in ["H2", "He", "H-", "H", "e-"]:
                current_rayleigh = self.param_array[self.get_param_index(molec_x)].get_rayleigh_value()
                last_rayleigh = self.dict_rayleigh_base[molec_x]
                cond8 = cond8 or (current_rayleigh != last_rayleigh)
                self.dict_rayleigh_base[molec_x] = current_rayleigh
            if cond1 or cond2 or cond3 or cond4 or cond5 or cond6 or cond7 or cond9:
                print("Generation of new atmosphere obj")
                load_new_opacities = True
                manual_model_obj.rad_mode = rad_mode
                manual_model_obj.path_targets = Path(self.path_folder_targets, self.target_information.name)
            else:
                load_new_opacities = False
            self.model_obj.populate_from_manual_model(
                manual_model_obj, load_new_opacities,
                None, None, minwlen_hr, maxwlen_hr,
                range_min_pressure, range_max_pressure, pressure_layers
            )

        self.last_rad_mode = rad_mode
        self.past_string = current_string_molec
        self.list_condensed_last_plot = list_condensed_current_plot
        self.last_continuum_opacity = self.textfield_continuum_opacity.get_text()
        param_full = self.model_obj.create_param_full(best_fit_parameters)
        _, _, not_retrieval_obj = self.model_obj.lh_function_gib(param_full)
        self.last_min_wl = minwlen_hr
        self.last_max_wl = maxwlen_hr
        self.last_min_pressure = range_min_pressure
        self.last_max_pressure = range_max_pressure
        self.last_pressure_layers = pressure_layers
        return not_retrieval_obj, dict_params

    def save_model(self):
        """
        Save the current model results and plots.
        Creates necessary directories and saves data in various formats.
        """
        if self.current_plot_for_saving is None:
            MyFigure(title="Error in saving model", message="No model to save")
            return
            
        try:
            # Create destination directory
            destination = str(Path(
                self.path_folder_targets, self.dropdown_target.get_value(), "Models",
                self.dropdown_rad_mode.get_value(), self.textfield_model_name.get_text()
            ))
            os.system("mkdir -p " + destination)
            
            # Copy plot
            os.system(
                "cp "
                + self.current_plot_for_saving
                + " "
                + str((Path(destination, "plot.png"))),
            )
            
            # Create YAML dictionary with model parameters
            yaml_dict = {
                'name': self.target_information.name,
                'radius_jup': float(self.target_information.radius),
                'mass_jup': float(self.target_information.mass),
                'gravity_cm_s2': float(self.target_information.gravity),
                't_eq_K': float(self.target_information.equilibrium_temperature),
                'stellar_radius_sun': float(self.target_information.stellar_radius),
                'stellar_mass_sun': float(self.target_information.stellar_mass),
                'stellar_teff_K': float(self.target_information.stellar_effective_temperature),
                'p0_log10_bar': float(self.target_information.pressure_at_layer_0),
                'hjd0_days': float(self.target_information.hjd0),
                'period_days': float(self.target_information.period),
                'kp_km_s': float(self.target_information.kp),
                'v_system_km_s': float(self.target_information.systemic_velocity),
                'Limit_Phase_t14': float(self.target_information.limit_phase_t14),
                'Limit_Phase_t12': float(self.target_information.limit_phase_t12),
                'Limit_Phase_t23': float(self.target_information.limit_phase_t23),
                'ecc': float(self.target_information.eccentricity),
                'periastron_argument': float(self.target_information.argument_of_periastron),
                "resolution": self.dropdown_resolution.get_value(),
                "rad_mode": self.dropdown_rad_mode.get_value(),
                "chemistry": self.dropdown_chemistry.get_value(),
                "t_p_profile": self.dropdown_t_p_profile.get_value(),
                "scattering": int(self.check_scattering.get_value()),
                "continuum_op": self.textfield_continuum_opacity.get_text(),
                "temp_min_pyrat": float(self.entry_temp_min.get_value()),
                "temp_max_pyrat": float(self.entry_temp_max.get_value()),
                "temp_step": float(self.entry_temp_step.get_value()),
                "retrieval_mode": self.dropdown_retrieval_mode.get_value(),
                "min_pressure": float(self.entry_min_pressure.get_value()),
                "max_pressure": float(self.entry_max_pressure.get_value()),
                "nlayers": int(self.entry_pressure_layers.get_value()),
                "lbl": int(self.entry_lbl_sampling.get_value()),
                "min_wl_hr": float(self.entry_min_wl_hr.get_value()),
                "max_wl_hr": float(self.entry_max_wl_hr.get_value())
            }
            
            # Add plot parameters
            yaml_dict = yaml_dict | self.dict_params_plot
            
            # Save data
            manual_model_data_dictionary = {
                "wl": self.wl,
                "model": self.model,
                "yaml_dict": yaml_dict
            }
            with open(Path(destination, "data.pkl"), "wb") as fo:
                pickle.dump(manual_model_data_dictionary, fo)
            
            # Save wavelength data
            hdu = fits.PrimaryHDU(self.wl)
            hdul = fits.HDUList([hdu])
            hdul.writeto(str(Path(destination, 'wavelength.fits')), overwrite=True)
            
            # Save transit depth data
            hdu = fits.PrimaryHDU(self.model)
            hdul = fits.HDUList([hdu])
            hdul.writeto(str(Path(destination, get_depth_filename(self.dropdown_rad_mode.get_value()))), overwrite=True)
            
            # Save YAML info
            with open(str(Path(destination, "info.yaml")), 'w') as f:
                yaml.dump(yaml_dict, f, sort_keys=False)
                
            MyFigure(title="Model saved", message="Model saved in " + destination)
            
        except Exception as e:
            MyFigure(title="Error in saving model", message=str(e) + "\n" + str(traceback.format_exc()))
            print(str(e) + "\n" + str(traceback.format_exc()))

    def modify_target_list(self, target_list):
        """
        Update the target dropdown list.
        
        Args:
            target_list: List of available targets
        """
        self.dropdown_target.clean_widget()
        self.dropdown_target = MyDropdown(
            self.row1, 0, 0, target_list, "Target:",
            color=self.color, columnspan=2
        )
        self.dropdown_target.chosen_var.trace("w", lambda *args: self.update_target())

    def include_h_m(self):
        """
        Handle H- inclusion in the model.
        Updates continuum opacity and parameter states based on H- checkbox.
        """
        if self.checkbox_include_h_m.get_value():
            self.textfield_continuum_opacity.insert_text("H2-H2,H2-He,H-")
            self.param_array[self.get_param_index("H-")].parameter_status("normal")
            self.param_array[self.get_param_index("H")].parameter_status("normal")
            self.param_array[self.get_param_index("e-")].parameter_status("normal")
        else:
            self.textfield_continuum_opacity.insert_text("H2-H2,H2-He")
            self.param_array[self.get_param_index("H-")].parameter_status("disabled")
            self.param_array[self.get_param_index("H")].parameter_status("disabled")
            self.param_array[self.get_param_index("e-")].parameter_status("disabled")
