import socket
from pathlib import Path

import psutil
from PyQt6.QtCore import QThread, pyqtSignal, Qt, pyqtSlot
from PyQt6.QtWidgets import (
    QApplication, QWidget, QVBoxLayout, QHBoxLayout, QLabel, QLineEdit, QPushButton, QFileDialog, QSpinBox, QComboBox,
    QGroupBox, QCheckBox, QProgressBar, QPlainTextEdit, QDoubleSpinBox, QMessageBox, QListWidget, QListWidgetItem,
    QTabWidget
)

from src.api.server_proxy import RenderServerProxy
from src.engines.engine_manager import EngineManager
from src.ui.engine_help_window import EngineHelpViewer
from src.utilities.misc_helper import COMMON_RESOLUTIONS, COMMON_FRAME_RATES
from src.utilities.zeroconf_server import ZeroconfServer


class NewRenderJobForm(QWidget):
    def __init__(self, project_path=None):
        super().__init__()
        self.resolution_options_list = None
        self.resolution_x_input = None
        self.resolution_y_input = None
        self.fps_options_list = None
        self.fps_input = None
        self.engine_group = None
        self.notes_group = None
        self.output_settings_group = None
        self.project_path = project_path

        # UI
        self.project_group = None
        self.load_file_group = None
        self.current_engine_options = None
        self.file_format_combo = None
        self.engine_options_layout = None
        self.cameras_list = None
        self.cameras_group = None
        self.engine_version_combo = None
        self.worker_thread = None
        self.msg_box = None
        self.engine_help_viewer = None
        self.raw_args = None
        self.submit_progress_label = None
        self.submit_progress = None
        self.engine_type = None
        self.process_label = None
        self.process_progress_bar = None
        self.splitjobs_same_os = None
        self.enable_splitjobs = None
        self.server_input = None
        self.submit_button = None
        self.notes_input = None
        self.priority_input = None
        self.end_frame_input = None
        self.start_frame_input = None
        self.job_name_input = None
        self.scene_file_input = None
        self.scene_file_browse_button = None
        self.tabs = None

        # Job / Server Data
        self.server_proxy = RenderServerProxy(socket.gethostname())
        self.project_info = None
        self.installed_engines = {}
        self.preferred_engine = None

        # Setup
        self.setWindowTitle("New Job")
        self.setup_ui()
        self.setup_project()

        self.show()

    def setup_ui(self):
        # Main widget layout
        main_layout = QVBoxLayout(self)

        # Tabs
        self.tabs = QTabWidget()

        # ==================== Loading Section (outside tabs) ====================
        self.load_file_group = QGroupBox("Loading")
        load_file_layout = QVBoxLayout(self.load_file_group)

        progress_layout = QHBoxLayout()
        self.process_label = QLabel("Processing")
        self.process_progress_bar = QProgressBar()
        self.process_progress_bar.setMinimum(0)
        self.process_progress_bar.setMaximum(0)  # Indeterminate
        progress_layout.addWidget(self.process_label)
        progress_layout.addWidget(self.process_progress_bar)
        load_file_layout.addLayout(progress_layout)

        # Scene File
        job_overview_group = QGroupBox("Project File")
        file_group_layout = QVBoxLayout(job_overview_group)

        # Job Name
        job_name_layout = QHBoxLayout()
        job_name_layout.addWidget(QLabel("Job name:"))
        self.job_name_input = QLineEdit()
        job_name_layout.addWidget(self.job_name_input)
        self.engine_type = QComboBox()
        job_name_layout.addWidget(self.engine_type)
        file_group_layout.addLayout(job_name_layout)

        # Job File
        scene_file_picker_layout = QHBoxLayout()
        scene_file_picker_layout.addWidget(QLabel("File:"))
        self.scene_file_input = QLineEdit()
        self.scene_file_input.setText(self.project_path)
        self.scene_file_browse_button = QPushButton("Browse...")
        self.scene_file_browse_button.clicked.connect(self.browse_scene_file)
        scene_file_picker_layout.addWidget(self.scene_file_input)
        scene_file_picker_layout.addWidget(self.scene_file_browse_button)
        file_group_layout.addLayout(scene_file_picker_layout)

        main_layout.addWidget(job_overview_group)

        main_layout.addWidget(self.load_file_group)
        main_layout.addWidget(self.tabs)

        # ==================== Tab 1: Job Settings ====================
        self.project_group = QWidget()
        project_layout = QVBoxLayout(self.project_group)  # Fixed: proper parent

        # Server / Hostname
        server_list_layout = QHBoxLayout()
        server_list_layout.addWidget(QLabel("Render Target:"))
        self.server_input = QComboBox()
        server_list_layout.addWidget(self.server_input)
        project_layout.addLayout(server_list_layout)

        # Priority
        priority_layout = QHBoxLayout()
        priority_layout.addWidget(QLabel("Priority:"))
        self.priority_input = QComboBox()
        self.priority_input.addItems(["High", "Medium", "Low"])
        self.priority_input.setCurrentIndex(1)
        priority_layout.addWidget(self.priority_input)
        project_layout.addLayout(priority_layout)

        # Split Jobs Options
        self.enable_splitjobs = QCheckBox("Automatically split render across multiple servers")
        project_layout.addWidget(self.enable_splitjobs)

        self.splitjobs_same_os = QCheckBox("Only render on same OS")
        project_layout.addWidget(self.splitjobs_same_os)

        project_layout.addStretch()  # Push everything up

        # ==================== Tab 2: Output Settings ====================
        self.output_settings_group = QWidget()
        output_settings_layout = QVBoxLayout(self.output_settings_group)

        # File Format
        format_group = QGroupBox("Format / Range")
        output_settings_layout.addWidget(format_group)
        format_group_layout = QVBoxLayout()
        format_group.setLayout(format_group_layout)
        file_format_layout = QHBoxLayout()
        file_format_layout.addWidget(QLabel("Format:"))
        self.file_format_combo = QComboBox()
        self.file_format_combo.setFixedWidth(200)
        file_format_layout.addWidget(self.file_format_combo)
        file_format_layout.addStretch()
        format_group_layout.addLayout(file_format_layout)

        # Frame Range
        frame_range_layout = QHBoxLayout()
        frame_range_layout.addWidget(QLabel("Frames:"))
        self.start_frame_input = QSpinBox()
        self.start_frame_input.setRange(1, 99999)
        self.start_frame_input.setFixedWidth(80)
        self.end_frame_input = QSpinBox()
        self.end_frame_input.setRange(1, 99999)
        self.end_frame_input.setFixedWidth(80)
        frame_range_layout.addWidget(self.start_frame_input)
        frame_range_layout.addWidget(QLabel("to"))
        frame_range_layout.addWidget(self.end_frame_input)
        frame_range_layout.addStretch()
        format_group_layout.addLayout(frame_range_layout)

        # --- Resolution & FPS Group ---
        resolution_group = QGroupBox("Resolution / Frame Rate")
        output_settings_layout.addWidget(resolution_group)
        resolution_group_layout = QVBoxLayout()
        resolution_group.setLayout(resolution_group_layout)

        # Resolution
        resolution_layout = QHBoxLayout()
        self.resolution_options_list = QComboBox()
        self.resolution_options_list.setFixedWidth(200)
        self.resolution_options_list.addItem("Original Size")
        for res in COMMON_RESOLUTIONS:
            self.resolution_options_list.addItem(res)
        self.resolution_options_list.currentIndexChanged.connect(self._resolution_preset_changed)
        resolution_layout.addWidget(self.resolution_options_list)
        resolution_group_layout.addLayout(resolution_layout)
        self.resolution_x_input = QSpinBox()
        self.resolution_x_input.setRange(1, 9999)
        self.resolution_x_input.setValue(1920)
        self.resolution_x_input.setFixedWidth(80)
        resolution_layout.addWidget(self.resolution_x_input)
        self.resolution_y_input = QSpinBox()
        self.resolution_y_input.setRange(1, 9999)
        self.resolution_y_input.setValue(1080)
        self.resolution_y_input.setFixedWidth(80)
        resolution_layout.addWidget(QLabel("x"))
        resolution_layout.addWidget(self.resolution_y_input)
        resolution_layout.addStretch()

        fps_layout = QHBoxLayout()
        self.fps_options_list = QComboBox()
        self.fps_options_list.setFixedWidth(200)
        self.fps_options_list.addItem("Original FPS")
        for fps_option in COMMON_FRAME_RATES:
            self.fps_options_list.addItem(fps_option)
        self.fps_options_list.currentIndexChanged.connect(self._fps_preset_changed)
        fps_layout.addWidget(self.fps_options_list)

        self.fps_input = QDoubleSpinBox()
        self.fps_input.setDecimals(3)
        self.fps_input.setRange(1.0, 999.0)
        self.fps_input.setValue(23.976)
        self.fps_input.setFixedWidth(80)

        fps_layout.addWidget(self.fps_input)
        fps_layout.addWidget(QLabel("fps"))
        fps_layout.addStretch()
        resolution_group_layout.addLayout(fps_layout)

        output_settings_layout.addStretch()

        # ==================== Tab 3: Engine Settings ====================
        self.engine_group = QWidget()
        engine_group_layout = QVBoxLayout(self.engine_group)

        engine_layout = QHBoxLayout()
        engine_layout.addWidget(QLabel("Engine Version:"))
        self.engine_version_combo = QComboBox()
        self.engine_version_combo.addItem('latest')
        engine_layout.addWidget(self.engine_version_combo)
        engine_group_layout.addLayout(engine_layout)

        # Dynamic engine options
        self.engine_options_layout = QVBoxLayout()
        engine_group_layout.addLayout(self.engine_options_layout)

        # Raw Args
        raw_args_layout = QHBoxLayout()
        raw_args_layout.addWidget(QLabel("Raw Args:"))
        self.raw_args = QLineEdit()
        raw_args_layout.addWidget(self.raw_args)
        args_help_button = QPushButton("?")
        args_help_button.clicked.connect(self.args_help_button_clicked)
        raw_args_layout.addWidget(args_help_button)
        engine_group_layout.addLayout(raw_args_layout)
        engine_group_layout.addStretch()

        # ==================== Tab 4: Cameras ====================
        self.cameras_group = QWidget()
        cameras_layout = QVBoxLayout(self.cameras_group)
        self.cameras_list = QListWidget()
        self.cameras_list.itemChanged.connect(self.update_job_count)
        cameras_layout.addWidget(self.cameras_list)

        # ==================== Tab 5: Misc / Notes ====================
        self.notes_group = QWidget()
        notes_layout = QVBoxLayout(self.notes_group)
        self.notes_input = QPlainTextEdit()
        notes_layout.addWidget(self.notes_input)

        # == Create Tabs
        self.tabs.addTab(self.project_group, "Job Settings")
        self.tabs.addTab(self.output_settings_group, "Output Settings")
        self.tabs.addTab(self.engine_group, "Engine Settings")
        self.tabs.addTab(self.cameras_group, "Cameras")
        self.tabs.addTab(self.notes_group, "Notes")
        self.update_server_list()

        index = self.tabs.indexOf(self.cameras_group)
        if index != -1:
            self.tabs.setTabEnabled(index, False)

        # ==================== Submit Section (outside tabs) ====================
        self.submit_button = QPushButton("Submit Job")
        self.submit_button.clicked.connect(self.submit_job)
        main_layout.addWidget(self.submit_button)

        self.submit_progress = QProgressBar()
        self.submit_progress.setMinimum(0)
        self.submit_progress.setMaximum(0)
        self.submit_progress.setHidden(True)
        main_layout.addWidget(self.submit_progress)

        self.submit_progress_label = QLabel("Submitting...")
        self.submit_progress_label.setHidden(True)
        main_layout.addWidget(self.submit_progress_label)

        # Initial engine state
        self.toggle_engine_enablement(False)
        self.tabs.setCurrentIndex(0)

    def update_job_count(self, changed_item=None):
        checked = 1
        if self.cameras_group.enabled:
            checked = 0
            total = self.cameras_list.count()

            for i in range(total):
                item = self.cameras_list.item(i)
                if item.checkState() == Qt.CheckState.Checked:
                    checked += 1

        message = f"Submit {checked} Jobs" if checked > 1 else "Submit Job"
        self.submit_button.setText(message)
        self.submit_button.setEnabled(bool(checked))

    def _resolution_preset_changed(self, index):
        selected_res = COMMON_RESOLUTIONS.get(self.resolution_options_list.currentText())
        if selected_res:
            self.resolution_x_input.setValue(selected_res[0])
            self.resolution_y_input.setValue(selected_res[1])
        elif index == 0:
            self.resolution_x_input.setValue(self.project_info.get('resolution_x'))
            self.resolution_y_input.setValue(self.project_info.get('resolution_y'))

    def _fps_preset_changed(self, index):
        selected_fps = COMMON_FRAME_RATES.get(self.fps_options_list.currentText())
        if selected_fps:
            self.fps_input.setValue(selected_fps)
        elif index == 0:
            self.fps_input.setValue(self.project_info.get('fps'))

    def engine_changed(self):
        # load the version numbers
        current_engine = self.engine_type.currentText().lower() or self.engine_type.itemText(0)
        self.engine_version_combo.clear()
        self.engine_version_combo.addItem('latest')
        self.file_format_combo.clear()
        if current_engine:
            engine_info = self.server_proxy.get_engine_info(current_engine, 'full', timeout=10)
            self.current_engine_options = engine_info.get('options', [])
            if not engine_info:
                raise FileNotFoundError(f"Cannot get information about engine '{current_engine}'")
            engine_vers = [v['version'] for v in engine_info['versions']]
            self.engine_version_combo.addItems(engine_vers)
            self.file_format_combo.addItems(engine_info.get('supported_export_formats'))

    def update_server_list(self):
        clients = ZeroconfServer.found_hostnames()
        self.server_input.clear()
        self.server_input.addItems(clients)

    def browse_scene_file(self):
        file_name, _ = QFileDialog.getOpenFileName(self, "Select Scene File")
        if file_name:
            self.scene_file_input.setText(file_name)
            self.setup_project()

    def setup_project(self):
        # UI stuff on main thread
        self.process_progress_bar.setHidden(False)
        self.process_label.setHidden(False)
        self.toggle_engine_enablement(False)

        output_name = Path(self.scene_file_input.text()).stem.replace(' ', '_')
        self.job_name_input.setText(output_name)
        file_name = self.scene_file_input.text()

        # setup bg worker
        self.worker_thread = GetProjectInfoWorker(window=self, project_path=file_name)
        self.worker_thread.message_signal.connect(self.post_get_project_info_update)
        self.worker_thread.error_signal.connect(self.show_error_message)
        self.worker_thread.start()

    def browse_output_path(self):
        directory = QFileDialog.getExistingDirectory(self, "Select Output Directory")
        if directory:
            self.job_name_input.setText(directory)

    def args_help_button_clicked(self):
        url = (f'http://{self.server_proxy.hostname}:{self.server_proxy.port}/api/engine/'
               f'{self.engine_type.currentText()}/help')
        self.engine_help_viewer = EngineHelpViewer(url)
        self.engine_help_viewer.show()

    def show_error_message(self, message):
        msg = QMessageBox(self)
        msg.setIcon(QMessageBox.Icon.Critical)
        msg.setWindowTitle("Error")
        msg.setText(message)
        msg.exec()

# -------- Update --------

    def post_get_project_info_update(self):
        """Called by the GetProjectInfoWorker - Do not call directly."""
        try:

            self.engine_type.addItems(self.installed_engines.keys())
            self.engine_type.setCurrentText(self.preferred_engine)
            self.engine_changed()

            # Set the best engine we can find
            input_path = self.scene_file_input.text()
            engine = EngineManager.engine_class_for_project_path(input_path)

            engine_index = self.engine_type.findText(engine.name().lower())
            if engine_index >= 0:
                self.engine_type.setCurrentIndex(engine_index)
            else:
                self.engine_type.setCurrentIndex(0)  #todo: find out why we don't have engine info yet
                # not ideal but if we don't have the engine info we have to pick something

            # cleanup progress UI
            self.load_file_group.setHidden(True)
            self.toggle_engine_enablement(True)

            # Load scene data
            self.start_frame_input.setValue(self.project_info.get('frame_start'))
            self.end_frame_input.setValue(self.project_info.get('frame_end'))
            self.resolution_x_input.setValue(self.project_info.get('resolution_x'))
            self.resolution_y_input.setValue(self.project_info.get('resolution_y'))
            self.fps_input.setValue(self.project_info.get('fps'))

            # Cameras
            self.cameras_list.clear()
            index = self.tabs.indexOf(self.cameras_group)
            if self.project_info.get('cameras'):
                self.tabs.setTabEnabled(index, True)
                found_active = False
                for camera in self.project_info['cameras']:
                    # create the list items and make them checkable
                    item = QListWidgetItem(f"{camera['name']} - {camera['lens']}mm")
                    item.setFlags(item.flags() | Qt.ItemFlag.ItemIsUserCheckable)
                    is_checked = camera['is_active'] or len(self.project_info['cameras']) == 1
                    found_active = found_active or is_checked
                    item.setCheckState(Qt.CheckState.Checked if is_checked else Qt.CheckState.Unchecked)
                    self.cameras_list.addItem(item)
                if not found_active:
                    self.cameras_list.item(0).setCheckState(Qt.CheckState.Checked)
            else:
                self.tabs.setTabEnabled(index, False)
            self.update_job_count()

            # Dynamic Engine Options
            clear_layout(self.engine_options_layout)  # clear old options
            # dynamically populate option list
            for option in self.current_engine_options:
                h_layout = QHBoxLayout()
                label = QLabel(option['name'].replace('_', ' ').capitalize() + ':')
                h_layout.addWidget(label)
                if option.get('options'):
                    combo_box = QComboBox()
                    for opt in option['options']:
                        combo_box.addItem(opt)
                    h_layout.addWidget(combo_box)
                else:
                    text_box = QLineEdit()
                    h_layout.addWidget(text_box)
                self.engine_options_layout.addLayout(h_layout)
        except AttributeError:
            pass

    def toggle_engine_enablement(self, enabled=False):
        """Toggle on/off all the render settings"""
        indexes = [self.tabs.indexOf(self.project_group),
                   self.tabs.indexOf(self.output_settings_group),
                   self.tabs.indexOf(self.engine_group),
                   self.tabs.indexOf(self.cameras_group),
                   self.tabs.indexOf(self.notes_group)]
        for idx in indexes:
            self.tabs.setTabEnabled(idx, enabled)
        self.submit_button.setEnabled(enabled)

    def after_job_submission(self, error_string):

        # UI cleanup
        self.submit_progress.setMaximum(0)
        self.submit_button.setHidden(False)
        self.submit_progress.setHidden(True)
        self.submit_progress_label.setHidden(True)
        self.process_progress_bar.setHidden(True)
        self.process_label.setHidden(True)
        self.toggle_engine_enablement(True)

        self.msg_box = QMessageBox()
        if not error_string:
            self.msg_box.setStandardButtons(QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No)
            self.msg_box.setIcon(QMessageBox.Icon.Information)
            self.msg_box.setText("Job successfully submitted to server. Submit another?")
            self.msg_box.setWindowTitle("Success")
            x = self.msg_box.exec()
            if x == QMessageBox.StandardButton.No:
                self.close()
        else:
            self.msg_box.setStandardButtons(QMessageBox.StandardButton.Ok)
            self.msg_box.setIcon(QMessageBox.Icon.Critical)
            self.msg_box.setText(error_string)
            self.msg_box.setWindowTitle("Error")
            self.msg_box.exec()

# -------- Submit Job Calls --------

    def submit_job(self):

        # Pre-worker UI
        self.submit_progress.setHidden(False)
        self.submit_progress_label.setHidden(False)
        self.submit_button.setHidden(True)
        self.submit_progress.setMaximum(0)

        # submit job in background thread
        self.worker_thread = SubmitWorker(window=self)
        self.worker_thread.update_ui_signal.connect(self.update_submit_progress)
        self.worker_thread.message_signal.connect(self.after_job_submission)
        self.worker_thread.start()

    @pyqtSlot(str, str)
    def update_submit_progress(self, hostname, percent):
        # Update the UI here. This slot will be executed in the main thread
        self.submit_progress_label.setText(f"Transferring to {hostname} - {percent}%")
        self.submit_progress.setMaximum(100)
        self.submit_progress.setValue(int(percent))


class SubmitWorker(QThread):
    """Worker class called to submit all the jobs to the server and update the UI accordingly"""

    message_signal = pyqtSignal(str)
    update_ui_signal = pyqtSignal(str, str)

    def __init__(self, window):
        super().__init__()
        self.window = window

    def run(self):
        def create_callback(encoder):
            encoder_len = encoder.len

            def callback(monitor):
                percent = f"{monitor.bytes_read / encoder_len * 100:.0f}"
                self.update_ui_signal.emit(hostname, percent)
            return callback

        try:
            hostname = self.window.server_input.currentText()
            resolution = (self.window.resolution_x_input.text(), self.window.resolution_y_input.text())
            job_json = {'owner': psutil.Process().username() + '@' + socket.gethostname(),
                        'engine_name': self.window.engine_type.currentText().lower(),
                        'engine_version': self.window.engine_version_combo.currentText(),
                        'args': {'raw': self.window.raw_args.text(),
                                 'export_format': self.window.file_format_combo.currentText(),
                                 'resolution': resolution,
                                 'fps': self.window.fps_input.text(),},
                        'output_path': self.window.job_name_input.text(),
                        'start_frame': self.window.start_frame_input.value(),
                        'end_frame': self.window.end_frame_input.value(),
                        'priority': self.window.priority_input.currentIndex() + 1,
                        'notes': self.window.notes_input.toPlainText(),
                        'enable_split_jobs': self.window.enable_splitjobs.isChecked(),
                        'split_jobs_same_os': self.window.splitjobs_same_os.isChecked(),
                        'name': self.window.job_name_input.text()}

            # get the dynamic args
            for i in range(self.window.engine_options_layout.count()):
                item = self.window.engine_options_layout.itemAt(i)
                layout = item.layout()  # get the layout
                for x in range(layout.count()):
                    z = layout.itemAt(x)
                    widget = z.widget()
                    if isinstance(widget, QComboBox):
                        job_json['args'][self.window.current_engine_options[i]['name']] = widget.currentText()
                    elif isinstance(widget, QLineEdit):
                        job_json['args'][self.window.current_engine_options[i]['name']] = widget.text()

            # determine if any cameras are checked
            selected_cameras = []
            if self.window.cameras_list.count() and not self.window.cameras_group.isHidden():
                for index in range(self.window.cameras_list.count()):
                    item = self.window.cameras_list.item(index)
                    if item.checkState() == Qt.CheckState.Checked:
                        selected_cameras.append(item.text().rsplit('-', 1)[0].strip())  # cleanup to just camera name

            # process cameras into nested format
            input_path = self.window.scene_file_input.text()
            if selected_cameras and self.window.cameras_list.count() > 1:
                children_jobs = []
                for cam in selected_cameras:
                    child_job_data = dict()
                    child_job_data['args'] = {}
                    child_job_data['args']['camera'] = cam
                    child_job_data['name'] = job_json['name'].replace(' ', '-') + "_" + cam.replace(' ', '')
                    child_job_data['output_path'] = child_job_data['name']
                    children_jobs.append(child_job_data)
                job_json['child_jobs'] = children_jobs

            # presubmission tasks - use local installs
            engine_class = EngineManager.engine_class_with_name(self.window.engine_type.currentText().lower())
            latest_engine = EngineManager.get_latest_engine_instance(engine_class)
            input_path = Path(latest_engine.perform_presubmission_tasks(input_path))
            # submit
            err_msg = ""
            result = self.window.server_proxy.post_job_to_server(file_path=input_path, job_data=job_json,
                                                          callback=create_callback)
            if not (result and result.ok):
                err_msg = f"Error posting job to server: {result.text}"

            self.message_signal.emit(err_msg)

        except Exception as e:
            self.message_signal.emit(str(e))


class GetProjectInfoWorker(QThread):
    """Worker class called to retrieve information about a project file on a background thread and update the UI"""

    message_signal = pyqtSignal()
    error_signal = pyqtSignal(str)

    def __init__(self, window, project_path):
        super().__init__()
        self.window = window
        self.project_path = project_path

    def run(self):
        try:
            # get the engine info and add them all to the ui
            self.window.installed_engines = self.window.server_proxy.get_installed_engines()
            # select the best engine for the file type
            self.window.preferred_engine = self.window.server_proxy.get_engine_for_filename(self.project_path)

            # this should be the only time we use a local engine instead of using the proxy besides submitting
            engine_class = EngineManager.engine_class_for_project_path(self.project_path)
            engine = EngineManager.get_latest_engine_instance(engine_class)
            self.window.project_info = engine.get_project_info(self.project_path)
            self.message_signal.emit()
        except Exception as e:
            self.error_signal.emit(str(e))


def clear_layout(layout):
    if layout is not None:
        # Go through the layout's items in reverse order
        for i in reversed(range(layout.count())):
            # Take the item at the current position
            item = layout.takeAt(i)

            # Check if the item is a widget
            if item.widget():
                # Remove the widget and delete it
                widget_to_remove = item.widget()
                widget_to_remove.setParent(None)
                widget_to_remove.deleteLater()
            elif item.layout():
                # If the item is a sub-layout, clear its contents recursively
                clear_layout(item.layout())
                # Then delete the layout
                item.layout().deleteLater()

# Run the application
if __name__ == '__main__':
    app = QApplication([])
    window = NewRenderJobForm()
    app.exec()
