# -*- coding: utf-8 -*-
"""
nessus file analyzer (NFA) by LimberDuck (pronounced *ˈlɪm.bɚ dʌk*) is a GUI
tool which enables you to parse multiple nessus files containing the results
of scans performed by using Nessus by (C) Tenable, Inc. and exports parsed
data to a Microsoft Excel Workbook for effortless analysis.
Copyright (C) 2019 Damian Krawczyk

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <https://www.gnu.org/licenses/>.
"""

import datetime
import glob
import os
import time
import traceback
import xlsxwriter
from PyQt5.QtGui import *
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
import sys
import subprocess
import platform
import nessus_file_reader as nfr
import nessus_file_analyzer as nfa
import urllib.parse
import fnmatch
import zipfile
from nessus_file_analyzer.dialogs import about
from nessus_file_analyzer.dialogs import update_check
from nessus_file_analyzer.dialogs import url_open
from nessus_file_analyzer import utilities, __about__
import requests
from packaging import version
from nessus_file_analyzer.plugins import discover_plugins


class FlowLayout(QLayout):
    """
    A layout that arranges widgets in a flow (wrapping to next line when needed).
    Based on Qt's Flow Layout example.
    """
    def __init__(self, parent=None, margin=0, spacing=-1):
        super(FlowLayout, self).__init__(parent)
        self.setContentsMargins(margin, margin, margin, margin)
        self.setSpacing(spacing)
        self.itemList = []

    def __del__(self):
        item = self.takeAt(0)
        while item:
            item = self.takeAt(0)

    def addItem(self, item):
        self.itemList.append(item)

    def count(self):
        return len(self.itemList)

    def itemAt(self, index):
        if 0 <= index < len(self.itemList):
            return self.itemList[index]
        return None

    def takeAt(self, index):
        if 0 <= index < len(self.itemList):
            return self.itemList.pop(index)
        return None

    def expandingDirections(self):
        return Qt.Orientations(Qt.Orientation(0))

    def hasHeightForWidth(self):
        return True

    def heightForWidth(self, width):
        height = self.doLayout(QRect(0, 0, width, 0), True)
        return height

    def setGeometry(self, rect):
        super(FlowLayout, self).setGeometry(rect)
        self.doLayout(rect, False)

    def sizeHint(self):
        return self.minimumSize()

    def minimumSize(self):
        size = QSize()
        for item in self.itemList:
            size = size.expandedTo(item.minimumSize())
        margin, _, _, _ = self.getContentsMargins()
        size += QSize(2 * margin, 2 * margin)
        return size

    def doLayout(self, rect, testOnly):
        # Get the contents margins and adjust the effective rect
        left, top, right, bottom = self.getContentsMargins()
        effectiveRect = rect.adjusted(left, top, -right, -bottom)

        x = effectiveRect.x()
        y = effectiveRect.y()
        lineHeight = 0
        spacing = self.spacing()

        for item in self.itemList:
            nextX = x + item.sizeHint().width() + spacing
            if nextX - spacing > effectiveRect.right() and lineHeight > 0:
                x = effectiveRect.x()
                y = y + lineHeight + spacing
                nextX = x + item.sizeHint().width() + spacing
                lineHeight = 0

            if not testOnly:
                item.setGeometry(QRect(QPoint(x, y), item.sizeHint()))

            x = nextX
            lineHeight = max(lineHeight, item.sizeHint().height())

        return y + lineHeight - rect.y() + bottom


class MainWindow(QMainWindow, nfa.Ui_MainWindow):

    def __init__(self, parent=None):
        super(MainWindow, self).__init__(parent)
        self.setupUi(self)

        self.__files_to_pars = []
        self.__parsing_settings = {}
        self.__target_directory = ""
        self.__target_directory_changed = False
        self.__file_analysis_counter = 0
        self.__suffix = ""
        self.update_parsing_settings("suffix", self.__suffix)
        self.__suffix_template = ""
        self.update_parsing_settings("suffix_template", self.__suffix_template)
        self.__target_file_name_prefix = "security_report"
        self.update_parsing_settings(
            "target_file_name_prefix", self.__target_file_name_prefix
        )

        # reports
        self.__report_scan_enabled = False
        self.update_parsing_settings("report_scan_enabled", self.__report_scan_enabled)
        self.__report_host_enabled = False
        self.update_parsing_settings("report_host_enabled", self.__report_host_enabled)
        self.__report_vulnerabilities_enabled = False
        self.update_parsing_settings(
            "report_vulnerabilities_enabled", self.__report_vulnerabilities_enabled
        )
        self.__report_noncompliance_enabled = False
        self.update_parsing_settings(
            "report_noncompliance_enabled", self.__report_noncompliance_enabled
        )

        # reports settings
        self.__report_scan_setting_debug_data_enabled = False
        self.update_parsing_settings(
            "report_scan_debug_data_enabled",
            self.__report_scan_setting_debug_data_enabled,
        )
        self.__report_host_setting_debug_data_enabled = False
        self.update_parsing_settings(
            "report_host_debug_data_enabled",
            self.__report_host_setting_debug_data_enabled,
        )
        self.__report_vulnerabilities_setting_debug_data_enabled = False
        self.update_parsing_settings(
            "report_vulnerabilities_debug_data_enabled",
            self.__report_vulnerabilities_setting_debug_data_enabled,
        )
        self.__report_vulnerabilities_setting_none_filter_out = False
        self.update_parsing_settings(
            "report_vulnerabilities_none_filter_out",
            self.__report_vulnerabilities_setting_none_filter_out,
        )
        self.__report_vulnerabilities_setting_none_skip = False
        self.update_parsing_settings(
            "report_vulnerabilities_none_skip",
            self.__report_vulnerabilities_setting_none_skip,
        )
        self.__report_noncompliance_setting_debug_data_enabled = False
        self.update_parsing_settings(
            "report_noncompliance_debug_data_enabled",
            self.__report_noncompliance_enabled,
        )
        self.__report_set_source_directory_as_target_directory_enabled = False
        self.update_parsing_settings(
            "set_source_directory_as_target_directory_enabled",
            self.__report_set_source_directory_as_target_directory_enabled,
        )

        # plugins
        self.__plugins = {}
        self.__plugin_checkboxes = {}
        self.__plugin_instances = {}
        self.__plugin_setting_labels = {}  # Maps setting_key to human-readable label

        self.parsing_thread = ParsingThread(
            files_to_pars=self.__files_to_pars,
            target_directory=self.__target_directory,
            target_directory_changed=self.__target_directory_changed,
            parsing_settings=self.__parsing_settings,
        )

        self.actionOpen_file.triggered.connect(self.open_files)
        self.actionOpen_directory.triggered.connect(self.open_directory)
        self.actionExit.triggered.connect(self.exit_application)

        self.actionStart_analysis.triggered.connect(self.parsing_thread_start)
        self.actionChange_target_directory.triggered.connect(
            self.change_target_directory
        )
        self.actionOpen_target_directory.triggered.connect(self.open_target_directory)
        self.actionAbout.triggered.connect(self.open_dialog_about)
        self.actionCheck_for_Update.triggered.connect(self.open_dialog_update_check)
        self.actionCheck_Announcements.triggered.connect(self.check_announcements_button)
        self.actionDocumentation.triggered.connect(self.open_url_documentation)
        self.actionGitHub.triggered.connect(self.open_url_github)
        self.actionReleases.triggered.connect(self.open_url_github_releases)

        self.checkBox_report_scan.stateChanged.connect(self.report_scan_changed)
        self.checkBox_debug_data_scan.stateChanged.connect(
            self.report_scan_setting_debug_changed
        )
        self.checkBox_report_host.stateChanged.connect(self.report_host_changed)
        self.checkBox_debug_data_host.stateChanged.connect(
            self.report_host_setting_debug_changed
        )
        self.checkBox_report_vulnerabilities.stateChanged.connect(
            self.report_vulnerabilities_changed
        )
        self.checkBox_debug_data_vulnerabilities.stateChanged.connect(
            self.report_vulnerabilities_setting_debug_changed
        )
        self.checkBox_vulnerabilities_none_filter_out.stateChanged.connect(
            self.report_vulnerabilities_setting_none_filter_out_changed
        )
        self.checkBox_vulnerabilities_none_skip.stateChanged.connect(
            self.report_vulnerabilities_setting_none_skip_changed
        )
        self.checkBox_report_noncompliance.stateChanged.connect(
            self.report_noncompliance_changed
        )
        self.checkBox_debug_data_noncompliance.stateChanged.connect(
            self.report_noncompliance_setting_debug_changed
        )

        self.checkBox_set_source_directory_as_target_directory.stateChanged.connect(
            self.set_source_directory_as_target_directory_changed
        )
        self.checkBox_suffix_timestamp.stateChanged.connect(
            self.suffix_timestamp_changed
        )

        self.lineEdit_suffix_custom_value.setDisabled(True)
        self.checkBox_suffix_custom.stateChanged.connect(
            self.check_box_suffix_state_changed
        )
        self.lineEdit_suffix_custom_value.textChanged.connect(
            self.line_edit_suffix_custom_value_changed
        )
        # Match any character but \/:*?"<>|
        reg_ex = QRegExp('[^\\\\/:*?"<>|]+')
        line_edit_suffix_custom_value_validator = QRegExpValidator(
            reg_ex, self.lineEdit_suffix_custom_value
        )
        self.lineEdit_suffix_custom_value.setValidator(
            line_edit_suffix_custom_value_validator
        )

        self.pushButton_start.clicked.connect(self.parsing_thread_start)
        self.pushButton_target_dir_change.clicked.connect(self.change_target_directory)
        self.pushButton_target_dir_open.clicked.connect(self.open_target_directory)

        self.checkBox_suffix_timestamp.setChecked(True)
        self.groupBox_options_scan.setDisabled(True)
        self.groupBox_options_host.setDisabled(True)
        self.groupBox_options_vulnerabilities.setDisabled(True)
        self.groupBox_options_noncompliance.setDisabled(True)
        self.pushButton_start.setDisabled(True)
        self.actionStart_analysis.setDisabled(True)
        self.progressBar.setHidden(True)

        target_dir = os.path.expanduser('~')
        self.set_target_directory(target_dir)
        self.get_target_directory_from_file()

        files = sys.argv[1:]
        if len(files) > 0:
            self.list_of_files_to_pars(files)
            source_file_path = os.path.dirname(os.path.abspath(files[0]))
            self.set_target_directory(source_file_path)
            self.get_target_directory_from_file()

        self.progressBar.setRange(0, 10)

        self.print_log(
            "If you don't know how to use particular options "
            "hover mouse pointer on option for which you have any doubts to see tooltip. "
            "Hover mouse pointer here, to see tooltip for progress preview.",
            "red",
        )

        self.initialize_plugins()

        self.check_announcements()

        self.setAcceptDrops(True)

    def initialize_plugins(self):
        """
        Discover and initialize plugins, creating UI elements in the Advanced tab.
        """
        try:
            # Discover all installed plugins
            plugin_classes = discover_plugins()

            # Update label text based on number of plugins
            num_plugins = len(plugin_classes)
            if num_plugins == 0:
                self.label_advanced_reports.setText("There are no advanced reports available.")
                # No plugins installed, show the "no plugins" message
                self.label_no_plugins.setVisible(True)
                return
            elif num_plugins == 1:
                self.label_advanced_reports.setText("There is 1 advanced report available. Enable report to generate:")
            else:
                self.label_advanced_reports.setText(f"There are {num_plugins} advanced reports available. Enable report to generate:")

            # Hide the "no plugins" message
            self.label_no_plugins.setVisible(False)

            # Get the layout from the scroll area
            layout = self.verticalLayout_advanced_plugins

            # Remove the "no plugins" label and spacer from layout
            layout.removeWidget(self.label_no_plugins)
            # Find and remove the spacer
            for i in range(layout.count()):
                item = layout.itemAt(i)
                if isinstance(item, QSpacerItem):
                    layout.removeItem(item)
                    break

            # Create UI elements for each plugin
            for plugin_class in plugin_classes:
                try:
                    plugin_instance = plugin_class()
                    metadata = plugin_instance.get_metadata()

                    # Store plugin class and instance
                    self.__plugins[metadata.id] = plugin_class
                    self.__plugin_instances[metadata.id] = plugin_instance

                    # Create a group box for this plugin
                    group_box = QGroupBox()
                    group_box.setTitle(f"{metadata.name} - v{metadata.version}")
                    # Use FlowLayout with margins matching standard checkboxes (10px) and smaller spacing
                    group_box_layout = FlowLayout(margin=10, spacing=10)

                    # Create main checkbox to enable/disable the plugin
                    checkbox = QCheckBox(f"Enable")

                    # Format plugin IDs for tooltip (handle None case for plugins that collect all IDs)
                    if metadata.plugin_ids is None:
                        plugin_ids_str = "All plugins"
                    else:
                        plugin_ids_str = ', '.join(map(str, metadata.plugin_ids))

                    checkbox.setToolTip(
                        f"{metadata.description}\n\n"
                        f"Version: {metadata.version}\n"
                        f"Author: {metadata.author}\n"
                        f"Plugin IDs: {plugin_ids_str}"
                    )

                    # Store checkbox reference
                    self.__plugin_checkboxes[metadata.id] = checkbox

                    # Initialize parsing settings for this plugin
                    setting_key = plugin_instance.get_enabled_setting_key()
                    self.update_parsing_settings(setting_key, False)

                    # Connect checkbox to handler
                    checkbox.stateChanged.connect(
                        lambda state, pid=metadata.id: self.plugin_checkbox_changed(pid, state)
                    )

                    group_box_layout.addWidget(checkbox)

                    # Add any additional settings from the plugin
                    additional_settings = plugin_instance.get_additional_settings()
                    for setting in additional_settings:
                        if setting['type'] == 'checkbox':
                            setting_checkbox = QCheckBox(setting['label'])
                            if 'tooltip' in setting:
                                setting_checkbox.setToolTip(setting['tooltip'])
                            setting_checkbox.setEnabled(False)  # Disabled until main checkbox is checked

                            # Initialize setting
                            self.update_parsing_settings(setting['key'], setting['default'])

                            # Store label for logging
                            self.__plugin_setting_labels[setting['key']] = setting['label']

                            # Connect to handler
                            setting_checkbox.stateChanged.connect(
                                lambda state, key=setting['key']: self.plugin_setting_changed(key, state)
                            )

                            # Store reference for enabling/disabling
                            if metadata.id not in self.__plugin_checkboxes:
                                self.__plugin_checkboxes[metadata.id] = {}
                            if not isinstance(self.__plugin_checkboxes[metadata.id], dict):
                                main_cb = self.__plugin_checkboxes[metadata.id]
                                self.__plugin_checkboxes[metadata.id] = {'main': main_cb}
                            self.__plugin_checkboxes[metadata.id][setting['key']] = setting_checkbox

                            group_box_layout.addWidget(setting_checkbox)

                    group_box.setLayout(group_box_layout)
                    layout.addWidget(group_box)

                    self.print_log(f"Loaded plugin: {metadata.name} v{metadata.version}", "green")

                except Exception as e:
                    self.print_log(f"Error loading plugin {plugin_class.__name__}: {str(e)}", "red")

            # Add spacer at the end
            spacer = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding)
            layout.addItem(spacer)

        except Exception as e:
            self.print_log(f"Error initializing plugins: {str(e)}", "red")
            import traceback
            traceback.print_exc()

    def plugin_checkbox_changed(self, plugin_id, state):
        """
        Handle plugin enable/disable checkbox state change.

        Args:
            plugin_id: The plugin ID
            state: Qt.CheckState value
        """
        try:
            plugin_instance = self.__plugin_instances[plugin_id]
            metadata = plugin_instance.get_metadata()
            setting_key = plugin_instance.get_enabled_setting_key()

            if state == Qt.Checked:
                info = f"{metadata.name} enabled."
                color = "green"
                self.print_log(info, color)
                self.update_parsing_settings(setting_key, True)
                # Enable additional setting checkboxes
                plugin_checkboxes = self.__plugin_checkboxes[plugin_id]
                if isinstance(plugin_checkboxes, dict):
                    for key, checkbox in plugin_checkboxes.items():
                        if key != 'main':
                            checkbox.setEnabled(True)
            else:
                info = f"{metadata.name} disabled."
                color = "green"
                self.print_log(info, color)
                self.update_parsing_settings(setting_key, False)
                # Disable additional setting checkboxes
                plugin_checkboxes = self.__plugin_checkboxes[plugin_id]
                if isinstance(plugin_checkboxes, dict):
                    for key, checkbox in plugin_checkboxes.items():
                        if key != 'main':
                            checkbox.setEnabled(False)

            # Update start button state - enable if any report or plugin is enabled AND files are selected
            any_report_enabled = (
                self.__report_scan_enabled
                or self.__report_host_enabled
                or self.__report_vulnerabilities_enabled
                or self.__report_noncompliance_enabled
            )

            # Check if any plugin is enabled
            any_plugin_enabled = any(
                self.__parsing_settings.get(self.__plugin_instances[pid].get_enabled_setting_key(), False)
                for pid in self.__plugin_instances
            )

            if (any_report_enabled or any_plugin_enabled) and self.__files_to_pars:
                self.pushButton_start.setEnabled(True)
                self.actionStart_analysis.setEnabled(True)
            elif not any_report_enabled and not any_plugin_enabled:
                self.pushButton_start.setDisabled(True)
                self.actionStart_analysis.setDisabled(True)

        except Exception as e:
            self.print_log(f"Error handling plugin checkbox change: {str(e)}", "red")

    def plugin_setting_changed(self, setting_key, state):
        """
        Handle plugin additional setting checkbox state change.

        Args:
            setting_key: The setting key in parsing_settings
            state: Qt.CheckState value
        """
        try:
            # Get the label for this setting
            setting_label = self.__plugin_setting_labels.get(setting_key, setting_key)

            if state == Qt.Checked:
                info = f"{setting_label} enabled."
                color = "green"
                self.print_log(info, color)
                self.update_parsing_settings(setting_key, True)
            else:
                info = f"{setting_label} disabled."
                color = "green"
                self.print_log(info, color)
                self.update_parsing_settings(setting_key, False)
        except Exception as e:
            self.print_log(f"Error handling plugin setting change: {str(e)}", "red")

    def dragEnterEvent(self, e):

        if e.mimeData().hasFormat("text/uri-list"):
            e.accept()
        else:
            e.ignore()

    def dropEvent(self, e):
        qurls = e.mimeData().urls()
        self.open_files_by_drag_and_drop(qurls)

    def print_settings(self):
        """
        Function prints all settings specified by user.
        """
        print(">>>")
        for setting, value in self.__parsing_settings.items():
            if value:
                print(setting, value, "++++++++++++++++++++++++++++")
            else:
                print(setting, value)
        print("<<<")

    def report_scan_changed(self):
        """
        Function enables or disables setting which allow generation of spreadsheet with scan sum-up.
        """
        if self.checkBox_report_scan.isChecked():
            info = "Scan report enabled."
            self.__report_scan_enabled = True
            self.groupBox_options_scan.setEnabled(True)
        else:
            info = "Scan report disabled."
            self.__report_scan_enabled = False
            self.groupBox_options_scan.setDisabled(True)
        color = "green"
        self.print_log(info, color)
        self.update_parsing_settings("report_scan_enabled", self.__report_scan_enabled)
        # self.print_settings()

        # Check if any standard report is enabled
        any_standard_report_enabled = (
            self.__report_scan_enabled
            or self.__report_host_enabled
            or self.__report_vulnerabilities_enabled
            or self.__report_noncompliance_enabled
        )

        # Check if any plugin is enabled
        any_plugin_enabled = any(
            self.__parsing_settings.get(self.__plugin_instances[pid].get_enabled_setting_key(), False)
            for pid in self.__plugin_instances
        )

        if (any_standard_report_enabled or any_plugin_enabled) and self.__files_to_pars:
            self.pushButton_start.setEnabled(True)
            self.actionStart_analysis.setEnabled(True)
        elif not any_standard_report_enabled and not any_plugin_enabled:
            self.pushButton_start.setDisabled(True)
            self.actionStart_analysis.setDisabled(True)

    def report_scan_setting_debug_changed(self):
        """
        Function enables or disables setting which allow generation of spreadsheet with scan sum-up with additional
        data.
        """
        if self.checkBox_debug_data_scan.isChecked():
            info = "Scan report debug data enabled."
            self.__report_scan_setting_debug_data_enabled = True
        else:
            info = "Scan report debug data disabled."
            self.__report_scan_setting_debug_data_enabled = False
        color = "green"
        self.print_log(info, color)
        self.update_parsing_settings(
            "report_scan_debug_data_enabled",
            self.__report_scan_setting_debug_data_enabled,
        )
        # self.print_settings()

    def report_host_changed(self):
        """
        Function enables or disables setting which allow generation of spreadsheet with host sum-up.
        """
        if self.checkBox_report_host.isChecked():
            info = "Host report enabled."
            self.__report_host_enabled = True
            self.groupBox_options_host.setEnabled(True)
        else:
            info = "Host report disabled."
            self.__report_host_enabled = False
            self.groupBox_options_host.setDisabled(True)
        color = "green"
        self.print_log(info, color)
        self.update_parsing_settings("report_host_enabled", self.__report_host_enabled)
        # self.print_settings()

        # Check if any standard report is enabled
        any_standard_report_enabled = (
            self.__report_scan_enabled
            or self.__report_host_enabled
            or self.__report_vulnerabilities_enabled
            or self.__report_noncompliance_enabled
        )

        # Check if any plugin is enabled
        any_plugin_enabled = any(
            self.__parsing_settings.get(self.__plugin_instances[pid].get_enabled_setting_key(), False)
            for pid in self.__plugin_instances
        )

        if (any_standard_report_enabled or any_plugin_enabled) and self.__files_to_pars:
            self.pushButton_start.setEnabled(True)
            self.actionStart_analysis.setEnabled(True)
        elif not any_standard_report_enabled and not any_plugin_enabled:
            self.pushButton_start.setDisabled(True)
            self.actionStart_analysis.setDisabled(True)

    def report_host_setting_debug_changed(self):
        """
        Function enables or disables setting which allow generation of spreadsheet with host sum-up with additional
        data.
        """
        if self.checkBox_debug_data_host.isChecked():
            info = "Host report debug data enabled."
            self.__report_host_setting_debug_data_enabled = True
        else:
            info = "Host report debug data disabled."
            self.__report_host_setting_debug_data_enabled = False
        color = "green"
        self.print_log(info, color)
        self.update_parsing_settings(
            "report_host_debug_data_enabled",
            self.__report_host_setting_debug_data_enabled,
        )
        # self.print_settings()

    def report_vulnerabilities_changed(self):
        """
        Function enables or disables setting which allow generation of spreadsheet with vulnerabilities sum-up.
        """
        if self.checkBox_report_vulnerabilities.isChecked():
            info = "Vulnerabilities report enabled."
            self.__report_vulnerabilities_enabled = True
            self.groupBox_options_vulnerabilities.setEnabled(True)
        else:
            info = "Vulnerabilities report disabled."
            self.__report_vulnerabilities_enabled = False
            self.groupBox_options_vulnerabilities.setDisabled(True)
        color = "green"
        self.print_log(info, color)
        self.update_parsing_settings(
            "report_vulnerabilities_enabled", self.__report_vulnerabilities_enabled
        )
        # self.print_settings()

        # Check if any standard report is enabled
        any_standard_report_enabled = (
            self.__report_scan_enabled
            or self.__report_host_enabled
            or self.__report_vulnerabilities_enabled
            or self.__report_noncompliance_enabled
        )

        # Check if any plugin is enabled
        any_plugin_enabled = any(
            self.__parsing_settings.get(self.__plugin_instances[pid].get_enabled_setting_key(), False)
            for pid in self.__plugin_instances
        )

        if (any_standard_report_enabled or any_plugin_enabled) and self.__files_to_pars:
            self.pushButton_start.setEnabled(True)
            self.actionStart_analysis.setEnabled(True)
        elif not any_standard_report_enabled and not any_plugin_enabled:
            self.pushButton_start.setDisabled(True)
            self.actionStart_analysis.setDisabled(True)

    def report_vulnerabilities_setting_debug_changed(self):
        """
        Function enables or disables setting which allow generation of spreadsheet with vulnerabilities sum-up with
        additional data.
        """
        if self.checkBox_debug_data_vulnerabilities.isChecked():
            info = "Vulnerabilities report debug data enabled."
            self.__report_vulnerabilities_setting_debug_data_enabled = True
        else:
            info = "Vulnerabilities report debug data disabled."
            self.__report_vulnerabilities_setting_debug_data_enabled = False
        color = "green"
        self.print_log(info, color)
        self.update_parsing_settings(
            "report_vulnerabilities_debug_data_enabled",
            self.__report_vulnerabilities_setting_debug_data_enabled,
        )
        # self.print_settings()

    def report_vulnerabilities_setting_none_filter_out_changed(self):
        """
        Function enables or disables setting which allow generation of spreadsheet with vulnerabilities sum-up with
        filtered out vulnerabilities with Risk Factor equal None.
        """
        if self.checkBox_vulnerabilities_none_filter_out.isChecked():
            info = "Vulnerabilities report None plugins filter out enabled."
            self.__report_vulnerabilities_setting_none_filter_out = True
            if self.checkBox_vulnerabilities_none_skip.isChecked():
                self.checkBox_vulnerabilities_none_skip.setChecked(False)
        else:
            info = "Vulnerabilities report None plugins filter out disabled."
            self.__report_vulnerabilities_setting_none_filter_out = False
        color = "green"
        self.print_log(info, color)
        self.update_parsing_settings(
            "report_vulnerabilities_none_filter_out",
            self.__report_vulnerabilities_setting_none_filter_out,
        )
        # self.print_settings()

    def report_vulnerabilities_setting_none_skip_changed(self):
        """
        Function enables or disables setting which allow generation of spreadsheet with vulnerabilities sum-up with out
        vulnerabilities with Risk Factor equal None.
        """
        if self.checkBox_vulnerabilities_none_skip.isChecked():
            info = "Vulnerabilities report None plugins skip enabled."
            self.__report_vulnerabilities_setting_none_skip = True
            if self.checkBox_vulnerabilities_none_filter_out.isChecked():
                self.checkBox_vulnerabilities_none_filter_out.setChecked(False)
        else:
            info = "Vulnerabilities report None plugins skip disabled."
            self.__report_vulnerabilities_setting_none_skip = False
        color = "green"
        self.print_log(info, color)
        self.update_parsing_settings(
            "report_vulnerabilities_none_skip",
            self.__report_vulnerabilities_setting_none_skip,
        )
        # self.print_settings()

    def report_noncompliance_changed(self):
        """
        Function enables or disables setting which allow generation of spreadsheet with noncompliance sum-up.
        """
        if self.checkBox_report_noncompliance.isChecked():
            info = "Noncompliance report enabled."
            self.__report_noncompliance_enabled = True
            self.groupBox_options_noncompliance.setEnabled(True)
        else:
            info = "Noncompliance report disabled."
            self.__report_noncompliance_enabled = False
            self.groupBox_options_noncompliance.setDisabled(True)
        color = "green"
        self.print_log(info, color)
        self.update_parsing_settings(
            "report_noncompliance_enabled", self.__report_noncompliance_enabled
        )
        # self.print_settings()

        # Check if any standard report is enabled
        any_standard_report_enabled = (
            self.__report_scan_enabled
            or self.__report_host_enabled
            or self.__report_vulnerabilities_enabled
            or self.__report_noncompliance_enabled
        )

        # Check if any plugin is enabled
        any_plugin_enabled = any(
            self.__parsing_settings.get(self.__plugin_instances[pid].get_enabled_setting_key(), False)
            for pid in self.__plugin_instances
        )

        if (any_standard_report_enabled or any_plugin_enabled) and self.__files_to_pars:
            self.pushButton_start.setEnabled(True)
            self.actionStart_analysis.setEnabled(True)
        elif not any_standard_report_enabled and not any_plugin_enabled:
            self.pushButton_start.setDisabled(True)
            self.actionStart_analysis.setDisabled(True)

    def report_noncompliance_setting_debug_changed(self):
        """
        Function enables or disables setting which allow generation of spreadsheet with noncompliance sum-up with
        additional data.
        """
        if self.checkBox_debug_data_noncompliance.isChecked():
            info = "noncompliance report debug data enabled."
            self.__report_noncompliance_setting_debug_data_enabled = True
        else:
            info = "noncompliance report debug data disabled."
            self.__report_noncompliance_setting_debug_data_enabled = False
        color = "green"
        self.print_log(info, color)
        self.update_parsing_settings(
            "report_noncompliance_debug_data_enabled",
            self.__report_noncompliance_setting_debug_data_enabled,
        )
        # self.print_settings()

    def set_source_directory_as_target_directory_changed(self):
        """
        Function enables or disables setting which inform about target directory which is set based on selected source
        directory.
        """
        if self.checkBox_set_source_directory_as_target_directory.isChecked():
            info = "Target directory based on selected source enabled."
            self.__report_set_source_directory_as_target_directory_enabled = True
        else:
            info = "Target directory based on selected source disabled."
            self.__report_set_source_directory_as_target_directory_enabled = False

        color = "green"
        self.print_log(info, color)
        self.update_parsing_settings(
            "report_set_source_directory_as_target_directory_enabled",
            self.__report_set_source_directory_as_target_directory_enabled,
        )

        self.set_source_directory_as_target_directory()

    def set_source_directory_as_target_directory(self):
        """
        Function sets target directory base on path from selected source file or base on path from first file from
        selected source directory, works only if source file or directory has been already selected.
        """
        files_to_pars = self.__files_to_pars

        if files_to_pars:
            if self.__report_set_source_directory_as_target_directory_enabled:
                old_target_directory = self.__target_directory
                first_file_path = os.path.dirname(files_to_pars[0])
                first_file_path_normalized = os.path.normpath(first_file_path)
                self.set_target_directory(first_file_path_normalized)
                self.lineEdit_target_directory.setText(self.__target_directory)
                color = "green"
                info2 = (
                    'Target directory changed from "'
                    + old_target_directory
                    + '" to "'
                    + self.__target_directory
                    + '"'
                )
                self.print_log(info2, color)
                self.__target_directory_changed = True
            else:
                old_target_directory = self.__target_directory
                cwd = os.getcwd()
                cwd_normalized = os.path.normpath(cwd)
                self.set_target_directory(cwd_normalized)
                self.lineEdit_target_directory.setText(self.__target_directory)
                color = "green"
                info2 = (
                    'Target directory changed from "'
                    + old_target_directory
                    + '" to "'
                    + self.__target_directory
                    + '"'
                )
                self.print_log(info2, color)
                self.__target_directory_changed = True

    def suffix_timestamp_changed(self):
        """
        Function sets suffix appropriately if checkBox_suffix_timestamp has changed.
        """
        if (
            self.checkBox_suffix_timestamp.isChecked()
            and not self.checkBox_suffix_custom.isChecked()
        ):
            time_now = datetime.datetime.now()
            time_now_formatted = time_now.strftime("%Y%m%d_%H%M%S")
            suffix = "_" + time_now_formatted
            self.change_suffix(suffix)
            self.label_target_file_name_value.setText(
                self.__target_file_name_prefix + suffix + ".xlsx"
            )
            self.__suffix_template = "suffix_timestamp"

        elif (
            not self.checkBox_suffix_timestamp.isChecked()
            and self.checkBox_suffix_custom.isChecked()
        ):
            suffix_custom_value = self.lineEdit_suffix_custom_value.text()
            if suffix_custom_value:
                space = "_"
                self.__suffix_template = "suffix_custom"
            else:
                space = ""
                self.__suffix_template = "suffix_custom_empty"
            suffix = space + suffix_custom_value
            self.change_suffix(suffix)
            self.label_target_file_name_value.setText(
                self.__target_file_name_prefix + suffix + ".xlsx"
            )

        elif (
            self.checkBox_suffix_timestamp.isChecked()
            and self.checkBox_suffix_custom.isChecked()
        ):
            time_now = datetime.datetime.now()
            time_now_formatted = time_now.strftime("%Y%m%d_%H%M%S")
            suffix_custom_value = self.lineEdit_suffix_custom_value.text()
            if suffix_custom_value:
                space = "_"
                self.__suffix_template = "suffix_custom_timestamp"
            else:
                space = ""
                self.__suffix_template = "suffix_custom_empty_timestamp"
            suffix = space + suffix_custom_value + "_" + time_now_formatted
            self.change_suffix(suffix)
            self.label_target_file_name_value.setText(
                self.__target_file_name_prefix + suffix + ".xlsx"
            )

        else:
            self.change_suffix("")
            self.label_target_file_name_value.setText(
                self.__target_file_name_prefix + ".xlsx"
            )
            self.__suffix_template = "empty"

    def check_box_suffix_state_changed(self):
        """
        Function enables and disables lineEdit_suffix_custom_value and sets suffix appropriately.
        """
        if (
            self.checkBox_suffix_custom.isChecked()
            and not self.checkBox_suffix_timestamp.isChecked()
        ):
            self.lineEdit_suffix_custom_value.setEnabled(True)
            suffix_custom_value = self.lineEdit_suffix_custom_value.text()
            if suffix_custom_value:
                space = "_"
                self.__suffix_template = "suffix_custom"
            else:
                space = ""
                self.__suffix_template = "suffix_custom_empty"
            suffix = space + suffix_custom_value
            self.change_suffix(suffix)
            self.label_target_file_name_value.setText(
                self.__target_file_name_prefix + suffix + ".xlsx"
            )

        elif (
            self.checkBox_suffix_custom.isChecked()
            and self.checkBox_suffix_timestamp.isChecked()
        ):
            self.lineEdit_suffix_custom_value.setEnabled(True)
            time_now = datetime.datetime.now()
            time_now_formatted = time_now.strftime("%Y%m%d_%H%M%S")
            suffix_custom_value = self.lineEdit_suffix_custom_value.text()
            if suffix_custom_value:
                space = "_"
                self.__suffix_template = "suffix_timestamp_custom"
            else:
                space = ""
                self.__suffix_template = "suffix_timestamp_custom_empty"
            suffix = "_" + time_now_formatted + space + suffix_custom_value
            self.change_suffix(suffix)
            self.label_target_file_name_value.setText(
                self.__target_file_name_prefix + suffix + ".xlsx"
            )

        elif (
            not self.checkBox_suffix_custom.isChecked()
            and self.checkBox_suffix_timestamp.isChecked()
        ):
            self.lineEdit_suffix_custom_value.setDisabled(True)
            time_now = datetime.datetime.now()
            time_now_formatted = time_now.strftime("%Y%m%d_%H%M%S")
            suffix = "_" + time_now_formatted
            self.change_suffix(suffix)
            self.__suffix_template = "suffix_timestamp"
            self.label_target_file_name_value.setText(
                self.__target_file_name_prefix + suffix + ".xlsx"
            )

        elif (
            not self.checkBox_suffix_custom.isChecked()
            and not self.checkBox_suffix_timestamp.isChecked()
        ):
            self.lineEdit_suffix_custom_value.setDisabled(True)
            self.change_suffix("")
            self.label_target_file_name_value.setText(
                self.__target_file_name_prefix + ".xlsx"
            )
            self.__suffix_template = "empty"

    def line_edit_suffix_custom_value_changed(self):
        """
        Function sets suffix appropriately if lineEdit_suffix_custom_value has changed.
        """
        if (
            self.checkBox_suffix_custom.isChecked()
            and not self.checkBox_suffix_timestamp.isChecked()
        ):
            suffix_custom_value = self.lineEdit_suffix_custom_value.text()
            if suffix_custom_value:
                space = "_"
                self.__suffix_template = "suffix_custom"
            else:
                space = ""
                self.__suffix_template = "suffix_custom_empty"
            suffix = space + suffix_custom_value
            self.change_suffix(suffix)
            self.label_target_file_name_value.setText(
                self.__target_file_name_prefix + suffix + ".xlsx"
            )

        elif (
            self.checkBox_suffix_custom.isChecked()
            and self.checkBox_suffix_timestamp.isChecked()
        ):
            time_now = datetime.datetime.now()
            time_now_formatted = time_now.strftime("%Y%m%d_%H%M%S")
            suffix_custom_value = self.lineEdit_suffix_custom_value.text()
            if suffix_custom_value:
                space = "_"
                self.__suffix_template = "suffix_timestamp_custom"
            else:
                space = ""
                self.__suffix_template = "suffix_timestamp_custom_empty"
            suffix = "_" + time_now_formatted + space + suffix_custom_value
            self.change_suffix(suffix)
            self.label_target_file_name_value.setText(
                self.__target_file_name_prefix + suffix + ".xlsx"
            )

    def open_dialog_about(self):
        """
        Function opens About dialog.
        """
        self.dialog_about = about.About()

    def open_dialog_update_check(self):
        """
        Function opens Update check dialog.
        """
        self.dialog_update_check = update_check.UpdateCheck()

    def check_announcements(self):
        """
        Function checks Announcements.
        """
        
        announcements = utilities.get_announcements(__about__.__package_name__,__about__.__version__)
        for a in announcements:
            self.print_log(f"[{a['type'].upper()}] {a['title']}: {a['message']}", "orange")

    def check_announcements_button(self):
        """
        Function checks Announcements.
        """
        
        announcements = utilities.get_announcements(__about__.__package_name__,__about__.__version__)
        for a in announcements:
            self.print_log(f"[{a['type'].upper()}] {a['title']}: {a['message']}", "orange")

        if not announcements:
            self.print_log("No new announcements.", "green")

    def display_update_window(self):
        """
        Function automatically opens Update check dialog if new version is available.
        """

        PACKAGE_NAME = __about__.__package_name__
        current_version = __about__.__version__

        try:
            response = requests.get(
                f"https://pypi.org/pypi/{PACKAGE_NAME}/json", timeout=1.5
            )
            response.raise_for_status()
            latest = response.json()["info"]["version"]
            if version.parse(latest) > version.parse(current_version):
                print("New version available:", latest)
                self.dialog_update_check = update_check.UpdateCheck()

        except requests.exceptions.ConnectionError as e:
            return (
                None,
                f"Could not check for updates: <br><br><i>Connection error</i><br><br>{e}",
            )
        except Exception as e:
            return None, f"Could not check for updates:<br><br>{e}"

    def open_url_documentation(self):
        """
        Function opens Url with documentation.
        """
        PACKAGE_NAME = nfa.__about__.__package_name__
        url_open.open_website(f"https://limberduck.org/en/latest/tools/{PACKAGE_NAME}/")

    def open_url_github(self):
        """
        Function opens Url with GitHub.
        """
        PACKAGE_NAME = nfa.__about__.__package_name__
        url_open.open_website(f"https://github.com/LimberDuck/{PACKAGE_NAME}")

    def open_url_github_releases(self):
        """
        Function opens Url with GitHub Releases.
        """
        PACKAGE_NAME = nfa.__about__.__package_name__
        url_open.open_website(f"https://github.com/LimberDuck/{PACKAGE_NAME}/releases")

    def parsing_thread_start(self):
        """
        Function starts separate thread to pars selected files.
        """
        self.__file_analysis_counter = 0
        self.statusbar.clearMessage()
        self.progressBar.setVisible(True)

        info = "Analysis started."
        color = "blue"
        self.print_log(info, color=color)
        try:
            if self.__suffix_template == "suffix_timestamp":
                time_now = datetime.datetime.now()
                time_now_formatted = time_now.strftime("%Y%m%d_%H%M%S")
                self.update_parsing_settings("suffix", "_" + time_now_formatted)

            elif self.__suffix_template == "suffix_custom":
                suffix_custom_value = self.lineEdit_suffix_custom_value.text()
                self.update_parsing_settings("suffix", "_" + suffix_custom_value)

            elif self.__suffix_template == "suffix_custom_empty":
                self.update_parsing_settings("suffix", "")

            elif self.__suffix_template == "suffix_custom_timestamp":
                time_now = datetime.datetime.now()
                time_now_formatted = time_now.strftime("%Y%m%d_%H%M%S")
                suffix_custom_value = self.lineEdit_suffix_custom_value.text()
                self.update_parsing_settings(
                    "suffix", "_" + suffix_custom_value + "_" + time_now_formatted
                )

            elif self.__suffix_template == "suffix_custom_empty_timestamp":
                time_now = datetime.datetime.now()
                time_now_formatted = time_now.strftime("%Y%m%d_%H%M%S")
                self.update_parsing_settings("suffix", "_" + time_now_formatted)

            elif self.__suffix_template == "suffix_timestamp_custom":
                time_now = datetime.datetime.now()
                time_now_formatted = time_now.strftime("%Y%m%d_%H%M%S")
                suffix_custom_value = self.lineEdit_suffix_custom_value.text()
                self.update_parsing_settings(
                    "suffix", "_" + time_now_formatted + "_" + suffix_custom_value
                )

            elif self.__suffix_template == "suffix_timestamp_custom_empty":
                time_now = datetime.datetime.now()
                time_now_formatted = time_now.strftime("%Y%m%d_%H%M%S")
                self.update_parsing_settings("suffix", "_" + time_now_formatted)

            elif self.__suffix_template == "empty":
                self.update_parsing_settings("suffix", "")

            self.parsing_thread = ParsingThread(
                files_to_pars=self.__files_to_pars,
                target_directory=self.__target_directory,
                target_directory_changed=self.__target_directory_changed,
                parsing_settings=self.__parsing_settings,
            )
            self.parsing_thread.start()
            self.parsing_thread.signal.connect(self.parsing_thread_done)

            self.parsing_thread.progress.connect(self.analysis_progress)
            self.parsing_thread.print_status_bar_info.connect(
                self.print_status_bar_info
            )

        except Exception as e:
            color = "red"
            self.print_log("\nUps... ERROR occurred. \n\n" + str(e), color=color)
            traceback.print_exc()
            print(">>>", e, "<<<")

    def print_status_bar_info(self, text):
        """
        Function sends notification via status bar about progress of analysis for each input file.
        :param text: text send to status bar
        """
        self.statusbar.clearMessage()
        self.statusbar.showMessage(text)
        self.statusbar.repaint()

    def analysis_progress(self, host_number, all_hosts_number):
        """
        Function shows analysis progress for each input file.
        Input parameters are used to set progress bar values.
        :param host_number: number of currently processed host in input file currently processed
        :param all_hosts_number: number of all hosts in input file currently processed
        """
        self.progressBar.setRange(0, all_hosts_number)
        self.progressBar.setValue(host_number)

    def parsing_thread_done(self, info):
        """
        Function shows information from parsing threads.
        :param info: information to display
        """
        if "[action=start]" in info:
            color = "blue"
            self.print_log(info, color)
        elif "[action=end  ]" in info:
            color = "green"
            self.print_log(info, color)
        else:
            color = "black"
            self.print_log(info, color)

    def update_parsing_settings(self, setting_name, setting_value):
        """
        Function gets current parsing setting and updates dictionary containing all parsing settings.
        :param setting_name: setting key name
        :param setting_value: setting value
        """
        self.__parsing_settings[setting_name] = setting_value

    def set_suffix(self, suffix_value):
        """
        Function sets given suffix into private variable __suffix and update parsing settings.
        :param suffix_value: input suffix
        """
        self.__suffix = suffix_value
        self.update_parsing_settings("suffix", suffix_value)

    def change_suffix(self, suffix_value):
        """
        Function changes given suffix in private variable.
        Confirmation is send to GUI into progress preview.
        :param suffix_value: new suffix value
        """
        old_suffix = self.__suffix
        new_suffix = suffix_value

        self.set_suffix(new_suffix)

        color = "green"
        info = 'Suffix changed from "' + old_suffix + '" to "' + new_suffix + '"'
        self.print_log(info, color)

    def set_target_directory(self, target_directory_value):
        """
        Function sets given target directory into private variable __target_directory.
        :param target_directory_value: target directory
        """
        self.__target_directory = target_directory_value

    def get_target_directory_from_file(self):
        """
        Function gets target directory from private variable __target_directory
        and set it into lineEdit_target_directory.
        Works only if user used selection by one or more files.
        """
        self.lineEdit_target_directory.setText(self.__target_directory)

    def get_target_directory_from_directory(self):
        """
        Function gets target directory from private variable __target_directory
        and set it into lineEdit_target_directory.
        Works only if user used selection by directory.
        """
        self.lineEdit_target_directory.setText(self.__target_directory)

    def change_target_directory(self):
        """
        Function changes target directory and set it in private variable __target_directory.
        Confirmation is send to GUI into progress preview.
        """
        old_target_directory = self.__target_directory

        title = "Choose new target directory"
        starting_directory = ""
        options = QFileDialog.Options()
        options |= QFileDialog.ShowDirsOnly

        file_dialog = QFileDialog()

        directories = file_dialog.getExistingDirectory(
            None, title, starting_directory, options=options
        )

        if directories:
            directories = nfr.file.nessus_scan_file_name_with_path(directories)
            self.set_target_directory(directories)
            self.get_target_directory_from_file()

            color = "green"
            info = (
                'Target directory changed from "'
                + old_target_directory
                + '" to "'
                + self.__target_directory
                + '"'
            )
            self.print_log(info, color)
            self.__target_directory_changed = True
        else:
            info = "Target directory not changed."
            color = "black"
            self.print_log(info, color=color)
            self.__target_directory_changed = False

    def open_files(self):
        """
        Function get list of files via dialog window.
        Possible to select one or more files.
        """
        info = "File\\-s opening."
        color = "black"
        self.print_log(info, color=color)

        extension = ["nessus", "zip"]

        title = f"Open {extension[0]} or {extension[1]} containing {extension[0]} files"
        starting_directory = ""
        file_filter = (
            f"Nessus scan file & ZIP Archive (*.nessus *.zip);;Nessus scan file (*.{extension[0]});;ZIP Archive (*.{extension[1]})"
        )
        options = QFileDialog.Options()
        options |= QFileDialog.ReadOnly

        file_dialog = QFileDialog()
        file_dialog.setFileMode(QFileDialog.ExistingFiles)

        files = file_dialog.getOpenFileNames(
            self, title, starting_directory, filter=file_filter, options=options
        )
        files_only = [os.path.abspath(item) for item in files[0]]
        # print(f'File open, after map {len(files_only)} {files_only}')

        if len(files_only):
            if self.checkBox_set_source_directory_as_target_directory.isChecked():
                for file_name in files_only:
                    # print(file_name)
                    target_directory = os.path.dirname(os.path.abspath(file_name))
                    self.set_target_directory(target_directory)
                    # print(target_directory)

                self.get_target_directory_from_file()

        self.list_of_files_to_pars(files_only)

        if len(files_only) > 0:
            # Check if any standard report is enabled
            any_standard_report_enabled = (
                self.__report_scan_enabled
                or self.__report_host_enabled
                or self.__report_vulnerabilities_enabled
                or self.__report_noncompliance_enabled
            )
            # Check if any plugin is enabled
            any_plugin_enabled = any(
                self.__parsing_settings.get(self.__plugin_instances[pid].get_enabled_setting_key(), False)
                for pid in self.__plugin_instances
            ) if self.__plugin_instances else False

            if any_standard_report_enabled or any_plugin_enabled:
                self.pushButton_start.setEnabled(True)
                self.actionStart_analysis.setEnabled(True)
        else:
            self.pushButton_start.setDisabled(True)
            self.actionStart_analysis.setDisabled(True)

    @staticmethod
    def check_if_subdirectory_exist(main_directory):
        """
        Function verifies whether provided directory has any subdirectories.
        :param main_directory: path to selected directory
        :return: 0 if there is no subdirectories
                 number_of_directories > 0 if there is any subdirectory, where number_of_directories is information
                 about number of subdirectories.
        """
        pattern = os.path.join(main_directory, "*")
        number_of_directories = 0
        for candidate in glob.glob(pattern):
            if os.path.isdir(candidate):
                number_of_directories += 1
                break
        return number_of_directories

    def open_directory(self):
        """
        Function gets list of files via dialog window.
        Possible to get files from selected directory and subdirectories.
        """
        info = "Files from directory and subdirectories opening."
        color = "black"
        self.print_log(info, color=color)

        extension = ["nessus", "zip"]

        title = f"Open {extension[0]} or {extension[1]} containing {extension[0]} files directory"
        starting_directory = ""
        options = QFileDialog.Options()
        options |= QFileDialog.ShowDirsOnly

        file_dialog = QFileDialog()
        directories = file_dialog.getExistingDirectory(
            None, title, starting_directory, options=options
        )
        os_separator = os.path.sep
        target_directory = os.path.abspath(directories)

        files = []
        if directories:
            if self.checkBox_set_source_directory_as_target_directory.isChecked():
                self.set_target_directory(target_directory)
                self.get_target_directory_from_directory()
                # print(target_directory)

            nessus_files = glob.glob(
                target_directory
                + os_separator
                + "**"
                + os_separator
                + "*."
                + extension[0],
                recursive=True,
            )
            zip_files = glob.glob(
                target_directory
                + os_separator
                + "**"
                + os_separator
                + "*."
                + extension[1],
                recursive=True,
            )
            files = nessus_files + zip_files
            # print(f'Dir open, {len(files)} {files}')

        self.list_of_files_to_pars(files)

        if len(files) > 0:
            # Check if any standard report is enabled
            any_standard_report_enabled = (
                self.__report_scan_enabled
                or self.__report_host_enabled
                or self.__report_vulnerabilities_enabled
                or self.__report_noncompliance_enabled
            )
            # Check if any plugin is enabled
            any_plugin_enabled = any(
                self.__parsing_settings.get(self.__plugin_instances[pid].get_enabled_setting_key(), False)
                for pid in self.__plugin_instances
            ) if self.__plugin_instances else False

            if any_standard_report_enabled or any_plugin_enabled:
                self.pushButton_start.setEnabled(True)
                self.actionStart_analysis.setEnabled(True)
        else:
            self.pushButton_start.setDisabled(True)
            self.actionStart_analysis.setDisabled(True)

    def open_files_by_drag_and_drop(self, qurls):
        """
        Function get list of files via Drag and Drop.
        Possible to select one or more files or directories.
        """
        info = "File\\-s opening by Drag & Drop."
        color = "black"
        self.print_log(info, color=color)

        paths = []
        for qurl in qurls:
            url = qurl.toString()
            url_data = urllib.parse.urlparse(url)
            path = urllib.parse.unquote(url_data.path)
            os_name = platform.system()
            if os_name == "Windows":
                path = os.path.abspath(path[1:])

            extension = ["nessus", "zip"]

            if os.path.isfile(path):
                if fnmatch.fnmatch(path, f"*.{extension[0]}") or fnmatch.fnmatch(
                    path, f"*.{extension[1]}"
                ):
                    paths.append(path)
                    if (
                        self.checkBox_set_source_directory_as_target_directory.isChecked()
                    ):
                        target_directory = os.path.dirname(os.path.abspath(path))
                        self.set_target_directory(target_directory)
                        self.get_target_directory_from_file()

            elif os.path.isdir(path):
                os_separator = os.path.sep
                target_directory = os.path.abspath(path)
                if self.checkBox_set_source_directory_as_target_directory.isChecked():
                    self.set_target_directory(target_directory)
                    self.get_target_directory_from_directory()

                nessus_files = glob.glob(
                    target_directory
                    + os_separator
                    + "**"
                    + os_separator
                    + "*."
                    + extension[0],
                    recursive=True,
                )
                zip_files = glob.glob(
                    target_directory
                    + os_separator
                    + "**"
                    + os_separator
                    + "*."
                    + extension[1],
                    recursive=True,
                )
                files = nessus_files + zip_files
                for file in files:
                    paths.append(file)

            self.list_of_files_to_pars(paths)

        if len(paths) > 0:
            # Check if any standard report is enabled
            any_standard_report_enabled = (
                self.__report_scan_enabled
                or self.__report_host_enabled
                or self.__report_vulnerabilities_enabled
                or self.__report_noncompliance_enabled
            )
            # Check if any plugin is enabled
            any_plugin_enabled = any(
                self.__parsing_settings.get(self.__plugin_instances[pid].get_enabled_setting_key(), False)
                for pid in self.__plugin_instances
            ) if self.__plugin_instances else False

            if any_standard_report_enabled or any_plugin_enabled:
                self.pushButton_start.setEnabled(True)
                self.actionStart_analysis.setEnabled(True)
        else:
            self.pushButton_start.setDisabled(True)
            self.actionStart_analysis.setDisabled(True)

    def open_target_directory(self):
        """
        Function open target directory taking into account Operating system:
        Linux: Linux
        Mac: Darwin
        Windows: Windows
        """

        os_name = platform.system()
        if os_name == "Darwin":
            subprocess.call(["open", self.__target_directory])
        elif os_name == "Windows":
            subprocess.call(["explorer", self.__target_directory])
        elif os_name == "Linux":
            subprocess.call(["nautilus", self.__target_directory])
        else:
            info = f"Can't open directory, your Operating System {os_name} is not supported. Please report it to us."
            color = "red"
            self.print_log(info, color=color)

    def is_dark_mode(self):
        """
        Detects if the application is running in dark mode.
        """
        app_palette = QApplication.palette()
        bg_color = app_palette.color(QPalette.ColorRole.Window)
        # Check if the background color is dark
        return bg_color.lightness() < 128  # lightness < 128 means dark mode

    def print_log(self, log_value, color):
        """
        Function displays actions information in GUI in Progress preview.

        :param log_value: information to display
        :param color: color for given information
        """
        dark_mode = self.is_dark_mode()

        # Define colors for light and dark mode
        color_map = {
            "black": (0, 0, 0) if not dark_mode else (255, 255, 255),
            "red": (230, 30, 30) if not dark_mode else (255, 100, 100),
            "green": (60, 160, 60) if not dark_mode else (100, 255, 100),
            "blue": (0, 0, 255) if not dark_mode else (100, 180, 255),
            "orange": (255, 165, 0) if not dark_mode else (255, 200, 120),
            "default": (0, 0, 0) if not dark_mode else (255, 255, 255),
        }

        r, g, b = color_map.get(color, color_map["default"])

        log_output = (
            "["
            + datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f")
            + "] "
            + log_value
        )

        html_output = f'''<div style="color: rgb({r}, {g}, {b});">{log_output}</div>'''

        cursor = self.textBrowser_progress.textCursor()
        cursor.movePosition(QTextCursor.End)

        if not self.textBrowser_progress.document().isEmpty():
            cursor.insertBlock()

        block_fmt = cursor.blockFormat()
        block_fmt.setBottomMargin(4.0)
        cursor.setBlockFormat(block_fmt)

        frag = QTextDocumentFragment.fromHtml(html_output)
        cursor.insertFragment(frag)

        self.textBrowser_progress.setTextCursor(cursor)
        self.textBrowser_progress.repaint()

    def exit_application(self):
        """
        Function to exit from application.
        """
        info = "Exit application"
        print(info)
        color = "black"
        self.print_log(info, color=color)
        self.close()

    def list_of_files_to_pars(self, files):
        """
        Function sets private variables:
        __files_to_pars with list of selected files,
        __file_analysis_counter reset to 0
        :param files: list of selected files.
        """
        number_of_files = len(files)
        if number_of_files == 1:
            suffix = ""
        else:
            suffix = "s"
        info = f"Selected {str(number_of_files)} file{suffix}."
        color = "blue"
        self.print_log(info, color=color)
        self.print_status_bar_info(info)
        self.__files_to_pars = files

        for file_to_pars in self.__files_to_pars:
            file_to_pars = nfr.file.nessus_scan_file_name_with_path(file_to_pars)
            color = "black"
            action_name = "info "
            notification_info = f"[action={action_name}] [source_file={file_to_pars}]"
            self.print_log(notification_info, color=color)

            nessus_files_only = []
            if fnmatch.fnmatch(file_to_pars, "*.zip"):
                zip_file_to_pars = zipfile.ZipFile(file_to_pars)
                nessus_file_names = zip_file_to_pars.namelist()
                for nessus_file_name in nessus_file_names:
                    if fnmatch.fnmatch(nessus_file_name, "*.nessus"):
                        # print('nessus file')
                        nessus_files_only.append(nessus_file_name)

                # print(len(nessus_files_only), nessus_files_only)

                if len(nessus_files_only) == 0:
                    color = "red"
                    action_name = "info "
                    notification_info = f"[action={action_name}] [There is no nessus files inside this zip archive.]"
                    self.print_log(notification_info, color=color)
                else:
                    for nessus_file_only in nessus_files_only:
                        color = "black"
                        action_name = "info "
                        notification_info = (
                            f"[action={action_name}] [source_file={nessus_file_only}]"
                        )
                        self.print_log(notification_info, color=color)

        self.__file_analysis_counter = 0


class ParsingThread(QThread):
    signal = pyqtSignal("PyQt_PyObject")
    progress = pyqtSignal(int, int)
    file_analysis_started = pyqtSignal(int)
    print_status_bar_info = pyqtSignal("QString")

    def __init__(
        self,
        files_to_pars,
        target_directory,
        target_directory_changed,
        parsing_settings,
        parent=None,
    ):
        super(ParsingThread, self).__init__(parent)

        self.files_to_pars = files_to_pars
        self.target_directory_changed = target_directory_changed
        self.target_directory = target_directory
        self.parsing_settings = parsing_settings

        self.report_counter = 0
        self.number_of_selected_reports = 0

        # target file name
        self.target_file_name_prefix = self.parsing_settings["target_file_name_prefix"]

        # reports
        self.report_scan_enabled = self.parsing_settings["report_scan_enabled"]
        self.report_host_enabled = self.parsing_settings["report_host_enabled"]
        self.report_vulnerabilities_enabled = self.parsing_settings[
            "report_vulnerabilities_enabled"
        ]
        self.report_noncompliance_enabled = self.parsing_settings[
            "report_noncompliance_enabled"
        ]

        # reports settings:
        self.report_scan_debug_data_enabled = self.parsing_settings[
            "report_scan_debug_data_enabled"
        ]
        self.report_host_debug_data_enabled = self.parsing_settings[
            "report_host_debug_data_enabled"
        ]
        self.report_vulnerabilities_debug_data_enabled = self.parsing_settings[
            "report_vulnerabilities_debug_data_enabled"
        ]
        self.report_vulnerabilities_none_filter_out = self.parsing_settings[
            "report_vulnerabilities_none_filter_out"
        ]
        self.report_vulnerabilities_none_skip = self.parsing_settings[
            "report_vulnerabilities_none_skip"
        ]
        self.report_noncompliance_debug_data_enabled = self.parsing_settings[
            "report_noncompliance_debug_data_enabled"
        ]
        # print(self.report_vulnerabilities_none_filter_out)
        # print(self.report_vulnerabilities_none_skip)

        # plugins
        self.enabled_plugins = []
        self.plugin_data = {}
        self._discover_enabled_plugins()

    def _discover_enabled_plugins(self):
        """Discover and instantiate enabled plugins."""
        try:
            from nessus_file_analyzer.plugins import discover_plugins

            plugin_classes = discover_plugins()
            for plugin_class in plugin_classes:
                plugin_instance = plugin_class()
                setting_key = plugin_instance.get_enabled_setting_key()
                if self.parsing_settings.get(setting_key, False):
                    self.enabled_plugins.append(plugin_instance)
                    metadata = plugin_instance.get_metadata()
                    self.plugin_data[metadata.id] = []
        except Exception as e:
            print(f"Error discovering plugins: {e}")

    def _extract_plugin_data(self, report_item, nfr):
        """
        Extract comprehensive plugin data from a report_item.

        Args:
            report_item: XML report_item element from nessus_file_reader
            nfr: nessus_file_reader module reference

        Returns:
            Dictionary with all available plugin data fields
        """
        # Helper to safely get report_item values
        def get_value(name, default=''):
            value = nfr.plugin.report_item_value(report_item, name)
            return value if value else default

        # Helper to get list values (like CVEs)
        def get_values(name):
            values = nfr.plugin.report_item_values(report_item, name)
            return values if values else []

        # Get raw values
        severity = get_value('severity')
        cvss_base_score = get_value('cvss_base_score')
        cvss3_base_score = get_value('cvss3_base_score')
        cvss4_base_score = get_value('cvss4_base_score')
        vpr_score = get_value('vpr_score')
        epss_score = get_value('epss_score')

        # Apply translation functions to convert numeric values to text labels
        severity_label = nfr.plugin.severity_number_to_label(severity)
        cvss_base_score_label = nfr.plugin.cvssv2_score_to_severity(cvss_base_score)
        cvss3_base_score_label = nfr.plugin.cvssv3_score_to_severity(cvss3_base_score)
        cvss4_base_score_label = nfr.plugin.cvssv4_score_to_severity(cvss4_base_score)
        vpr_score_label = nfr.plugin.vpr_score_to_severity(vpr_score)
        epss_score_percent = nfr.plugin.epss_score_decimal_to_percent(epss_score)

        return {
            # Plugin identification
            'plugin_name': get_value('pluginName'),
            'plugin_family': get_value('pluginFamily'),
            'plugin_type': get_value('plugin_type'),

            # Severity and risk
            'severity': severity,
            'severity_id': int(get_value('severity', '0')),
            'severity_label': severity_label,  # Translated label
            'risk_factor': get_value('risk_factor'),

            # Network details
            'port': get_value('port'),
            'protocol': get_value('protocol'),
            'service': get_value('svc_name'),

            # CVSS v2
            'cvss_base_score': cvss_base_score,
            'cvss_base_score_label': cvss_base_score_label,  # Translated label
            'cvss_vector': get_value('cvss_vector'),
            'cvss_temporal_score': get_value('cvss_temporal_score'),
            'cvss_temporal_vector': get_value('cvss_temporal_vector'),

            # CVSS v3
            'cvss3_base_score': cvss3_base_score,
            'cvss3_base_score_label': cvss3_base_score_label,  # Translated label
            'cvss3_vector': get_value('cvss3_vector'),
            'cvss3_temporal_score': get_value('cvss3_temporal_score'),
            'cvss3_temporal_vector': get_value('cvss3_temporal_vector'),

            # CVSS v4
            'cvss4_base_score': cvss4_base_score,
            'cvss4_base_score_label': cvss4_base_score_label,  # Translated label
            'cvss4_vector': get_value('cvss4_vector'),
            'cvss4_temporal_score': get_value('cvss4_temporal_score'),
            'cvss4_temporal_vector': get_value('cvss4_temporal_vector'),

            # VPR (Vulnerability Priority Rating)
            'vpr_score': vpr_score,
            'vpr_score_label': vpr_score_label,  # Translated label

            # EPSS (Exploit Prediction Scoring System)
            'epss_score': epss_score,
            'epss_score_percent': epss_score_percent,  # Formatted as percentage
            'epss_percentile': get_value('epss_percentile'),

            # References
            'cve': get_values('cve'),
            'bid': get_value('bid'),
            'xref': get_value('xref'),
            'see_also': get_value('see_also'),

            # Content
            'description': get_value('description'),
            'synopsis': get_value('synopsis'),
            'solution': get_value('solution'),
            'plugin_output': get_value('plugin_output'),

            # Exploit information
            'exploit_available': get_value('exploit_available'),
            'exploit_code_maturity': get_value('exploit_code_maturity'),
            'exploitability_ease': get_value('exploitability_ease'),
            'exploit_framework_canvas': get_value('exploit_framework_canvas'),
            'exploit_framework_metasploit': get_value('exploit_framework_metasploit'),
            'exploit_framework_core': get_value('exploit_framework_core'),
            'exploited_by_malware': get_value('exploited_by_malware'),
            'exploited_by_nessus': get_value('exploited_by_nessus'),
            'in_the_news': get_value('in_the_news'),

            # Dates
            'plugin_publication_date': get_value('plugin_publication_date'),
            'plugin_modification_date': get_value('plugin_modification_date'),
            'vuln_publication_date': get_value('vuln_publication_date'),
            'patch_publication_date': get_value('patch_publication_date'),

            # Additional metadata
            'script_version': get_value('script_version'),
            'plugin_version': get_value('plugin_version'),
            'asset_inventory': get_value('asset_inventory'),
            'os_identification': get_value('os_identification'),

            # Compliance
            'compliance': get_value('cm:compliance-result'),
            'compliance_check_name': get_value('cm:compliance-check-name'),
            'compliance_audit_file': get_value('cm:compliance-audit-file'),
            'compliance_check_id': get_value('cm:compliance-check-id'),

            # STIG
            'stig_severity': get_value('stig-severity'),

            # Agent information (for agent scans)
            'fname': get_value('fname'),
            'agent': get_value('agent'),
        }

    def _generate_plugin_reports(self, workbook, list_of_source_files):
        """
        Generate reports for all enabled plugins.

        Args:
            workbook: xlsxwriter.Workbook object
            list_of_source_files: List of .nessus files to process
        """
        # Initialize counters for progress tracking
        file_counter = 0
        total_files = len(list_of_source_files)
        total_hosts_processed = 0

        # Process all files and collect data for plugins
        for scan_file in list_of_source_files:
            file_counter += 1
            source_file_type = ""
            files_to_pars = []

            if fnmatch.fnmatch(scan_file, "*.nessus"):
                files_to_pars.append(scan_file)
                source_file_type = "nessus"
            elif fnmatch.fnmatch(scan_file, "*.zip"):
                source_file_type = "zip"
                zip_source = zipfile.ZipFile(scan_file)
                zip_files_list = zip_source.namelist()
                for zip_file in zip_files_list:
                    if fnmatch.fnmatch(zip_file, "*.nessus"):
                        files_to_pars.append(zip_file)

            for file_to_pars in files_to_pars:
                try:
                    # Get root element
                    if source_file_type == "nessus":
                        root = nfr.file.nessus_scan_file_root_element(file_to_pars)
                    elif source_file_type == "zip":
                        file_to_pars = zip_source.open(file_to_pars)
                        root = nfr.file.nessus_scan_file_root_element(file_to_pars)

                    # Get total hosts for progress tracking
                    number_of_hosts = nfr.scan.number_of_scanned_hosts(root)
                    host_counter = 0

                    # Process each host
                    for report_host in nfr.scan.report_hosts(root):
                        host_counter += 1
                        total_hosts_processed += 1

                        # Emit status bar update during data collection
                        info_report = "Report: 0/" + str(self.number_of_selected_reports)
                        info_file = ", File: " + str(file_counter) + "/" + str(total_files)
                        info_host = ", Host: " + str(host_counter) + "/" + str(number_of_hosts)
                        info_final = info_report + info_file + info_host
                        self.print_status_bar_info.emit(info_final)

                        # Emit progress bar update during data collection
                        self.progress.emit(host_counter, number_of_hosts)

                        # Build host_data dictionary for plugins
                        # Get netbios info (returns dict with computer_name and domain_name)
                        netbios_info = nfr.host.netbios_network_name(root, report_host) or {}
                        netbios_name = netbios_info.get('netbios_computer_name', '') or ""

                        host_data = {
                            'report_host_name': nfr.host.report_host_name(report_host) or "",
                            'resolved_hostname': nfr.host.resolved_hostname(report_host) or "",
                            'hostname': nfr.host.resolved_hostname(report_host) or nfr.host.report_host_name(report_host) or "",  # Kept for backward compatibility
                            'ip': nfr.host.resolved_ip(report_host) or "",
                            'os': nfr.host.detected_os(report_host) or "",
                            'mac_address': nfr.host.host_property_value(report_host, 'mac-address') or "",
                            'netbios_name': netbios_name,
                            'fqdn': nfr.host.resolved_fqdn(report_host) or "",
                            'scan_start': nfr.host.host_property_value(report_host, 'HOST_START') or "",
                            'scan_end': nfr.host.host_property_value(report_host, 'HOST_END') or "",
                            'scan_start_time': nfr.host.host_time_start(report_host),  # Datetime object
                            'scan_end_time': nfr.host.host_time_end(report_host),  # Datetime object
                            'credentialed_checks': nfr.host.credentialed_checks(root, report_host) or "",
                            'plugins': []  # Changed from dict to list to support multiple occurrences
                        }

                        # Determine which plugin IDs to collect across all enabled plugins
                        collect_all_plugins = False
                        specific_plugin_ids = set()

                        for plugin in self.enabled_plugins:
                            metadata = plugin.get_metadata()
                            if metadata.plugin_ids is None:
                                collect_all_plugins = True
                                break  # If any plugin needs all, collect all
                            else:
                                specific_plugin_ids.update(metadata.plugin_ids)

                        # Collect plugin data once for all enabled plugins
                        if collect_all_plugins:
                            # Collect all report_items for this host
                            for report_item in nfr.host.report_items(report_host):
                                plugin_id = int(nfr.plugin.report_item_value(report_item, 'pluginID'))
                                plugin_data = self._extract_plugin_data(report_item, nfr)
                                plugin_data['plugin_id'] = plugin_id
                                host_data['plugins'].append(plugin_data)
                        else:
                            # Collect only specified plugin_ids (all occurrences)
                            for report_item in nfr.host.report_items(report_host):
                                item_plugin_id = int(nfr.plugin.report_item_value(report_item, 'pluginID'))
                                if item_plugin_id in specific_plugin_ids:
                                    plugin_data = self._extract_plugin_data(report_item, nfr)
                                    plugin_data['plugin_id'] = item_plugin_id
                                    host_data['plugins'].append(plugin_data)

                        # Let each plugin process this host's data
                        for plugin in self.enabled_plugins:
                            try:
                                metadata = plugin.get_metadata()
                                processed_data = plugin.process_host_data(host_data)
                                if processed_data:
                                    self.plugin_data[metadata.id].append(processed_data)
                            except Exception as e:
                                error_msg = f"Error processing host {host_data.get('hostname', '')} with plugin {metadata.name}: {e}"
                                print(error_msg)
                                self.signal.emit(error_msg)

                except Exception as e:
                    error_msg = f"Error processing file {file_to_pars}: {e}"
                    print(error_msg)
                    self.signal.emit(error_msg)

        # Generate reports for each plugin
        for plugin in self.enabled_plugins:
            try:
                metadata = plugin.get_metadata()
                processed_data = self.plugin_data[metadata.id]

                # Increment report counter and emit status bar message
                self.report_counter += 1
                info_report = (
                    "Report: "
                    + str(self.report_counter)
                    + "/"
                    + str(self.number_of_selected_reports)
                )
                info_file = ", File: " + str(total_files) + "/" + str(total_files)
                info_host = ", Host: " + str(total_hosts_processed) + "/" + str(total_hosts_processed)
                info_final = info_report + info_file + info_host

                if processed_data:
                    info_msg = f"Generating report for {metadata.name} ({len(processed_data)} hosts)"
                    print(info_msg)
                    self.signal.emit(info_msg)
                    self.print_status_bar_info.emit(info_final)

                    # Emit progress bar update during report generation (show final counts)
                    self.progress.emit(total_hosts_processed, total_hosts_processed)

                    # Debug: Show what data we have
                    # debug_msg = f"DEBUG: Plugin {metadata.name} has {len(processed_data)} host records"
                    # print(debug_msg)
                    # self.signal.emit(debug_msg)

                    # if processed_data:
                    #     first_host = processed_data[0]
                    #     debug_msg2 = f"DEBUG: First host keys: {list(first_host.keys())}"
                    #     print(debug_msg2)
                    #     self.signal.emit(debug_msg2)
                    #     if 'software' in first_host:
                    #         debug_msg3 = f"DEBUG: First host has {len(first_host['software'])} software items"
                    #         print(debug_msg3)
                    #         self.signal.emit(debug_msg3)

                    plugin.generate_report(workbook, processed_data, self.parsing_settings)

                    success_msg = f"Plugin report '{metadata.name}' generated successfully"
                    print(success_msg)
                    self.signal.emit(success_msg)
                else:
                    info_msg = f"No data found for plugin {metadata.name}"
                    print(info_msg)
                    self.signal.emit(info_msg)
                    self.print_status_bar_info.emit(info_final)

                    # Emit progress bar update even when no data found
                    self.progress.emit(total_hosts_processed, total_hosts_processed)

            except Exception as e:
                error_msg = f"Error generating report for plugin {metadata.name}: {e}"
                print(error_msg)
                self.signal.emit(error_msg)
                import traceback
                traceback.print_exc()

    def run(self):
        files_to_pars = self.files_to_pars
        target_directory = self.target_directory

        target_file_name_prefix = self.target_file_name_prefix

        suffix = self.parsing_settings["suffix"]

        target_file_name = target_file_name_prefix + suffix + ".xlsx"
        final_path_to_save = target_directory + "/" + target_file_name
        workbook = xlsxwriter.Workbook(final_path_to_save, {"constant_memory": True})

        workbook.use_zip64()

        self.set_worksheet_properties(workbook)

        # Count enabled plugins to include in total report count
        number_of_enabled_plugins = len(self.enabled_plugins)

        if (
            self.report_scan_enabled
            and not self.report_host_enabled
            and not self.report_vulnerabilities_enabled
            and not self.report_noncompliance_enabled
        ):
            self.number_of_selected_reports = 1 + number_of_enabled_plugins
            self.create_worksheet_for_scans(workbook, files_to_pars)
        elif (
            not self.report_scan_enabled
            and self.report_host_enabled
            and not self.report_vulnerabilities_enabled
            and not self.report_noncompliance_enabled
        ):
            self.number_of_selected_reports = 1 + number_of_enabled_plugins
            self.create_worksheet_for_hosts(workbook, files_to_pars)
        elif (
            not self.report_scan_enabled
            and not self.report_host_enabled
            and self.report_vulnerabilities_enabled
            and not self.report_noncompliance_enabled
        ):
            self.number_of_selected_reports = 1 + number_of_enabled_plugins
            self.create_worksheet_for_vulnerabilities(workbook, files_to_pars)
        elif (
            not self.report_scan_enabled
            and not self.report_host_enabled
            and not self.report_vulnerabilities_enabled
            and self.report_noncompliance_enabled
        ):
            self.number_of_selected_reports = 1 + number_of_enabled_plugins
            self.create_worksheet_for_noncompliance(workbook, files_to_pars)
        elif (
            self.report_scan_enabled
            and self.report_host_enabled
            and not self.report_vulnerabilities_enabled
            and not self.report_noncompliance_enabled
        ):
            self.number_of_selected_reports = 2 + number_of_enabled_plugins
            self.create_worksheet_for_scans(workbook, files_to_pars)
            self.create_worksheet_for_hosts(workbook, files_to_pars)
        elif (
            self.report_scan_enabled
            and not self.report_host_enabled
            and self.report_vulnerabilities_enabled
            and not self.report_noncompliance_enabled
        ):
            self.number_of_selected_reports = 2 + number_of_enabled_plugins
            self.create_worksheet_for_scans(workbook, files_to_pars)
            self.create_worksheet_for_vulnerabilities(workbook, files_to_pars)
        elif (
            self.report_scan_enabled
            and not self.report_host_enabled
            and not self.report_vulnerabilities_enabled
            and self.report_noncompliance_enabled
        ):
            self.number_of_selected_reports = 2 + number_of_enabled_plugins
            self.create_worksheet_for_scans(workbook, files_to_pars)
            self.create_worksheet_for_noncompliance(workbook, files_to_pars)
        elif (
            not self.report_scan_enabled
            and self.report_host_enabled
            and self.report_vulnerabilities_enabled
            and not self.report_noncompliance_enabled
        ):
            self.number_of_selected_reports = 2 + number_of_enabled_plugins
            self.create_worksheet_for_hosts(workbook, files_to_pars)
            self.create_worksheet_for_vulnerabilities(workbook, files_to_pars)
        elif (
            not self.report_scan_enabled
            and self.report_host_enabled
            and not self.report_vulnerabilities_enabled
            and self.report_noncompliance_enabled
        ):
            self.number_of_selected_reports = 2 + number_of_enabled_plugins
            self.create_worksheet_for_hosts(workbook, files_to_pars)
            self.create_worksheet_for_noncompliance(workbook, files_to_pars)
        elif (
            not self.report_scan_enabled
            and not self.report_host_enabled
            and self.report_vulnerabilities_enabled
            and self.report_noncompliance_enabled
        ):
            self.number_of_selected_reports = 2 + number_of_enabled_plugins
            self.create_worksheet_for_vulnerabilities(workbook, files_to_pars)
            self.create_worksheet_for_noncompliance(workbook, files_to_pars)
        elif (
            self.report_scan_enabled
            and self.report_host_enabled
            and self.report_vulnerabilities_enabled
            and not self.report_noncompliance_enabled
        ):
            self.number_of_selected_reports = 3 + number_of_enabled_plugins
            self.create_worksheet_for_scans(workbook, files_to_pars)
            self.create_worksheet_for_hosts(workbook, files_to_pars)
            self.create_worksheet_for_vulnerabilities(workbook, files_to_pars)
        elif (
            self.report_scan_enabled
            and self.report_host_enabled
            and not self.report_vulnerabilities_enabled
            and self.report_noncompliance_enabled
        ):
            self.number_of_selected_reports = 3 + number_of_enabled_plugins
            self.create_worksheet_for_scans(workbook, files_to_pars)
            self.create_worksheet_for_hosts(workbook, files_to_pars)
            self.create_worksheet_for_noncompliance(workbook, files_to_pars)
        elif (
            self.report_scan_enabled
            and not self.report_host_enabled
            and self.report_vulnerabilities_enabled
            and self.report_noncompliance_enabled
        ):
            self.number_of_selected_reports = 3 + number_of_enabled_plugins
            self.create_worksheet_for_scans(workbook, files_to_pars)
            self.create_worksheet_for_vulnerabilities(workbook, files_to_pars)
            self.create_worksheet_for_noncompliance(workbook, files_to_pars)
        elif (
            not self.report_scan_enabled
            and self.report_host_enabled
            and self.report_vulnerabilities_enabled
            and self.report_noncompliance_enabled
        ):
            self.number_of_selected_reports = 3 + number_of_enabled_plugins
            self.create_worksheet_for_hosts(workbook, files_to_pars)
            self.create_worksheet_for_vulnerabilities(workbook, files_to_pars)
            self.create_worksheet_for_noncompliance(workbook, files_to_pars)
        elif (
            self.report_scan_enabled
            and self.report_host_enabled
            and self.report_vulnerabilities_enabled
            and self.report_noncompliance_enabled
        ):
            self.number_of_selected_reports = 4 + number_of_enabled_plugins
            self.create_worksheet_for_scans(workbook, files_to_pars)
            self.create_worksheet_for_hosts(workbook, files_to_pars)
            self.create_worksheet_for_vulnerabilities(workbook, files_to_pars)
            self.create_worksheet_for_noncompliance(workbook, files_to_pars)

        else:
            # Only plugins are enabled (no standard reports)
            if self.enabled_plugins:
                self.number_of_selected_reports = number_of_enabled_plugins
            else:
                # No reports or plugins enabled
                info = (
                    "You did not choose any report type to generate. Select at least one."
                )
                print(info)

        # Generate plugin reports
        if self.enabled_plugins:
            self._generate_plugin_reports(workbook, files_to_pars)

        try:
            # info_msg = "DEBUG: Closing workbook..."
            # print(info_msg)
            # self.signal.emit(info_msg)
            workbook.close()
            # info_msg = "DEBUG: Workbook closed successfully"
            # print(info_msg)
            # self.signal.emit(info_msg)
        except Exception as e:
            error_msg = f"ERROR: Failed to close workbook: {e}"
            print(error_msg)
            self.signal.emit(error_msg)
            import traceback
            traceback.print_exc()

    def set_worksheet_properties(self, workbook):

        workbook.set_properties(
            {
                "title": "Security report",
                "subject": "Vulnerability Assessment results",
                "category": "Report",
                "keywords": "Vulnerabilities, VA, VM, Nessus",
                "comments": "Report generated with nessus file analyzer (NFA) by LimberDuck. "
                "Check https://limberduck.org for more details.",
            }
        )

    def create_worksheet_for_scans(self, workbook, list_of_source_files):
        """
        Function create spreadsheet with scan sum-up in given workbook for selected files.
        :param workbook: workbook where spreadsheet are created
        :param list_of_source_files: list of selected files
        """
        report_name = "scans"
        self.report_counter += 1
        info_report = (
            "Report: "
            + str(self.report_counter)
            + "/"
            + str(self.number_of_selected_reports)
        )

        worksheet = workbook.add_worksheet(report_name)

        cell_format_bold = workbook.add_format({"bold": True})
        worksheet.set_row(0, None, cell_format_bold)

        # print('>>>>>>>>>>>>>>>> ', self.report_scan_debug_data_enabled)

        if not self.report_scan_debug_data_enabled:
            headers = [
                "Target hosts",
                "Target hosts (without duplicates)",
                "Scanned hosts",
                "Scanned hosts with credentialed checks",
                "Unreachable hosts",
                "Scan started",
                "Scan ended",
                "Elapsed time per scan",
                "Login used",
                "DB SID",
                "DB port",
                "ALL plugins",
                "Critical plugins",
                "High plugins",
                "Medium plugins",
                "Low plugins",
                "None plugins",
                "ALL compliance",
                "Passed compliance",
                "Failed compliance",
                "Warning compliance",
            ]
        else:
            headers = [
                "Nessus scan name",
                "Nessus file name",
                "nessus file size",
                "Target hosts",
                "Target hosts (without duplicates)",
                "Scanned hosts",
                "Scanned hosts with credentialed checks",
                "Unreachable hosts",
                "Scan started",
                "Scan ended",
                "Elapsed time per scan",
                "Policy name",
                "Login used",
                "DB SID",
                "DB port",
                "Reverse lookup",
                "Max hosts",
                "Max checks",
                "Network timeout",
                "Used plugins",
                "ALL plugins",
                "Critical plugins",
                "High plugins",
                "Medium plugins",
                "Low plugins",
                "None plugins",
                "ALL compliance",
                "Passed compliance",
                "Failed compliance",
                "Warning compliance",
            ]

        number_of_columns = len(headers)
        # print('Number of columns: ' + str(number_of_columns))

        worksheet.set_column(0, number_of_columns - 1, 18)

        debug_columns_list = [0, 1, 2, 11, 15, 16, 17, 18, 19]

        for column_index, header in enumerate(headers):
            if (
                self.report_scan_debug_data_enabled
                and column_index in debug_columns_list
            ):
                cell_format_bold_blue = workbook.add_format()
                cell_format_bold_blue.set_bold()
                cell_format_bold_blue.set_font_color("blue")
                worksheet.write(0, column_index, header, cell_format_bold_blue)
            else:
                worksheet.write(0, column_index, header)

        worksheet.freeze_panes(1, 0)  # Freeze the first row.

        nessus_scan_file_number = 0
        number_of_files_with_errors = 0

        number_of_rows = 0
        row_index = 0
        for scan_file in list_of_source_files:
            source_file_type = ""
            files_to_pars = []
            if fnmatch.fnmatch(scan_file, "*.nessus"):
                files_to_pars.append(scan_file)
                source_file_type = "nessus"
            elif fnmatch.fnmatch(scan_file, "*.zip"):
                source_file_type = "zip"
                zip_source = zipfile.ZipFile(scan_file)
                zip_files_list = zip_source.namelist()
                for zip_file in zip_files_list:
                    if fnmatch.fnmatch(zip_file, "*.nessus"):
                        files_to_pars.append(zip_file)

            print(files_to_pars)
            for file_to_pars in files_to_pars:
                # print(row_index, number_of_rows, nessus_scan_file_number)
                row_index += 1
                number_of_rows += 1
                nessus_scan_file_number += 1

                info_file = (
                    ", File: " + str(row_index) + "/" + str(len(list_of_source_files))
                )

                try:
                    file_to_pars_full_name = ""
                    source_file_size = 0
                    if source_file_type == "nessus":
                        root = nfr.file.nessus_scan_file_root_element(file_to_pars)
                        scan_file_source = nfr.scan.scan_file_source(root)
                        file_to_pars_full_name = (
                            nfr.file.nessus_scan_file_name_with_path(file_to_pars)
                        )
                        source_file_size = nfa.utilities.size_of_file_human(
                            file_to_pars_full_name
                        )
                    elif source_file_type == "zip":
                        source_file_size = nfa.utilities.size_of_file_inside_zip_human(
                            zip_source, file_to_pars
                        )
                        file_to_pars_full_name = (
                            f"{nfr.file.nessus_scan_file_name_with_path(scan_file)} "
                            f"[{zip_source.getinfo(file_to_pars).filename}]"
                        )
                        file_to_pars = zip_source.open(file_to_pars)
                        root = nfr.file.nessus_scan_file_root_element(file_to_pars)
                        scan_file_source = nfr.scan.scan_file_source(root)

                    start_time = time.time()
                    self.log_emitter("start", file_to_pars_full_name)
                    self.file_analysis_started.emit(1)

                    self.log_emitter(
                        "info ",
                        file_to_pars_full_name,
                        f"[scan_file_source={scan_file_source}]",
                    )
                    self.log_emitter(
                        "info ",
                        file_to_pars_full_name,
                        f"[source_file_size={source_file_size}]",
                    )
                    self.log_emitter(
                        "info ", file_to_pars_full_name, f"[report_type={report_name}]"
                    )
                    self.log_emitter(
                        "info ",
                        file_to_pars_full_name,
                        f"[number_of_target_hosts={nfr.scan.number_of_target_hosts(root)}]",
                    )
                    self.log_emitter(
                        "info ",
                        file_to_pars_full_name,
                        f"[number_of_scanned_hosts={nfr.scan.number_of_scanned_hosts(root)}]",
                    )
                    self.log_emitter(
                        "info ",
                        file_to_pars_full_name,
                        f"[number_of_scanned_hosts_with_credentials="
                        f"{nfr.scan.number_of_scanned_hosts_with_credentialed_checks_yes(root)}]",
                    )

                    date_format = workbook.add_format(
                        {"num_format": "yyyy-mm-dd hh:mm:ss"}
                    )

                    login_used = None
                    number_of_plugins = 0
                    number_of_plugins_critical = 0
                    number_of_plugins_high = 0
                    number_of_plugins_medium = 0
                    number_of_plugins_low = 0
                    number_of_plugins_none = 0
                    number_of_compliance_plugins = 0
                    number_of_compliance_plugins_passed = 0
                    number_of_compliance_plugins_failed = 0
                    number_of_compliance_plugins_warning = 0

                    policy_max_hosts = nfr.scan.policy_max_hosts(root)
                    policy_max_checks = nfr.scan.policy_max_checks(root)
                    policy_checks_read_timeout = nfr.scan.policy_checks_read_timeout(
                        root
                    )

                    report_host_counter = 0
                    number_of_report_hosts = nfr.scan.number_of_scanned_hosts(root)

                    info_host = (
                        ", Host: "
                        + str(report_host_counter)
                        + "/"
                        + str(number_of_report_hosts)
                    )
                    info_final = info_report + info_file + info_host
                    self.print_status_bar_info.emit(info_final)

                    for report_host in nfr.scan.report_hosts(root):
                        report_host_counter += 1
                        self.progress.emit(report_host_counter, number_of_report_hosts)

                        info_host = (
                            ", Host: "
                            + str(report_host_counter)
                            + "/"
                            + str(number_of_report_hosts)
                        )
                        info_final = info_report + info_file + info_host
                        self.print_status_bar_info.emit(info_final)

                        login_used = nfr.host.login_used(report_host)

                        number_of_plugins += nfr.host.number_of_plugins(report_host)
                        number_of_plugins_critical += (
                            nfr.host.number_of_plugins_per_risk_factor(
                                report_host, "Critical"
                            )
                        )
                        number_of_plugins_high += (
                            nfr.host.number_of_plugins_per_risk_factor(
                                report_host, "High"
                            )
                        )
                        number_of_plugins_medium += (
                            nfr.host.number_of_plugins_per_risk_factor(
                                report_host, "Medium"
                            )
                        )
                        number_of_plugins_low += (
                            nfr.host.number_of_plugins_per_risk_factor(
                                report_host, "Low"
                            )
                        )
                        number_of_plugins_none += (
                            nfr.host.number_of_plugins_per_risk_factor(
                                report_host, "None"
                            )
                        )
                        number_of_compliance_plugins += (
                            nfr.host.number_of_compliance_plugins(report_host)
                        )
                        number_of_compliance_plugins_passed += (
                            nfr.host.number_of_compliance_plugins_per_result(
                                report_host, "PASSED"
                            )
                        )
                        number_of_compliance_plugins_failed += (
                            nfr.host.number_of_compliance_plugins_per_result(
                                report_host, "FAILED"
                            )
                        )
                        number_of_compliance_plugins_warning += (
                            nfr.host.number_of_compliance_plugins_per_result(
                                report_host, "WARNING"
                            )
                        )

                    if not self.report_scan_debug_data_enabled:
                        worksheet.write(
                            row_index, 0, nfr.scan.number_of_target_hosts(root)
                        )
                        worksheet.write(
                            row_index,
                            1,
                            nfr.scan.number_of_target_hosts_without_duplicates(root),
                        )
                        worksheet.write(
                            row_index, 2, nfr.scan.number_of_scanned_hosts(root)
                        )
                        worksheet.write(
                            row_index,
                            3,
                            nfr.scan.number_of_scanned_hosts_with_credentialed_checks_yes(
                                root
                            ),
                        )
                        worksheet.write(
                            row_index, 4, nfr.scan.number_of_not_scanned_hosts(root)
                        )

                        scan_start_time = nfr.scan.scan_time_start(root)
                        if scan_start_time is not None:
                            worksheet.write_datetime(
                                row_index, 5, scan_start_time, date_format
                            )
                        else:
                            worksheet.write_blank(row_index, 5, None)

                        scan_end_time = nfr.scan.scan_time_end(root)
                        if scan_end_time is not None:
                            worksheet.write_datetime(
                                row_index, 6, scan_end_time, date_format
                            )
                        else:
                            worksheet.write_blank(row_index, 6, None)

                        worksheet.write(row_index, 7, nfr.scan.scan_time_elapsed(root))
                        worksheet.write(row_index, 8, login_used)
                        worksheet.write(row_index, 9, nfr.scan.policy_db_sid(root))
                        worksheet.write(row_index, 10, nfr.scan.policy_db_port(root))
                        worksheet.write(row_index, 11, number_of_plugins)
                        worksheet.write(row_index, 12, number_of_plugins_critical)
                        worksheet.write(row_index, 13, number_of_plugins_high)
                        worksheet.write(row_index, 14, number_of_plugins_medium)
                        worksheet.write(row_index, 15, number_of_plugins_low)
                        worksheet.write(row_index, 16, number_of_plugins_none)
                        worksheet.write(row_index, 17, number_of_compliance_plugins)
                        worksheet.write(
                            row_index, 18, number_of_compliance_plugins_passed
                        )
                        worksheet.write(
                            row_index, 19, number_of_compliance_plugins_failed
                        )
                        worksheet.write(
                            row_index, 20, number_of_compliance_plugins_warning
                        )
                    else:
                        worksheet.write(row_index, 0, nfr.scan.report_name(root))
                        worksheet.write(row_index, 1, file_to_pars_full_name)
                        worksheet.write(row_index, 2, source_file_size)
                        worksheet.write(
                            row_index, 3, nfr.scan.number_of_target_hosts(root)
                        )
                        worksheet.write(
                            row_index,
                            4,
                            nfr.scan.number_of_target_hosts_without_duplicates(root),
                        )
                        worksheet.write(
                            row_index, 5, nfr.scan.number_of_scanned_hosts(root)
                        )
                        worksheet.write(
                            row_index,
                            6,
                            nfr.scan.number_of_scanned_hosts_with_credentialed_checks_yes(
                                root
                            ),
                        )
                        worksheet.write(
                            row_index, 7, nfr.scan.number_of_not_scanned_hosts(root)
                        )

                        scan_start_time = nfr.scan.scan_time_start(root)
                        if scan_start_time is not None:
                            worksheet.write_datetime(
                                row_index, 8, scan_start_time, date_format
                            )
                        else:
                            worksheet.write_blank(row_index, 8, None)

                        scan_end_time = nfr.scan.scan_time_end(root)
                        if scan_end_time is not None:
                            worksheet.write_datetime(
                                row_index, 9, scan_end_time, date_format
                            )
                        else:
                            worksheet.write_blank(row_index, 9, None)

                        worksheet.write(row_index, 10, nfr.scan.scan_time_elapsed(root))
                        worksheet.write(row_index, 11, nfr.scan.policy_name(root))
                        worksheet.write(row_index, 12, login_used)
                        worksheet.write(row_index, 13, nfr.scan.policy_db_sid(root))
                        worksheet.write(row_index, 14, nfr.scan.policy_db_port(root))
                        worksheet.write(row_index, 15, nfr.scan.reverse_lookup(root))
                        worksheet.write(row_index, 16, policy_max_hosts)
                        worksheet.write(row_index, 17, policy_max_checks)
                        worksheet.write(row_index, 18, policy_checks_read_timeout)
                        worksheet.write(row_index, 19, nfr.scan.plugin_set_number(root))
                        worksheet.write(row_index, 20, number_of_plugins)
                        worksheet.write(row_index, 21, number_of_plugins_critical)
                        worksheet.write(row_index, 22, number_of_plugins_high)
                        worksheet.write(row_index, 23, number_of_plugins_medium)
                        worksheet.write(row_index, 24, number_of_plugins_low)
                        worksheet.write(row_index, 25, number_of_plugins_none)
                        worksheet.write(row_index, 26, number_of_compliance_plugins)
                        worksheet.write(
                            row_index, 27, number_of_compliance_plugins_passed
                        )
                        worksheet.write(
                            row_index, 28, number_of_compliance_plugins_failed
                        )
                        worksheet.write(
                            row_index, 29, number_of_compliance_plugins_warning
                        )

                    end_time = time.time()
                    elapsed_time = end_time - start_time
                    elapsed_time_parsed = time.strftime(
                        "%H:%M:%S", time.gmtime(elapsed_time)
                    )

                    self.log_emitter(
                        "info ",
                        file_to_pars_full_name,
                        f"[elapsed_time={elapsed_time_parsed}]",
                    )
                    self.log_emitter("end  ", file_to_pars_full_name)

                except Exception as e:
                    number_of_files_with_errors += 1
                    traceback.print_exc()
                    self.log_emitter(
                        "info",
                        "ERROR Parsing ["
                        + str(nessus_scan_file_number)
                        + "/"
                        + str(len(list_of_source_files))
                        + "] nessus files",
                    )
                    self.log_emitter("info", e)

        if number_of_rows > 0:
            worksheet.autofilter(0, 0, number_of_rows, number_of_columns - 1)

    def create_worksheet_for_hosts(self, workbook, list_of_source_files):
        """
        Function create spreadsheet with host sum-up in given workbook for selected files.
        :param workbook: workbook where spreadsheet are created
        :param list_of_source_files: list of selected files
        """
        report_name = "hosts"
        self.report_counter += 1
        info_report = (
            "Report: "
            + str(self.report_counter)
            + "/"
            + str(self.number_of_selected_reports)
        )

        worksheet = workbook.add_worksheet(report_name)

        cell_format_bold = workbook.add_format({"bold": True})
        worksheet.set_row(0, None, cell_format_bold)

        # print('>>>>>>>>>>>>>>>> ', self.report_host_debug_data_enabled)

        if not self.report_host_debug_data_enabled:
            headers = [
                # "Nessus scanner IP",
                # "Nessus scan name",
                # "Nessus file name",
                "Target",
                "Hostname",
                "FQDN",
                # "NetBIOS Computer name",
                # "NetBIOS Domain name",
                "IP",
                "Scanned",
                "Credentialed checks",
                "Scan started",
                "Scan ended",
                "Elapsed time per host",
                "Elapsed time per scan",
                # "Policy name",
                "Login used",
                "DB SID",
                "DB port",
                # "Max hosts",
                # "Max checks",
                # "Network timeout",
                "Operating System",
                "ALL plugins",
                "Critical plugins",
                "High plugins",
                "Medium plugins",
                "Low plugins",
                "None plugins",
                "ALL compliance",
                "Passed compliance",
                "Failed compliance",
                "Warning compliance",
                # "10180: Ping to remote host",
                # "10287: Traceroute Information",
                # "11936: OS Identification",
                # "45590: Common Platform Enumeration (CPE)",
                # "54615: Device Type",
                # "21745: Authentication Failure - Local Checks Not Run",
                # "12634: Authenticated Check : OS Name and Installed Package Enumeration",
                # "110385: Authentication Success Insufficient Access",
                # "102094: SSH Commands Require Privilege Escalation",
                # "10394: Microsoft Windows SMB Log In Possible",
                # "24786: Nessus Windows Scan Not Performed with Admin Privileges",
                # "24269: Windows Management Instrumentation (WMI) Available",
                # "11011: Microsoft Windows SMB Service Detection",
                # "10400: Microsoft Windows SMB Registry Remotely Accessible",
                # "26917: Microsoft Windows SMB Registry : Nessus Cannot Access the Windows Registry",
                # "42897: SMB Registry : Start the Registry Service during the scan (WMI)",
                # "20811: Microsoft Windows Installed Software Enumeration (credentialed check)",
                # "91825: Oracle DB Login Possible",
                # "91827: Microsoft SQL Server Login Possible",
                # "47864: Cisco IOS Version",
                # "67217: Cisco IOS XE Version"
            ]
        else:
            headers = [
                "Nessus scanner IP",
                "Nessus scan name",
                "Nessus file name",
                "Target",
                "Hostname",
                "FQDN",
                "NetBIOS Computer name",
                "NetBIOS Domain name",
                "IP",
                "Scanned",
                "Credentialed checks",
                "Scan started",
                "Scan ended",
                "Elapsed time per host",
                "Elapsed time per scan",
                "Policy name",
                "Login used",
                "DB SID",
                "DB port",
                "Reverse lookup",
                "Max hosts",
                "Max checks",
                "Network timeout",
                "Operating System",
                "ALL plugins",
                "Critical plugins",
                "High plugins",
                "Medium plugins",
                "Low plugins",
                "None plugins",
                "ALL compliance",
                "Passed compliance",
                "Failed compliance",
                "Warning compliance",
                "10180: Ping to remote host",
                "10287: Traceroute Information",
                "11936: OS Identification",
                "45590: Common Platform Enumeration (CPE)",
                "54615: Device Type",
                "21745: Authentication Failure - Local Checks Not Run",
                "12634: Authenticated Check : OS Name and Installed Package Enumeration",
                "110385: Authentication Success Insufficient Access",
                "102094: SSH Commands Require Privilege Escalation",
                "10394: Microsoft Windows SMB Log In Possible",
                "24786: Nessus Windows Scan Not Performed with Admin Privileges",
                "24269: Windows Management Instrumentation (WMI) Available",
                "11011: Microsoft Windows SMB Service Detection",
                "10400: Microsoft Windows SMB Registry Remotely Accessible",
                "26917: Microsoft Windows SMB Registry : Nessus Cannot Access the Windows Registry",
                "42897: SMB Registry : Start the Registry Service during the scan (WMI)",
                "20811: Microsoft Windows Installed Software Enumeration (credentialed check)",
                "91825: Oracle DB Login Possible",
                "91827: Microsoft SQL Server Login Possible",
                "47864: Cisco IOS Version",
                "67217: Cisco IOS XE Version",
            ]
        number_of_columns = len(headers)
        # print('Number of columns: ' + str(number_of_columns))

        worksheet.set_column(0, number_of_columns - 1, 18)

        debug_columns_list = [
            0,
            1,
            2,
            6,
            7,
            13,
            17,
            18,
            19,
            20,
            32,
            33,
            34,
            35,
            36,
            37,
            38,
            39,
            40,
            41,
            42,
            43,
            44,
            45,
            46,
            47,
            48,
            49,
            50,
            51,
            52,
        ]

        for column_index, header in enumerate(headers):
            if (
                self.report_host_debug_data_enabled
                and column_index in debug_columns_list
            ):
                cell_format_bold_blue = workbook.add_format()
                cell_format_bold_blue.set_bold()
                cell_format_bold_blue.set_font_color("blue")
                worksheet.write(0, column_index, header, cell_format_bold_blue)
            else:
                worksheet.write(0, column_index, header)

        worksheet.freeze_panes(1, 0)  # Freeze the first row.

        nessus_scan_file_number = 0
        number_of_files_with_errors = 0

        number_of_rows = 0
        row_index = 0
        row_index_per_file = 0
        for scan_file in list_of_source_files:
            source_file_type = ""
            files_to_pars = []
            if fnmatch.fnmatch(scan_file, "*.nessus"):
                files_to_pars.append(scan_file)
                source_file_type = "nessus"
            elif fnmatch.fnmatch(scan_file, "*.zip"):
                source_file_type = "zip"
                zip_source = zipfile.ZipFile(scan_file)
                zip_files_list = zip_source.namelist()
                for zip_file in zip_files_list:
                    if fnmatch.fnmatch(zip_file, "*.nessus"):
                        files_to_pars.append(zip_file)

            for file_to_pars in files_to_pars:
                row_index_per_file += 1
                nessus_scan_file_number += 1
                info_file = (
                    ", File: "
                    + str(row_index_per_file)
                    + "/"
                    + str(len(list_of_source_files))
                )

                try:
                    file_to_pars_full_name = ""
                    source_file_size = 0
                    if source_file_type == "nessus":
                        root = nfr.file.nessus_scan_file_root_element(file_to_pars)
                        scan_file_source = nfr.scan.scan_file_source(root)
                        file_to_pars_full_name = (
                            nfr.file.nessus_scan_file_name_with_path(file_to_pars)
                        )
                        source_file_size = nfa.utilities.size_of_file_human(
                            file_to_pars_full_name
                        )
                    elif source_file_type == "zip":
                        source_file_size = nfa.utilities.size_of_file_inside_zip_human(
                            zip_source, file_to_pars
                        )
                        file_to_pars_full_name = (
                            f"{nfr.file.nessus_scan_file_name_with_path(scan_file)} "
                            f"[{zip_source.getinfo(file_to_pars).filename}]"
                        )
                        file_to_pars = zip_source.open(file_to_pars)
                        root = nfr.file.nessus_scan_file_root_element(file_to_pars)
                        scan_file_source = nfr.scan.scan_file_source(root)

                    start_time = time.time()
                    self.log_emitter("start", file_to_pars_full_name)
                    self.file_analysis_started.emit(1)

                    self.log_emitter(
                        "info ",
                        file_to_pars_full_name,
                        f"[scan_file_source={scan_file_source}]",
                    )
                    self.log_emitter(
                        "info ",
                        file_to_pars_full_name,
                        f"[source_file_size={source_file_size}]",
                    )
                    self.log_emitter(
                        "info ", file_to_pars_full_name, f"[report_type={report_name}]"
                    )
                    self.log_emitter(
                        "info ",
                        file_to_pars_full_name,
                        f"[number_of_target_hosts={nfr.scan.number_of_target_hosts(root)}]",
                    )
                    self.log_emitter(
                        "info ",
                        file_to_pars_full_name,
                        f"[number_of_scanned_hosts={nfr.scan.number_of_scanned_hosts(root)}]",
                    )
                    self.log_emitter(
                        "info ",
                        file_to_pars_full_name,
                        f"[number_of_scanned_hosts_with_credentials="
                        f"{nfr.scan.number_of_scanned_hosts_with_credentialed_checks_yes(root)}]",
                    )

                    date_format = workbook.add_format(
                        {"num_format": "yyyy-mm-dd hh:mm:ss"}
                    )
                    policy_max_hosts = nfr.scan.policy_max_hosts(root)
                    policy_max_checks = nfr.scan.policy_max_checks(root)
                    policy_checks_read_timeout = nfr.scan.policy_checks_read_timeout(
                        root
                    )
                    scan_report_name = nfr.scan.report_name(root)
                    nessus_scan_file_name_with_path = (
                        nfr.file.nessus_scan_file_name_with_path(file_to_pars_full_name)
                    )
                    scan_time_elapsed = nfr.scan.scan_time_elapsed(root)
                    scan_policy_name = nfr.scan.policy_name(root)
                    scan_policy_db_sid = nfr.scan.policy_db_sid(root)
                    scan_policy_db_port = nfr.scan.policy_db_port(root)
                    scan_reverse_lookup = nfr.scan.reverse_lookup(root)
                    scan_policy_login_specified = nfr.scan.policy_login_specified(root)

                    report_host_counter = 0
                    number_of_report_hosts = nfr.scan.number_of_scanned_hosts(root)

                    info_host = (
                        ", Host: "
                        + str(report_host_counter)
                        + "/"
                        + str(number_of_report_hosts)
                    )
                    info_final = info_report + info_file + info_host
                    self.print_status_bar_info.emit(info_final)

                    for report_host in nfr.scan.report_hosts(root):
                        # print(row_index, number_of_rows, nessus_scan_file_number)
                        row_index += 1
                        number_of_rows += 1

                        report_host_counter += 1
                        self.progress.emit(report_host_counter, number_of_report_hosts)

                        info_host = (
                            ", Host: "
                            + str(report_host_counter)
                            + "/"
                            + str(number_of_report_hosts)
                        )
                        info_final = info_report + info_file + info_host
                        self.print_status_bar_info.emit(info_final)

                        login_used = nfr.host.login_used(report_host)
                        if login_used is None:
                            login_used = scan_policy_login_specified

                        number_of_plugins = nfr.host.number_of_plugins(report_host)
                        number_of_plugins_critical = (
                            nfr.host.number_of_plugins_per_risk_factor(
                                report_host, "Critical"
                            )
                        )
                        number_of_plugins_high = (
                            nfr.host.number_of_plugins_per_risk_factor(
                                report_host, "High"
                            )
                        )
                        number_of_plugins_medium = (
                            nfr.host.number_of_plugins_per_risk_factor(
                                report_host, "Medium"
                            )
                        )
                        number_of_plugins_low = (
                            nfr.host.number_of_plugins_per_risk_factor(
                                report_host, "Low"
                            )
                        )
                        number_of_plugins_none = (
                            nfr.host.number_of_plugins_per_risk_factor(
                                report_host, "None"
                            )
                        )
                        number_of_compliance_plugins = (
                            nfr.host.number_of_compliance_plugins(report_host)
                        )
                        number_of_compliance_plugins_passed = (
                            nfr.host.number_of_compliance_plugins_per_result(
                                report_host, "PASSED"
                            )
                        )
                        number_of_compliance_plugins_failed = (
                            nfr.host.number_of_compliance_plugins_per_result(
                                report_host, "FAILED"
                            )
                        )
                        number_of_compliance_plugins_warning = (
                            nfr.host.number_of_compliance_plugins_per_result(
                                report_host, "WARNING"
                            )
                        )
                        if not self.report_host_debug_data_enabled:
                            worksheet.write(
                                row_index, 0, nfr.host.report_host_name(report_host)
                            )
                            worksheet.write(
                                row_index, 1, nfr.host.resolved_hostname(report_host)
                            )
                            worksheet.write(
                                row_index, 2, nfr.host.resolved_fqdn(report_host)
                            )
                            worksheet.write(
                                row_index, 3, nfr.host.resolved_ip(report_host)
                            )
                            worksheet.write(row_index, 4, "yes")
                            worksheet.write(
                                row_index,
                                5,
                                nfr.host.credentialed_checks(root, report_host),
                            )

                            host_start_time = nfr.host.host_time_start(report_host)
                            if host_start_time is not None:
                                worksheet.write_datetime(
                                    row_index, 6, host_start_time, date_format
                                )
                            else:
                                worksheet.write_blank(row_index, 6, None)

                            host_end_time = nfr.host.host_time_end(report_host)
                            if host_end_time is not None:
                                worksheet.write_datetime(
                                    row_index, 7, host_end_time, date_format
                                )
                            else:
                                worksheet.write_blank(row_index, 7, None)

                            worksheet.write(
                                row_index, 8, nfr.host.host_time_elapsed(report_host)
                            )
                            worksheet.write(row_index, 9, scan_time_elapsed)
                            worksheet.write(row_index, 10, login_used)
                            worksheet.write(row_index, 11, scan_policy_db_sid)
                            worksheet.write(row_index, 12, scan_policy_db_port)
                            worksheet.write(
                                row_index, 13, nfr.host.detected_os(report_host)
                            )
                            worksheet.write(row_index, 14, number_of_plugins)
                            worksheet.write(row_index, 15, number_of_plugins_critical)
                            worksheet.write(row_index, 16, number_of_plugins_high)
                            worksheet.write(row_index, 17, number_of_plugins_medium)
                            worksheet.write(row_index, 18, number_of_plugins_low)
                            worksheet.write(row_index, 19, number_of_plugins_none)
                            worksheet.write(row_index, 20, number_of_compliance_plugins)
                            worksheet.write(
                                row_index, 21, number_of_compliance_plugins_passed
                            )
                            worksheet.write(
                                row_index, 22, number_of_compliance_plugins_failed
                            )
                            worksheet.write(
                                row_index, 23, number_of_compliance_plugins_warning
                            )

                        else:
                            worksheet.write(
                                row_index, 0, nfr.host.scanner_ip(root, report_host)
                            )
                            worksheet.write(row_index, 1, scan_report_name)
                            worksheet.write(
                                row_index, 2, nessus_scan_file_name_with_path
                            )
                            worksheet.write(
                                row_index, 3, nfr.host.report_host_name(report_host)
                            )
                            worksheet.write(
                                row_index, 4, nfr.host.resolved_hostname(report_host)
                            )
                            worksheet.write(
                                row_index, 5, nfr.host.resolved_fqdn(report_host)
                            )
                            worksheet.write(
                                row_index,
                                6,
                                nfr.host.netbios_network_name(root, report_host)[
                                    "netbios_computer_name"
                                ],
                            )
                            worksheet.write(
                                row_index,
                                7,
                                nfr.host.netbios_network_name(root, report_host)[
                                    "netbios_domain_name"
                                ],
                            )
                            worksheet.write(
                                row_index, 8, nfr.host.resolved_ip(report_host)
                            )
                            worksheet.write(row_index, 9, "yes")
                            worksheet.write(
                                row_index,
                                10,
                                nfr.host.credentialed_checks(root, report_host),
                            )

                            host_start_time = nfr.host.host_time_start(report_host)
                            if host_start_time is not None:
                                worksheet.write_datetime(
                                    row_index, 11, host_start_time, date_format
                                )
                            else:
                                worksheet.write_blank(row_index, 11, None)

                            host_end_time = nfr.host.host_time_end(report_host)
                            if host_end_time is not None:
                                worksheet.write_datetime(
                                    row_index, 12, host_end_time, date_format
                                )
                            else:
                                worksheet.write_blank(row_index, 12, None)

                            worksheet.write(
                                row_index, 13, nfr.host.host_time_elapsed(report_host)
                            )
                            worksheet.write(row_index, 14, scan_time_elapsed)
                            worksheet.write(row_index, 15, scan_policy_name)
                            worksheet.write(row_index, 16, login_used)
                            worksheet.write(row_index, 17, scan_policy_db_sid)
                            worksheet.write(row_index, 18, scan_policy_db_port)
                            worksheet.write(row_index, 19, scan_reverse_lookup)
                            worksheet.write(row_index, 20, policy_max_hosts)
                            worksheet.write(row_index, 21, policy_max_checks)
                            worksheet.write(row_index, 22, policy_checks_read_timeout)
                            worksheet.write(
                                row_index, 23, nfr.host.detected_os(report_host)
                            )
                            worksheet.write(row_index, 24, number_of_plugins)
                            worksheet.write(row_index, 25, number_of_plugins_critical)
                            worksheet.write(row_index, 26, number_of_plugins_high)
                            worksheet.write(row_index, 27, number_of_plugins_medium)
                            worksheet.write(row_index, 28, number_of_plugins_low)
                            worksheet.write(row_index, 29, number_of_plugins_none)
                            worksheet.write(row_index, 30, number_of_compliance_plugins)
                            worksheet.write(
                                row_index, 31, number_of_compliance_plugins_passed
                            )
                            worksheet.write(
                                row_index, 32, number_of_compliance_plugins_failed
                            )
                            worksheet.write(
                                row_index, 33, number_of_compliance_plugins_warning
                            )
                            # '10180: Ping to remote host'
                            worksheet.write(
                                row_index,
                                34,
                                nfr.plugin.plugin_output(root, report_host, "10180"),
                            )
                            # '10287: Traceroute Information'
                            worksheet.write(
                                row_index,
                                35,
                                nfr.plugin.plugin_output(root, report_host, "10287"),
                            )
                            # '11936: OS Identification'
                            worksheet.write(
                                row_index,
                                36,
                                nfr.plugin.plugin_output(root, report_host, "11936"),
                            )
                            # '45590: Common Platform Enumeration (CPE)'
                            worksheet.write(
                                row_index,
                                37,
                                nfr.plugin.plugin_output(root, report_host, "45590"),
                            )
                            # '54615: Device Type'
                            worksheet.write(
                                row_index,
                                38,
                                nfr.plugin.plugin_output(root, report_host, "54615"),
                            )
                            # '21745: Authentication Failure - Local Checks Not Run'
                            worksheet.write(
                                row_index,
                                39,
                                nfr.plugin.plugin_output(root, report_host, "21745"),
                            )
                            # '12634: Authenticated Check : OS Name and Installed Package Enumeration'
                            worksheet.write(
                                row_index,
                                40,
                                nfr.plugin.plugin_output(root, report_host, "12634"),
                            )
                            # '110385: Authentication Success Insufficient Access'
                            worksheet.write(
                                row_index,
                                41,
                                nfr.plugin.plugin_output(root, report_host, "110385"),
                            )
                            # '102094: SSH Commands Require Privilege Escalation'
                            worksheet.write(
                                row_index,
                                42,
                                nfr.plugin.plugin_output(root, report_host, "102094"),
                            )
                            # '10394: Microsoft Windows SMB Log In Possible'
                            worksheet.write(
                                row_index,
                                43,
                                nfr.plugin.plugin_output(root, report_host, "10394"),
                            )
                            # '24786: Nessus Windows Scan Not Performed with Admin Privileges'
                            worksheet.write(
                                row_index,
                                44,
                                nfr.plugin.plugin_output(root, report_host, "24786"),
                            )
                            # '24269: Windows Management Instrumentation (WMI) Available'
                            worksheet.write(
                                row_index,
                                45,
                                nfr.plugin.plugin_output(root, report_host, "24269"),
                            )
                            # '11011: Microsoft Windows SMB Service Detection'
                            worksheet.write(
                                row_index,
                                46,
                                nfr.plugin.plugin_outputs(root, report_host, "11011"),
                            )
                            # '10400: Microsoft Windows SMB Registry Remotely Accessible'
                            worksheet.write(
                                row_index,
                                47,
                                nfr.plugin.plugin_output(root, report_host, "10400"),
                            )
                            # '26917: Microsoft Windows SMB Registry : Nessus Cannot Access the Windows Registry'
                            worksheet.write(
                                row_index,
                                48,
                                nfr.plugin.plugin_output(root, report_host, "26917"),
                            )
                            # '42897: SMB Registry : Start the Registry Service during the scan (WMI)'
                            worksheet.write(
                                row_index,
                                49,
                                nfr.plugin.plugin_output(root, report_host, "42897"),
                            )
                            # '20811: Microsoft Windows Installed Software Enumeration (credentialed check)'
                            worksheet.write(
                                row_index,
                                50,
                                nfr.plugin.plugin_output(root, report_host, "20811"),
                            )
                            # '91825: Oracle DB Login Possible'
                            worksheet.write(
                                row_index,
                                51,
                                nfr.plugin.plugin_output(root, report_host, "91825"),
                            )
                            # '91827: Microsoft SQL Server Login Possible'
                            worksheet.write(
                                row_index,
                                52,
                                nfr.plugin.plugin_output(root, report_host, "91827"),
                            )
                            # '47864: Cisco IOS Version'
                            worksheet.write(
                                row_index,
                                53,
                                nfr.plugin.plugin_output(root, report_host, "47864"),
                            )
                            # '67217: Cisco IOS XE Version'
                            worksheet.write(
                                row_index,
                                54,
                                nfr.plugin.plugin_output(root, report_host, "67217"),
                            )

                    number_of_not_scanned_hosts = nfr.scan.number_of_not_scanned_hosts(
                        root
                    )
                    if number_of_not_scanned_hosts is not None:
                        if number_of_not_scanned_hosts > 0:
                            not_scanned_hosts = nfr.scan.list_of_not_scanned_hosts(root)
                            not_scanned_host_counter = 0

                            file_source = nfr.scan.scan_file_source(root)
                            if file_source == "Tenable.sc":
                                targets_sc = nfr.scan.list_of_target_hosts_sc_fqdn_ip(
                                    root
                                )

                                for target in not_scanned_hosts:
                                    row_index += 1
                                    not_scanned_host_counter += 1
                                    self.progress.emit(
                                        not_scanned_host_counter,
                                        number_of_not_scanned_hosts,
                                    )

                                    target_fqdn = ""
                                    target_hostname = ""
                                    target_ip = ""
                                    for target_sc in targets_sc:
                                        if target == target_sc["target_fqdn"]:
                                            target_fqdn = target_sc["target_fqdn"]
                                            target_hostname = target_fqdn.split(".")[0]
                                            target_ip = target_sc["target_ip"]

                                    if not self.report_host_debug_data_enabled:
                                        worksheet.write(row_index, 0, target)
                                        worksheet.write(row_index, 1, target_hostname)
                                        worksheet.write(row_index, 2, target_fqdn)
                                        worksheet.write(row_index, 3, target_ip)
                                        worksheet.write(row_index, 4, "no")
                                        worksheet.write(row_index, 5, "no")
                                        worksheet.write_blank(row_index, 6, None)
                                        worksheet.write_blank(row_index, 7, None)
                                        worksheet.write(row_index, 8, "0:00:00")
                                        worksheet.write(row_index, 9, scan_time_elapsed)
                                        worksheet.write(
                                            row_index, 10, scan_policy_login_specified
                                        )
                                        worksheet.write(
                                            row_index, 11, scan_policy_db_sid
                                        )
                                        worksheet.write(
                                            row_index, 12, scan_policy_db_port
                                        )
                                        worksheet.write_blank(row_index, 13, None)
                                        worksheet.write_blank(row_index, 14, None)
                                        worksheet.write_blank(row_index, 15, None)
                                        worksheet.write_blank(row_index, 16, None)
                                        worksheet.write_blank(row_index, 17, None)
                                        worksheet.write_blank(row_index, 18, None)
                                        worksheet.write_blank(row_index, 19, None)
                                        worksheet.write_blank(row_index, 20, None)
                                        worksheet.write_blank(row_index, 21, None)
                                        worksheet.write_blank(row_index, 22, None)
                                    else:
                                        # worksheet.write_blank(row_index, 0, None)
                                        worksheet.write(row_index, 1, scan_report_name)
                                        worksheet.write(
                                            row_index,
                                            2,
                                            nessus_scan_file_name_with_path,
                                        )
                                        worksheet.write(row_index, 3, target)
                                        worksheet.write(row_index, 4, target_hostname)
                                        worksheet.write(row_index, 5, target_fqdn)
                                        # worksheet.write_blank(row_index, 6, None)
                                        # worksheet.write_blank(row_index, 7, None)
                                        worksheet.write(row_index, 8, target_ip)
                                        worksheet.write(row_index, 9, "no")
                                        worksheet.write(row_index, 10, "no")
                                        # worksheet.write_blank(row_index, 11, None)
                                        # worksheet.write_blank(row_index, 12, None)
                                        worksheet.write(row_index, 13, "0:00:00")
                                        worksheet.write(
                                            row_index, 14, scan_time_elapsed
                                        )
                                        worksheet.write(row_index, 15, scan_policy_name)
                                        worksheet.write(
                                            row_index, 16, scan_policy_login_specified
                                        )
                                        worksheet.write(
                                            row_index, 17, scan_policy_db_sid
                                        )
                                        worksheet.write(
                                            row_index, 18, scan_policy_db_port
                                        )
                                        worksheet.write(
                                            row_index, 19, scan_reverse_lookup
                                        )
                                        worksheet.write(row_index, 20, policy_max_hosts)
                                        worksheet.write(
                                            row_index, 21, policy_max_checks
                                        )
                                        # worksheet.write_blank(row_index, 22, None)
                                        # worksheet.write_blank(row_index, 23, None)
                                        # worksheet.write_blank(row_index, 24, None)
                                        # worksheet.write_blank(row_index, 25, None)
                                        # worksheet.write_blank(row_index, 26, None)
                                        # worksheet.write_blank(row_index, 27, None)
                                        # worksheet.write_blank(row_index, 28, None)
                                        # worksheet.write_blank(row_index, 29, None)
                                        # worksheet.write_blank(row_index, 30, None)
                                        # worksheet.write_blank(row_index, 31, None)
                                        # worksheet.write_blank(row_index, 32, None)
                                        # # '10180: Ping to remote host'
                                        # worksheet.write_blank(row_index, 33, None)
                                        # # '10287: Traceroute Information'
                                        # worksheet.write_blank(row_index, 34, None)
                                        # # '11936: OS Identification'
                                        # worksheet.write_blank(row_index, 35, None)
                                        # # '45590: Common Platform Enumeration (CPE)'
                                        # worksheet.write_blank(row_index, 36, None)
                                        # # '54615: Device Type'
                                        # worksheet.write_blank(row_index, 37, None)
                                        # # '21745: Authentication Failure - Local Checks Not Run'
                                        # worksheet.write_blank(row_index, 38, None)
                                        # # '12634: Authenticated Check : OS Name and Installed Package Enumeration'
                                        # worksheet.write_blank(row_index, 39, None)
                                        # # '110385: Authentication Success Insufficient Access'
                                        # worksheet.write_blank(row_index, 40, None)
                                        # # '102094: SSH Commands Require Privilege Escalation'
                                        # worksheet.write_blank(row_index, 41, None)
                                        # # '10394: Microsoft Windows SMB Log In Possible'
                                        # worksheet.write_blank(row_index, 42, None)
                                        # # '24786: Nessus Windows Scan Not Performed with Admin Privileges'
                                        # worksheet.write_blank(row_index, 43, None)
                                        # # '24269: Windows Management Instrumentation (WMI) Available'
                                        # worksheet.write_blank(row_index, 44, None)
                                        # # '11011: Microsoft Windows SMB Service Detection'
                                        # worksheet.write_blank(row_index, 45, None)
                                        # # '10400: Microsoft Windows SMB Registry Remotely Accessible'
                                        # worksheet.write_blank(row_index, 46, None)
                                        # # '26917: Microsoft Windows SMB Registry :
                                        # # Nessus Cannot Access the Windows Registry'
                                        # worksheet.write_blank(row_index, 47, None)
                                        # # '42897: SMB Registry : Start the Registry Service during the scan (WMI)'
                                        # worksheet.write_blank(row_index, 48, None)
                                        # # '20811: Microsoft Windows Installed Software Enumeration (credentialed check)'
                                        # worksheet.write_blank(row_index, 49, None)
                                        # # '91825: Oracle DB Login Possible'
                                        # worksheet.write_blank(row_index, 50, None)
                                        # # '91827: Microsoft SQL Server Login Possible'
                                        # worksheet.write_blank(row_index, 51, None)
                                        # # '47864: Cisco IOS Version'
                                        # worksheet.write_blank(row_index, 52, None)
                                        # # '67217: Cisco IOS XE Version'
                                        # worksheet.write_blank(row_index, 53, None)

                            else:
                                for target in not_scanned_hosts:
                                    row_index += 1
                                    not_scanned_host_counter += 1
                                    self.progress.emit(
                                        not_scanned_host_counter,
                                        number_of_not_scanned_hosts,
                                    )

                                    if not self.report_host_debug_data_enabled:
                                        worksheet.write(row_index, 0, target)
                                        worksheet.write_blank(row_index, 1, None)
                                        worksheet.write_blank(row_index, 2, None)
                                        worksheet.write_blank(row_index, 3, None)
                                        worksheet.write(row_index, 4, "no")
                                        worksheet.write(row_index, 5, "no")
                                        worksheet.write_blank(row_index, 6, None)
                                        worksheet.write_blank(row_index, 7, None)
                                        worksheet.write(row_index, 8, "0:00:00")
                                        worksheet.write(row_index, 9, scan_time_elapsed)
                                        worksheet.write(
                                            row_index, 10, scan_policy_login_specified
                                        )
                                        worksheet.write(
                                            row_index, 11, scan_policy_db_sid
                                        )
                                        worksheet.write(
                                            row_index, 12, scan_policy_db_port
                                        )
                                        worksheet.write_blank(row_index, 13, None)
                                        worksheet.write_blank(row_index, 14, None)
                                        worksheet.write_blank(row_index, 15, None)
                                        worksheet.write_blank(row_index, 16, None)
                                        worksheet.write_blank(row_index, 17, None)
                                        worksheet.write_blank(row_index, 18, None)
                                        worksheet.write_blank(row_index, 19, None)
                                        worksheet.write_blank(row_index, 20, None)
                                        worksheet.write_blank(row_index, 21, None)
                                        worksheet.write_blank(row_index, 22, None)
                                    else:
                                        # worksheet.write_blank(row_index, 0, None)
                                        worksheet.write(row_index, 1, scan_report_name)
                                        worksheet.write(
                                            row_index,
                                            2,
                                            nessus_scan_file_name_with_path,
                                        )
                                        worksheet.write(row_index, 3, target)
                                        # worksheet.write_blank(row_index, 4, None)
                                        # worksheet.write_blank(row_index, 5, None)
                                        # worksheet.write_blank(row_index, 6, None)
                                        # worksheet.write_blank(row_index, 7, None)
                                        # worksheet.write_blank(row_index, 8, None)
                                        worksheet.write(row_index, 9, "no")
                                        worksheet.write(row_index, 10, "no")
                                        # worksheet.write_blank(row_index, 11, None)
                                        # worksheet.write_blank(row_index, 12, None)
                                        worksheet.write(row_index, 13, "0:00:00")
                                        worksheet.write(
                                            row_index, 14, scan_time_elapsed
                                        )
                                        worksheet.write(row_index, 15, scan_policy_name)
                                        worksheet.write(
                                            row_index, 16, scan_policy_login_specified
                                        )
                                        worksheet.write(
                                            row_index, 17, scan_policy_db_sid
                                        )
                                        worksheet.write(
                                            row_index, 18, scan_policy_db_port
                                        )
                                        worksheet.write(
                                            row_index, 19, scan_reverse_lookup
                                        )
                                        worksheet.write(row_index, 20, policy_max_hosts)
                                        worksheet.write(
                                            row_index, 21, policy_max_checks
                                        )
                                        # worksheet.write_blank(row_index, 22, None)
                                        # worksheet.write_blank(row_index, 23, None)
                                        # worksheet.write_blank(row_index, 24, None)
                                        # worksheet.write_blank(row_index, 25, None)
                                        # worksheet.write_blank(row_index, 26, None)
                                        # worksheet.write_blank(row_index, 27, None)
                                        # worksheet.write_blank(row_index, 28, None)
                                        # worksheet.write_blank(row_index, 29, None)
                                        # worksheet.write_blank(row_index, 30, None)
                                        # worksheet.write_blank(row_index, 31, None)
                                        # worksheet.write_blank(row_index, 32, None)
                                        # # '10180: Ping to remote host'
                                        # worksheet.write_blank(row_index, 33, None)
                                        # # '10287: Traceroute Information'
                                        # worksheet.write_blank(row_index, 34, None)
                                        # # '11936: OS Identification'
                                        # worksheet.write_blank(row_index, 35, None)
                                        # # '45590: Common Platform Enumeration (CPE)'
                                        # worksheet.write_blank(row_index, 36, None)
                                        # # '54615: Device Type'
                                        # worksheet.write_blank(row_index, 37, None)
                                        # # '21745: Authentication Failure - Local Checks Not Run'
                                        # worksheet.write_blank(row_index, 38, None)
                                        # # '12634: Authenticated Check : OS Name and Installed Package Enumeration'
                                        # worksheet.write_blank(row_index, 39, None)
                                        # # '110385: Authentication Success Insufficient Access'
                                        # worksheet.write_blank(row_index, 40, None)
                                        # # '102094: SSH Commands Require Privilege Escalation'
                                        # worksheet.write_blank(row_index, 41, None)
                                        # # '10394: Microsoft Windows SMB Log In Possible'
                                        # worksheet.write_blank(row_index, 42, None)
                                        # # '24786: Nessus Windows Scan Not Performed with Admin Privileges'
                                        # worksheet.write_blank(row_index, 43, None)
                                        # # '24269: Windows Management Instrumentation (WMI) Available'
                                        # worksheet.write_blank(row_index, 44, None)
                                        # # '11011: Microsoft Windows SMB Service Detection'
                                        # worksheet.write_blank(row_index, 45, None)
                                        # # '10400: Microsoft Windows SMB Registry Remotely Accessible'
                                        # worksheet.write_blank(row_index, 46, None)
                                        # # '26917: Microsoft Windows SMB Registry :
                                        # # Nessus Cannot Access the Windows Registry'
                                        # worksheet.write_blank(row_index, 47, None)
                                        # # '42897: SMB Registry : Start the Registry Service during the scan (WMI)'
                                        # worksheet.write_blank(row_index, 48, None)
                                        # # '20811: Microsoft Windows Installed Software Enumeration (credentialed check)'
                                        # worksheet.write_blank(row_index, 49, None)
                                        # # '91825: Oracle DB Login Possible'
                                        # worksheet.write_blank(row_index, 50, None)
                                        # # '91827: Microsoft SQL Server Login Possible'
                                        # worksheet.write_blank(row_index, 51, None)
                                        # # '47864: Cisco IOS Version'
                                        # worksheet.write_blank(row_index, 52, None)
                                        # # '67217: Cisco IOS XE Version'
                                        # worksheet.write_blank(row_index, 53, None)

                        end_time = time.time()
                        elapsed_time = end_time - start_time
                        elapsed_time_parsed = time.strftime(
                            "%H:%M:%S", time.gmtime(elapsed_time)
                        )

                        self.log_emitter(
                            "info ",
                            file_to_pars_full_name,
                            f"[elapsed_time={elapsed_time_parsed}]",
                        )
                        self.log_emitter("end  ", file_to_pars_full_name)

                except Exception as e:
                    number_of_files_with_errors += 1
                    traceback.print_exc()
                    self.log_emitter(
                        "info",
                        "ERROR Parsing ["
                        + str(nessus_scan_file_number)
                        + "/"
                        + str(len(list_of_source_files))
                        + "] nessus files",
                    )
                    self.log_emitter("info", e)

        if number_of_rows > 0:
            worksheet.autofilter(0, 0, number_of_rows, number_of_columns - 1)

    def create_worksheet_for_vulnerabilities(self, workbook, list_of_source_files):
        """
        Function create spreadsheet with vulnerabilities sum-up in given workbook for selected files.
        :param workbook: workbook where spreadsheet are created
        :param list_of_source_files: list of selected files
        """

        sheet_index = 1
        row_index = 0
        rows_in_current_sheet = 0

        MAX_ROWS = 1_048_576
        HEADER_ROWS = 1

        report_name = "vulnerabilities"
        self.report_counter += 1
        info_report = (
            "Report: "
            + str(self.report_counter)
            + "/"
            + str(self.number_of_selected_reports)
        )

        worksheet = workbook.add_worksheet(f"{report_name}")

        cell_format_bold = workbook.add_format({"bold": True})
        worksheet.set_row(0, None, cell_format_bold)

        # print('>>>>>>>>>>>>>>>> ', self.report_vulnerabilities_debug_data_enabled)

        if not self.report_vulnerabilities_debug_data_enabled:
            headers = [
                # "Nessus scanner IP",
                # "Nessus scan name",
                # "Nessus file name",
                "Target",
                "Hostname",
                "FQDN",
                "IP",
                "Scanned",
                "Credentialed checks",
                # "Policy name",
                "Protocol",
                "Service Name",
                "Port",
                "Plugin ID",
                "Plugin name",
                "Plugin type",
                # "Severity Number",
                "Severity",
                "Risk Factor",
                # "CVSSv2 Base Score",
                "CVSSv2",
                # "CVSSv3 Base Score",
                "CVSSv3",
                # "CVSSv4 Base Score",
                "CVSSv4",
                # "VPR Score",
                "VPR",
                # "EPSS",
                "EPSS %",
                "Plugin family",
                # "Plugin file name",
                "Plugin version",
                "Plugin publication date",
                "Plugin modification date",
                "Plugin description",
                "Solution",
                "Plugin output",
                "CVE counter",
                "CVE number",
                "Exploit available",
                "Exploit code maturity",
                "Exploit framework metasploit",
                "Exploitability ease",
            ]
        else:
            headers = [
                "Nessus scanner IP",
                "Nessus scan name",
                "Nessus file name",
                "Target",
                "Hostname",
                "FQDN",
                "IP",
                "Scanned",
                "Credentialed checks",
                "Policy name",
                "Protocol",
                "Service Name",
                "Port",
                "Plugin ID",
                "Plugin name",
                "Plugin type",
                "Severity Number",
                "Severity",
                "Risk Factor",
                "CVSSv2 Base Score",
                "CVSSv2",
                "CVSSv3 Base Score",
                "CVSSv3",
                "CVSSv4 Base Score",
                "CVSSv4",
                "VPR Score",
                "VPR",
                "EPSS",
                "EPSS %",
                "Plugin family",
                "Plugin file name",
                "Plugin version",
                "Plugin publication date",
                "Plugin modification date",
                "Plugin description",
                "Solution",
                "Plugin output",
                "CVE counter",
                "CVE number",
                "Exploit available",
                "Exploit code maturity",
                "Exploit framework metasploit",
                "Exploitability ease",
            ]

        number_of_columns = len(headers)
        # print('Number of columns: ' + str(number_of_columns))

        worksheet.set_column(0, number_of_columns - 1, 18)

        debug_columns_list = [0, 1, 2, 9, 16, 19, 21, 23, 25, 27, 30]

        for column_index, header in enumerate(headers):
            if (
                self.report_vulnerabilities_debug_data_enabled
                and column_index in debug_columns_list
            ):
                cell_format_bold_blue = workbook.add_format()
                cell_format_bold_blue.set_bold()
                cell_format_bold_blue.set_font_color("blue")
                worksheet.write(0, column_index, header, cell_format_bold_blue)
            else:
                worksheet.write(0, column_index, header)

        worksheet.freeze_panes(1, 0)  # Freeze the first row.

        nessus_scan_file_number = 0
        number_of_files_with_errors = 0

        number_of_rows = 0
        row_index = 0
        row_index_per_file = 0
        for scan_file in list_of_source_files:
            source_file_type = ""
            files_to_pars = []
            if fnmatch.fnmatch(scan_file, "*.nessus"):
                files_to_pars.append(scan_file)
                source_file_type = "nessus"
            elif fnmatch.fnmatch(scan_file, "*.zip"):
                source_file_type = "zip"
                zip_source = zipfile.ZipFile(scan_file)
                zip_files_list = zip_source.namelist()
                for zip_file in zip_files_list:
                    if fnmatch.fnmatch(zip_file, "*.nessus"):
                        files_to_pars.append(zip_file)

            for file_to_pars in files_to_pars:
                row_index_per_file += 1
                nessus_scan_file_number = nessus_scan_file_number + 1
                info_file = (
                    ", File: "
                    + str(row_index_per_file)
                    + "/"
                    + str(len(list_of_source_files))
                )

                try:
                    file_to_pars_full_name = ""
                    source_file_size = 0
                    if source_file_type == "nessus":
                        root = nfr.file.nessus_scan_file_root_element(file_to_pars)
                        scan_file_source = nfr.scan.scan_file_source(root)
                        file_to_pars_full_name = (
                            nfr.file.nessus_scan_file_name_with_path(file_to_pars)
                        )
                        source_file_size = nfa.utilities.size_of_file_human(
                            file_to_pars_full_name
                        )
                    elif source_file_type == "zip":
                        source_file_size = nfa.utilities.size_of_file_inside_zip_human(
                            zip_source, file_to_pars
                        )
                        file_to_pars_full_name = (
                            f"{nfr.file.nessus_scan_file_name_with_path(scan_file)} ["
                            f"{zip_source.getinfo(file_to_pars).filename}]"
                        )
                        file_to_pars = zip_source.open(file_to_pars)
                        root = nfr.file.nessus_scan_file_root_element(file_to_pars)
                        scan_file_source = nfr.scan.scan_file_source(root)

                    start_time = time.time()
                    self.log_emitter("start", file_to_pars_full_name)
                    self.file_analysis_started.emit(1)

                    self.log_emitter(
                        "info ",
                        file_to_pars_full_name,
                        f"[scan_file_source={scan_file_source}]",
                    )
                    self.log_emitter(
                        "info ",
                        file_to_pars_full_name,
                        f"[source_file_size={source_file_size}]",
                    )
                    self.log_emitter(
                        "info ", file_to_pars_full_name, f"[report_type={report_name}]"
                    )
                    self.log_emitter(
                        "info ",
                        file_to_pars_full_name,
                        f"[number_of_target_hosts={nfr.scan.number_of_target_hosts(root)}]",
                    )
                    self.log_emitter(
                        "info ",
                        file_to_pars_full_name,
                        f"[number_of_scanned_hosts={nfr.scan.number_of_scanned_hosts(root)}]",
                    )
                    self.log_emitter(
                        "info ",
                        file_to_pars_full_name,
                        f"[number_of_scanned_hosts_with_credentials="
                        f"{nfr.scan.number_of_scanned_hosts_with_credentialed_checks_yes(root)}]",
                    )

                    date_format = workbook.add_format({"num_format": "yyyy-mm-dd"})

                    scan_report_name = nfr.scan.report_name(root)
                    nessus_scan_file_name_with_path = (
                        nfr.file.nessus_scan_file_name_with_path(file_to_pars_full_name)
                    )
                    scan_policy_name = nfr.scan.policy_name(root)

                    report_host_counter = 0
                    number_of_report_hosts = nfr.scan.number_of_scanned_hosts(root)

                    info_host = (
                        ", Host: "
                        + str(report_host_counter)
                        + "/"
                        + str(number_of_report_hosts)
                    )
                    info_final = info_report + info_file + info_host
                    self.print_status_bar_info.emit(info_final)

                    for report_host in nfr.scan.report_hosts(root):

                        report_host_counter += 1
                        self.progress.emit(report_host_counter, number_of_report_hosts)

                        info_host = (
                            ", Host: "
                            + str(report_host_counter)
                            + "/"
                            + str(number_of_report_hosts)
                        )
                        info_final = info_report + info_file + info_host
                        self.print_status_bar_info.emit(info_final)

                        report_items_per_host = nfr.host.report_items(report_host)

                        host_scanner_ip = nfr.host.scanner_ip(root, report_host)
                        host_report_host_name = nfr.host.report_host_name(report_host)
                        host_resolved_hostname = nfr.host.resolved_hostname(report_host)
                        host_resolved_fqdn = nfr.host.resolved_fqdn(report_host)
                        host_resolved_ip = nfr.host.resolved_ip(report_host)
                        host_credentialed_checks = nfr.host.credentialed_checks(
                            root, report_host
                        )

                        for report_item in report_items_per_host:
                            if row_index >= MAX_ROWS - HEADER_ROWS:
                                # Apply autofilter to the completed worksheet before creating a new one
                                if rows_in_current_sheet > 0:
                                    worksheet.autofilter(0, 0, rows_in_current_sheet, number_of_columns - 1)

                                    if self.report_vulnerabilities_none_filter_out:
                                        risk_factors = ["Critical", "High", "Medium", "Low"]
                                        if not self.report_vulnerabilities_debug_data_enabled:
                                            worksheet.filter_column_list(12, risk_factors)
                                        else:
                                            worksheet.filter_column_list(16, risk_factors)

                                sheet_index += 1
                                worksheet = workbook.add_worksheet(f"{report_name}_{sheet_index}")
                                row_index = 0  # Reset for the new sheet
                                rows_in_current_sheet = 0  # Reset row counter for new sheet

                                # Apply header formatting and freeze panes for new sheet
                                worksheet.set_row(0, None, cell_format_bold)
                                worksheet.set_column(0, number_of_columns - 1, 18)

                                # Write headers to new sheet
                                for column_index, header in enumerate(headers):
                                    if (
                                        self.report_vulnerabilities_debug_data_enabled
                                        and column_index in debug_columns_list
                                    ):
                                        cell_format_bold_blue = workbook.add_format()
                                        cell_format_bold_blue.set_bold()
                                        cell_format_bold_blue.set_font_color("blue")
                                        worksheet.write(0, column_index, header, cell_format_bold_blue)
                                    else:
                                        worksheet.write(0, column_index, header)

                                worksheet.freeze_panes(1, 0)  # Freeze the first row

                            number_of_rows += 1
                            row_index += 1
                            rows_in_current_sheet = row_index

                            protocol = nfr.plugin.report_item_value(
                                report_item, "protocol"
                            )
                            service_name = nfr.plugin.report_item_value(
                                report_item, "svc_name"
                            )
                            port = int(
                                nfr.plugin.report_item_value(report_item, "port")
                            )
                            plugin_id = int(
                                nfr.plugin.report_item_value(report_item, "pluginID")
                            )
                            plugin_name = nfr.plugin.report_item_value(
                                report_item, "pluginName"
                            )
                            plugin_type = nfr.plugin.report_item_value(
                                report_item, "plugin_type"
                            )
                            severity = nfr.plugin.report_item_value(
                                report_item, "severity"
                            )
                            severity_label = nfr.plugin.severity_number_to_label(
                                severity
                            )
                            risk_factor = nfr.plugin.report_item_value(
                                report_item, "risk_factor"
                            )
                            cvssv2_base_score = nfr.plugin.report_item_value(
                                report_item, "cvss_base_score"
                            )
                            cvssv2_base_score_label = (
                                nfr.plugin.cvssv2_score_to_severity(cvssv2_base_score)
                            )
                            cvssv3_base_score = nfr.plugin.report_item_value(
                                report_item, "cvss3_base_score"
                            )
                            cvssv3_base_score_label = (
                                nfr.plugin.cvssv3_score_to_severity(cvssv3_base_score)
                            )
                            cvssv4_base_score = nfr.plugin.report_item_value(
                                report_item, "cvss4_base_score"
                            )
                            cvssv4_base_score_label = (
                                nfr.plugin.cvssv4_score_to_severity(cvssv4_base_score)
                            )
                            vpr_score = nfr.plugin.report_item_value(
                                report_item, "vpr_score"
                            )
                            vpr_score_label = nfr.plugin.vpr_score_to_severity(
                                vpr_score
                            )
                            epss_score = nfr.plugin.report_item_value(
                                report_item, "epss_score"
                            )
                            epss_score_percent = (
                                nfr.plugin.epss_score_decimal_to_percent(epss_score)
                            )
                            plugin_family = nfr.plugin.report_item_value(
                                report_item, "pluginFamily"
                            )
                            plugin_file_name = nfr.plugin.report_item_value(
                                report_item, "fname"
                            )
                            plugin_version = nfr.plugin.report_item_value(
                                report_item, "script_version"
                            )
                            plugin_publication_date = nfr.plugin.report_item_value(
                                report_item, "plugin_publication_date"
                            )
                            plugin_modification_date = nfr.plugin.report_item_value(
                                report_item, "plugin_modification_date"
                            )
                            plugin_description = nfr.plugin.report_item_value(
                                report_item, "description"
                            )
                            solution = nfr.plugin.report_item_value(
                                report_item, "solution"
                            )
                            plugin_output = nfr.plugin.report_item_value(
                                report_item, "plugin_output"
                            )
                            plugin_cves = nfr.plugin.report_item_values(
                                report_item, "cve"
                            )
                            exploit_available = nfr.plugin.report_item_value(
                                report_item, "exploit_available"
                            )
                            exploit_code_maturity = nfr.plugin.report_item_value(
                                report_item, "exploit_code_maturity"
                            )
                            exploit_framework_metasploit = nfr.plugin.report_item_value(
                                report_item, "exploit_framework_metasploit"
                            )
                            exploitability_ease = nfr.plugin.report_item_value(
                                report_item, "exploitability_ease"
                            )

                            if (
                                not self.report_vulnerabilities_debug_data_enabled
                                and not self.report_vulnerabilities_none_skip
                            ):
                                worksheet.set_column("E:G", 10)
                                worksheet.set_column("I:I", 7)
                                worksheet.set_column("J:J", 10)
                                worksheet.set_column("L:L", 12)
                                worksheet.set_column("M:S", 10)
                                worksheet.set_column("V:W", 10)
                                worksheet.set_column("AA:AA", 13)
                                worksheet.write(row_index, 0, host_report_host_name)
                                worksheet.write(row_index, 1, host_resolved_hostname)
                                worksheet.write(row_index, 2, host_resolved_fqdn)
                                worksheet.write(row_index, 3, host_resolved_ip)
                                worksheet.write(row_index, 4, "yes")
                                worksheet.write(row_index, 5, host_credentialed_checks)
                                worksheet.write(row_index, 6, protocol)
                                worksheet.write(row_index, 7, service_name)
                                worksheet.write(row_index, 8, port)
                                worksheet.write(row_index, 9, plugin_id)
                                worksheet.write(row_index, 10, plugin_name)
                                worksheet.write(row_index, 11, plugin_type)
                                if self.report_vulnerabilities_none_filter_out:
                                    if risk_factor == "None":
                                        worksheet.set_row(
                                            row_index, options={"hidden": True}
                                        )

                                worksheet.write(row_index, 12, severity_label)
                                worksheet.write(row_index, 13, risk_factor)
                                worksheet.write(row_index, 14, cvssv2_base_score_label)
                                worksheet.write(row_index, 15, cvssv3_base_score_label)
                                worksheet.write(row_index, 16, cvssv4_base_score_label)
                                worksheet.write(row_index, 17, vpr_score_label)
                                if epss_score is not None:
                                    percent_format = workbook.add_format(
                                        {"num_format": "0.0%"}
                                    )
                                    worksheet.write_number(
                                        row_index, 18, float(epss_score), percent_format
                                    )
                                else:
                                    worksheet.write_blank(row_index, 18, None)

                                worksheet.write(row_index, 19, plugin_family)
                                worksheet.write(row_index, 20, plugin_version)
                                if plugin_publication_date is not None:
                                    worksheet.write_datetime(
                                        row_index,
                                        21,
                                        nfr.plugin.plugin_date(plugin_publication_date),
                                        date_format,
                                    )
                                else:
                                    worksheet.write_blank(row_index, 21, None)
                                if plugin_modification_date is not None:
                                    worksheet.write_datetime(
                                        row_index,
                                        22,
                                        nfr.plugin.plugin_date(
                                            plugin_modification_date
                                        ),
                                        date_format,
                                    )
                                else:
                                    worksheet.write_blank(row_index, 22, None)
                                worksheet.write(row_index, 23, plugin_description)
                                worksheet.write(row_index, 24, solution)
                                if plugin_output is not None:
                                    worksheet.write_string(row_index, 25, plugin_output)
                                else:
                                    worksheet.write_blank(row_index, 25, None)
                                if plugin_cves:
                                    worksheet.write_number(
                                        row_index, 26, len(plugin_cves)
                                    )
                                    worksheet.write_string(
                                        row_index, 27, ",".join(plugin_cves)
                                    )
                                else:
                                    worksheet.write_number(row_index, 26, 0)
                                    worksheet.write_blank(row_index, 27, None)
                                if exploit_available is not None:
                                    worksheet.write_string(
                                        row_index, 28, exploit_available
                                    )
                                else:
                                    worksheet.write_blank(row_index, 28, None)
                                if exploit_code_maturity is not None:
                                    worksheet.write_string(
                                        row_index, 29, exploit_code_maturity
                                    )
                                else:
                                    worksheet.write_blank(row_index, 29, None)
                                if exploit_framework_metasploit is not None:
                                    worksheet.write_string(
                                        row_index, 30, exploit_framework_metasploit
                                    )
                                else:
                                    worksheet.write_blank(row_index, 30, None)
                                if exploitability_ease is not None:
                                    worksheet.write_string(
                                        row_index, 31, exploitability_ease
                                    )
                                else:
                                    worksheet.write_blank(row_index, 31, None)
                            elif (
                                not self.report_vulnerabilities_debug_data_enabled
                                and self.report_vulnerabilities_none_skip
                            ):
                                if risk_factor == "None":
                                    number_of_rows -= 1
                                    row_index -= 1
                                elif risk_factor != "None":
                                    worksheet.set_column("E:G", 10)
                                    worksheet.set_column("I:I", 7)
                                    worksheet.set_column("J:J", 10)
                                    worksheet.set_column("L:L", 12)
                                    worksheet.set_column("M:S", 10)
                                    worksheet.set_column("V:W", 10)
                                    worksheet.set_column("AA:AA", 13)
                                    worksheet.write(row_index, 0, host_report_host_name)
                                    worksheet.write(
                                        row_index, 1, host_resolved_hostname
                                    )
                                    worksheet.write(row_index, 2, host_resolved_fqdn)
                                    worksheet.write(row_index, 3, host_resolved_ip)
                                    worksheet.write(row_index, 4, "yes")
                                    worksheet.write(
                                        row_index, 5, host_credentialed_checks
                                    )
                                    worksheet.write(row_index, 6, protocol)
                                    worksheet.write(row_index, 7, service_name)
                                    worksheet.write(row_index, 8, port)
                                    worksheet.write(row_index, 9, plugin_id)
                                    worksheet.write(row_index, 10, plugin_name)
                                    worksheet.write(row_index, 11, plugin_type)

                                    worksheet.write(row_index, 12, severity_label)
                                    worksheet.write(row_index, 13, risk_factor)
                                    worksheet.write(
                                        row_index, 14, cvssv2_base_score_label
                                    )
                                    worksheet.write(
                                        row_index, 15, cvssv3_base_score_label
                                    )
                                    worksheet.write(
                                        row_index, 16, cvssv4_base_score_label
                                    )
                                    worksheet.write(row_index, 17, vpr_score_label)
                                    if epss_score is not None:
                                        percent_format = workbook.add_format(
                                            {"num_format": "0.0%"}
                                        )
                                        worksheet.write_number(
                                            row_index,
                                            18,
                                            float(epss_score),
                                            percent_format,
                                        )
                                    else:
                                        worksheet.write_blank(row_index, 18, None)

                                    worksheet.write(row_index, 19, plugin_family)
                                    worksheet.write(row_index, 20, plugin_version)
                                    if plugin_publication_date is not None:
                                        worksheet.write_datetime(
                                            row_index,
                                            21,
                                            nfr.plugin.plugin_date(
                                                plugin_publication_date
                                            ),
                                            date_format,
                                        )
                                    else:
                                        worksheet.write_blank(row_index, 21, None)
                                    if plugin_modification_date is not None:
                                        worksheet.write_datetime(
                                            row_index,
                                            22,
                                            nfr.plugin.plugin_date(
                                                plugin_modification_date
                                            ),
                                            date_format,
                                        )
                                    else:
                                        worksheet.write_blank(row_index, 22, None)
                                    worksheet.write(row_index, 23, plugin_description)
                                    worksheet.write(row_index, 24, solution)
                                    if plugin_output is not None:
                                        worksheet.write_string(
                                            row_index, 25, plugin_output
                                        )
                                    else:
                                        worksheet.write_blank(row_index, 25, None)
                                    if plugin_cves:
                                        worksheet.write_number(
                                            row_index, 26, len(plugin_cves)
                                        )
                                        worksheet.write_string(
                                            row_index, 27, ",".join(plugin_cves)
                                        )
                                    else:
                                        worksheet.write_number(row_index, 26, 0)
                                        worksheet.write_blank(row_index, 27, None)
                                    if exploit_available is not None:
                                        worksheet.write_string(
                                            row_index, 28, exploit_available
                                        )
                                    else:
                                        worksheet.write_blank(row_index, 28, None)
                                    if exploit_code_maturity is not None:
                                        worksheet.write_string(
                                            row_index, 29, exploit_code_maturity
                                        )
                                    else:
                                        worksheet.write_blank(row_index, 29, None)
                                    if exploit_framework_metasploit is not None:
                                        worksheet.write_string(
                                            row_index, 30, exploit_framework_metasploit
                                        )
                                    else:
                                        worksheet.write_blank(row_index, 30, None)
                                    if exploitability_ease is not None:
                                        worksheet.write_string(
                                            row_index, 31, exploitability_ease
                                        )
                                    else:
                                        worksheet.write_blank(row_index, 31, None)
                            elif (
                                self.report_vulnerabilities_debug_data_enabled
                                and not self.report_vulnerabilities_none_skip
                            ):
                                worksheet.set_column("H:I", 10)
                                worksheet.set_column("K:K", 10)
                                worksheet.set_column("M:M", 7)
                                worksheet.set_column("N:N", 10)
                                worksheet.set_column("P:P", 12)
                                worksheet.set_column("Q:AC", 10)
                                worksheet.set_column("AG:AH", 10)
                                worksheet.set_column("AL:AL", 13)
                                worksheet.write(row_index, 0, host_scanner_ip)
                                worksheet.write(row_index, 1, scan_report_name)
                                worksheet.write(
                                    row_index, 2, nessus_scan_file_name_with_path
                                )
                                worksheet.write(row_index, 3, host_report_host_name)
                                worksheet.write(row_index, 4, host_resolved_hostname)
                                worksheet.write(row_index, 5, host_resolved_fqdn)
                                worksheet.write(row_index, 6, host_resolved_ip)
                                worksheet.write(row_index, 7, "yes")
                                worksheet.write(row_index, 8, host_credentialed_checks)
                                worksheet.write(row_index, 9, scan_policy_name)
                                worksheet.write(row_index, 10, protocol)
                                worksheet.write(row_index, 11, service_name)
                                worksheet.write(row_index, 12, port)
                                worksheet.write(row_index, 13, plugin_id)
                                worksheet.write(row_index, 14, plugin_name)
                                worksheet.write(row_index, 15, plugin_type)
                                if self.report_vulnerabilities_none_filter_out:
                                    if risk_factor == "None":
                                        worksheet.set_row(
                                            row_index, options={"hidden": True}
                                        )
                                worksheet.write_number(row_index, 16, int(severity))
                                worksheet.write(row_index, 17, severity_label)
                                worksheet.write(row_index, 18, risk_factor)
                                if cvssv2_base_score is not None:
                                    worksheet.write_number(
                                        row_index, 19, float(cvssv2_base_score)
                                    )
                                else:
                                    worksheet.write_blank(row_index, 19, None)
                                worksheet.write(row_index, 20, cvssv2_base_score_label)
                                if cvssv3_base_score is not None:
                                    worksheet.write_number(
                                        row_index, 21, float(cvssv3_base_score)
                                    )
                                else:
                                    worksheet.write_blank(row_index, 21, None)
                                worksheet.write(row_index, 22, cvssv3_base_score_label)
                                if cvssv4_base_score is not None:
                                    worksheet.write_number(
                                        row_index, 23, float(cvssv4_base_score)
                                    )
                                else:
                                    worksheet.write_blank(row_index, 23, None)
                                worksheet.write(row_index, 24, cvssv4_base_score_label)
                                if vpr_score is not None:
                                    worksheet.write_number(
                                        row_index, 25, float(vpr_score)
                                    )
                                else:
                                    worksheet.write_blank(row_index, 25, None)
                                worksheet.write(row_index, 26, vpr_score_label)
                                if epss_score is not None:
                                    worksheet.write_number(
                                        row_index, 27, float(epss_score)
                                    )
                                else:
                                    worksheet.write_blank(row_index, 27, None)
                                if epss_score is not None:
                                    worksheet.write_number(
                                        row_index, 27, float(epss_score)
                                    )
                                    percent_format = workbook.add_format(
                                        {"num_format": "0.0%"}
                                    )
                                    worksheet.write_number(
                                        row_index, 28, float(epss_score), percent_format
                                    )
                                else:
                                    worksheet.write_blank(row_index, 27, None)
                                    worksheet.write_blank(row_index, 28, None)
                                worksheet.write(row_index, 29, plugin_family)
                                worksheet.write(row_index, 30, plugin_file_name)
                                worksheet.write(row_index, 31, plugin_version)
                                if plugin_publication_date is not None:
                                    worksheet.write_datetime(
                                        row_index,
                                        32,
                                        nfr.plugin.plugin_date(plugin_publication_date),
                                        date_format,
                                    )
                                else:
                                    worksheet.write_blank(row_index, 32, None)
                                if plugin_modification_date is not None:
                                    worksheet.write_datetime(
                                        row_index,
                                        33,
                                        nfr.plugin.plugin_date(
                                            plugin_modification_date
                                        ),
                                        date_format,
                                    )
                                else:
                                    worksheet.write_blank(row_index, 33, None)
                                worksheet.write(row_index, 34, plugin_description)
                                worksheet.write(row_index, 35, solution)
                                if plugin_output is not None:
                                    worksheet.write_string(row_index, 36, plugin_output)
                                else:
                                    worksheet.write_blank(row_index, 36, None)
                                if plugin_cves:
                                    worksheet.write_number(
                                        row_index, 37, len(plugin_cves)
                                    )
                                    worksheet.write_string(
                                        row_index, 38, ",".join(plugin_cves)
                                    )
                                else:
                                    worksheet.write_number(row_index, 37, 0)
                                    worksheet.write_blank(row_index, 38, None)
                                if exploit_available is not None:
                                    worksheet.write_string(
                                        row_index, 39, exploit_available
                                    )
                                else:
                                    worksheet.write_blank(row_index, 39, None)
                                if exploit_code_maturity is not None:
                                    worksheet.write_string(
                                        row_index, 40, exploit_code_maturity
                                    )
                                else:
                                    worksheet.write_blank(row_index, 40, None)
                                if exploit_framework_metasploit is not None:
                                    worksheet.write_string(
                                        row_index, 41, exploit_framework_metasploit
                                    )
                                else:
                                    worksheet.write_blank(row_index, 41, None)
                                if exploitability_ease is not None:
                                    worksheet.write_string(
                                        row_index, 42, exploitability_ease
                                    )
                                else:
                                    worksheet.write_blank(row_index, 42, None)
                            elif (
                                self.report_vulnerabilities_debug_data_enabled
                                and self.report_vulnerabilities_none_skip
                            ):
                                if risk_factor == "None":
                                    number_of_rows -= 1
                                    row_index -= 1
                                elif risk_factor != "None":
                                    worksheet.set_column("H:I", 10)
                                    worksheet.set_column("K:K", 10)
                                    worksheet.set_column("M:M", 7)
                                    worksheet.set_column("N:N", 10)
                                    worksheet.set_column("P:P", 12)
                                    worksheet.set_column("Q:AC", 10)
                                    worksheet.set_column("AG:AH", 10)
                                    worksheet.set_column("AL:AL", 13)
                                    worksheet.write(row_index, 0, host_scanner_ip)
                                    worksheet.write(row_index, 1, scan_report_name)
                                    worksheet.write(
                                        row_index, 2, nessus_scan_file_name_with_path
                                    )
                                    worksheet.write(row_index, 3, host_report_host_name)
                                    worksheet.write(
                                        row_index, 4, host_resolved_hostname
                                    )
                                    worksheet.write(row_index, 5, host_resolved_fqdn)
                                    worksheet.write(row_index, 6, host_resolved_ip)
                                    worksheet.write(row_index, 7, "yes")
                                    worksheet.write(
                                        row_index, 8, host_credentialed_checks
                                    )
                                    worksheet.write(row_index, 9, scan_policy_name)
                                    worksheet.write(row_index, 10, protocol)
                                    worksheet.write(row_index, 11, service_name)
                                    worksheet.write(row_index, 12, port)
                                    worksheet.write(row_index, 13, plugin_id)
                                    worksheet.write(row_index, 14, plugin_name)
                                    worksheet.write(row_index, 15, plugin_type)
                                    worksheet.write_number(row_index, 16, int(severity))
                                    worksheet.write(row_index, 17, severity_label)
                                    worksheet.write(row_index, 18, risk_factor)
                                    if cvssv2_base_score is not None:
                                        worksheet.write_number(
                                            row_index, 19, float(cvssv2_base_score)
                                        )
                                    else:
                                        worksheet.write_blank(row_index, 19, None)
                                    worksheet.write(
                                        row_index, 20, cvssv2_base_score_label
                                    )
                                    if cvssv3_base_score is not None:
                                        worksheet.write_number(
                                            row_index, 21, float(cvssv3_base_score)
                                        )
                                    else:
                                        worksheet.write_blank(row_index, 21, None)
                                    worksheet.write(
                                        row_index, 22, cvssv3_base_score_label
                                    )
                                    if cvssv4_base_score is not None:
                                        worksheet.write_number(
                                            row_index, 23, float(cvssv4_base_score)
                                        )
                                    else:
                                        worksheet.write_blank(row_index, 23, None)
                                    worksheet.write(
                                        row_index, 24, cvssv4_base_score_label
                                    )
                                    if vpr_score is not None:
                                        worksheet.write_number(
                                            row_index, 25, float(vpr_score)
                                        )
                                    else:
                                        worksheet.write_blank(row_index, 25, None)
                                    worksheet.write(row_index, 26, vpr_score_label)
                                    if epss_score is not None:
                                        worksheet.write_number(
                                            row_index, 27, float(epss_score)
                                        )
                                        percent_format = workbook.add_format(
                                            {"num_format": "0.0%"}
                                        )
                                        worksheet.write_number(
                                            row_index,
                                            28,
                                            float(epss_score),
                                            percent_format,
                                        )
                                    else:
                                        worksheet.write_blank(row_index, 27, None)
                                        worksheet.write_blank(row_index, 28, None)
                                    worksheet.write(row_index, 29, plugin_family)
                                    worksheet.write(row_index, 30, plugin_file_name)
                                    worksheet.write(row_index, 31, plugin_version)
                                    if plugin_publication_date is not None:
                                        worksheet.write_datetime(
                                            row_index,
                                            32,
                                            nfr.plugin.plugin_date(
                                                plugin_publication_date
                                            ),
                                            date_format,
                                        )
                                    else:
                                        worksheet.write_blank(row_index, 32, None)
                                    if plugin_modification_date is not None:
                                        worksheet.write_datetime(
                                            row_index,
                                            33,
                                            nfr.plugin.plugin_date(
                                                plugin_modification_date
                                            ),
                                            date_format,
                                        )
                                    else:
                                        worksheet.write_blank(row_index, 33, None)
                                    worksheet.write(row_index, 34, plugin_description)
                                    worksheet.write(row_index, 35, solution)
                                    if plugin_output is not None:
                                        worksheet.write_string(
                                            row_index, 36, plugin_output
                                        )
                                    else:
                                        worksheet.write_blank(row_index, 36, None)
                                    if plugin_cves:
                                        worksheet.write_number(
                                            row_index, 37, len(plugin_cves)
                                        )
                                        worksheet.write_string(
                                            row_index, 38, ",".join(plugin_cves)
                                        )
                                    else:
                                        worksheet.write_number(row_index, 37, 0)
                                        worksheet.write_blank(row_index, 38, None)
                                    if exploit_available is not None:
                                        worksheet.write_string(
                                            row_index, 39, exploit_available
                                        )
                                    else:
                                        worksheet.write_blank(row_index, 39, None)
                                    if exploit_code_maturity is not None:
                                        worksheet.write_string(
                                            row_index, 40, exploit_code_maturity
                                        )
                                    else:
                                        worksheet.write_blank(row_index, 40, None)
                                    if exploit_framework_metasploit is not None:
                                        worksheet.write_string(
                                            row_index, 41, exploit_framework_metasploit
                                        )
                                    else:
                                        worksheet.write_blank(row_index, 41, None)
                                    if exploitability_ease is not None:
                                        worksheet.write_string(
                                            row_index, 42, exploitability_ease
                                        )
                                    else:
                                        worksheet.write_blank(row_index, 42, None)

                    end_time = time.time()
                    elapsed_time = end_time - start_time
                    elapsed_time_parsed = time.strftime(
                        "%H:%M:%S", time.gmtime(elapsed_time)
                    )

                    self.log_emitter(
                        "info ",
                        file_to_pars_full_name,
                        f"[elapsed_time={elapsed_time_parsed}]",
                    )
                    self.log_emitter("end  ", file_to_pars_full_name)

                except Exception as e:
                    number_of_files_with_errors += 1
                    traceback.print_exc()
                    self.log_emitter(
                        "info",
                        "ERROR Parsing ["
                        + str(nessus_scan_file_number)
                        + "/"
                        + str(len(list_of_source_files))
                        + "] nessus files",
                    )
                    self.log_emitter("info", e)

        # Apply autofilter to the final worksheet
        if rows_in_current_sheet > 0:
            worksheet.autofilter(0, 0, rows_in_current_sheet, number_of_columns - 1)

            if self.report_vulnerabilities_none_filter_out:
                risk_factors = ["Critical", "High", "Medium", "Low"]
                if not self.report_vulnerabilities_debug_data_enabled:
                    worksheet.filter_column_list(12, risk_factors)
                else:
                    worksheet.filter_column_list(16, risk_factors)

    def create_worksheet_for_noncompliance(self, workbook, list_of_source_files):
        """
        Function create spreadsheet with noncompliance sum-up in given workbook for selected files.
        :param workbook: workbook where spreadsheet are created
        :param list_of_source_files: list of selected files
        """
        report_name = "noncompliance"
        self.report_counter += 1
        info_report = (
            "Report: "
            + str(self.report_counter)
            + "/"
            + str(self.number_of_selected_reports)
        )

        worksheet = workbook.add_worksheet(report_name)

        cell_format_bold = workbook.add_format({"bold": True})
        worksheet.set_row(0, None, cell_format_bold)

        # print('>>>>>>>>>>>>>>>> ', self.report_noncompliance_debug_data_enabled)

        if not self.report_noncompliance_debug_data_enabled:
            headers = [
                # "Nessus scanner IP",
                # "Nessus scan name",
                # "Nessus file name",
                "Target",
                "Hostname",
                "FQDN",
                "IP",
                "Scanned",
                "Credentialed checks",
                # "Policy name",
                "Plugin ID",
                "Plugin name",
                "Plugin type",
                # "Risk Factor",
                "Plugin family",
                # "Compliance plugin file",
                # "Plugin file name",
                "Plugin version",
                "Plugin publication date",
                "Plugin modification date",
                "Check name",
                "Audit file name",
                # "Check ID",
                "Current value",
                # "Uname",
                "Description",
                "Check status",
                "Reference",
                "Error",
            ]
        else:
            headers = [
                "Nessus scanner IP",
                "Nessus scan name",
                "Nessus file name",
                "Target",
                "Hostname",
                "FQDN",
                "IP",
                "Scanned",
                "Credentialed checks",
                "Policy name",
                "Plugin ID",
                "Plugin name",
                "Plugin type",
                "Risk Factor",
                "Plugin family",
                "Compliance plugin file",
                "Plugin file name",
                "Plugin version",
                "Plugin publication date",
                "Plugin modification date",
                "Check name",
                "Audit file name",
                "Check ID",
                "Current value",
                "Uname",
                "Description",
                "Check status",
                "Reference",
                "Error",
            ]
        number_of_columns = len(headers)
        # print('Number of columns: ' + str(number_of_columns))

        worksheet.set_column(0, number_of_columns - 1, 18)

        debug_columns_list = [0, 1, 2, 9, 13, 15, 16, 22, 24]

        for column_index, header in enumerate(headers):
            if (
                self.report_noncompliance_debug_data_enabled
                and column_index in debug_columns_list
            ):
                cell_format_bold_blue = workbook.add_format()
                cell_format_bold_blue.set_bold()
                cell_format_bold_blue.set_font_color("blue")
                worksheet.write(0, column_index, header, cell_format_bold_blue)
            else:
                worksheet.write(0, column_index, header)

        worksheet.freeze_panes(1, 0)  # Freeze the first row.

        nessus_scan_file_number = 0
        number_of_files_with_errors = 0

        number_of_rows = 0
        row_index = 0
        row_index_per_file = 0
        for scan_file in list_of_source_files:
            source_file_type = ""
            files_to_pars = []
            if fnmatch.fnmatch(scan_file, "*.nessus"):
                files_to_pars.append(scan_file)
                source_file_type = "nessus"
            elif fnmatch.fnmatch(scan_file, "*.zip"):
                source_file_type = "zip"
                zip_source = zipfile.ZipFile(scan_file)
                zip_files_list = zip_source.namelist()
                for zip_file in zip_files_list:
                    if fnmatch.fnmatch(zip_file, "*.nessus"):
                        files_to_pars.append(zip_file)

            for file_to_pars in files_to_pars:
                row_index_per_file += 1
                nessus_scan_file_number = nessus_scan_file_number + 1
                info_file = (
                    ", File: "
                    + str(row_index_per_file)
                    + "/"
                    + str(len(list_of_source_files))
                )

                try:
                    file_to_pars_full_name = ""
                    source_file_size = 0
                    if source_file_type == "nessus":
                        root = nfr.file.nessus_scan_file_root_element(file_to_pars)
                        scan_file_source = nfr.scan.scan_file_source(root)
                        file_to_pars_full_name = (
                            nfr.file.nessus_scan_file_name_with_path(file_to_pars)
                        )
                        source_file_size = nfa.utilities.size_of_file_human(
                            file_to_pars_full_name
                        )
                    elif source_file_type == "zip":
                        source_file_size = nfa.utilities.size_of_file_inside_zip_human(
                            zip_source, file_to_pars
                        )
                        file_to_pars_full_name = (
                            f"{nfr.file.nessus_scan_file_name_with_path(scan_file)} ["
                            f"{zip_source.getinfo(file_to_pars).filename}]"
                        )
                        file_to_pars = zip_source.open(file_to_pars)
                        root = nfr.file.nessus_scan_file_root_element(file_to_pars)
                        scan_file_source = nfr.scan.scan_file_source(root)

                    start_time = time.time()
                    self.log_emitter("start", file_to_pars_full_name)
                    self.file_analysis_started.emit(1)
                    self.log_emitter(
                        "info ",
                        file_to_pars_full_name,
                        f"[scan_file_source={scan_file_source}]",
                    )
                    self.log_emitter(
                        "info ",
                        file_to_pars_full_name,
                        f"[source_file_size={source_file_size}]",
                    )
                    self.log_emitter(
                        "info ", file_to_pars_full_name, f"[report_type={report_name}]"
                    )
                    self.log_emitter(
                        "info ",
                        file_to_pars_full_name,
                        f"[number_of_target_hosts={nfr.scan.number_of_target_hosts(root)}]",
                    )
                    self.log_emitter(
                        "info ",
                        file_to_pars_full_name,
                        f"[number_of_scanned_hosts={nfr.scan.number_of_scanned_hosts(root)}]",
                    )
                    self.log_emitter(
                        "info ",
                        file_to_pars_full_name,
                        f"[number_of_scanned_hosts_with_credentials="
                        f"{nfr.scan.number_of_scanned_hosts_with_credentialed_checks_yes(root)}]",
                    )

                    date_format = workbook.add_format(
                        {"num_format": "yyyy-mm-dd hh:mm:ss"}
                    )

                    scan_report_name = nfr.scan.report_name(root)
                    nessus_scan_file_name_with_path = (
                        nfr.file.nessus_scan_file_name_with_path(file_to_pars_full_name)
                    )
                    scan_policy_name = nfr.scan.policy_name(root)

                    report_host_counter = 0
                    number_of_report_hosts = nfr.scan.number_of_scanned_hosts(root)

                    info_host = (
                        ", Host: "
                        + str(report_host_counter)
                        + "/"
                        + str(number_of_report_hosts)
                    )
                    info_final = info_report + info_file + info_host
                    self.print_status_bar_info.emit(info_final)

                    for report_host in nfr.scan.report_hosts(root):
                        report_host_counter += 1
                        self.progress.emit(report_host_counter, number_of_report_hosts)

                        info_host = (
                            ", Host: "
                            + str(report_host_counter)
                            + "/"
                            + str(number_of_report_hosts)
                        )
                        info_final = info_report + info_file + info_host
                        self.print_status_bar_info.emit(info_final)

                        report_items_per_host = nfr.host.report_items(report_host)
                        host_scanner_ip = nfr.host.scanner_ip(root, report_host)
                        host_report_host_name = nfr.host.report_host_name(report_host)
                        host_resolved_hostname = nfr.host.resolved_hostname(report_host)
                        host_resolved_fqdn = nfr.host.resolved_fqdn(report_host)
                        host_resolved_ip = nfr.host.resolved_ip(report_host)
                        host_credentialed_checks = nfr.host.credentialed_checks(
                            root, report_host
                        )

                        for report_item in report_items_per_host:
                            if nfr.plugin.compliance_plugin(report_item):
                                compliance_plugin = "true"
                                row_index += 1
                                number_of_rows += 1
                                compliance_check_name = (
                                    nfr.plugin.compliance_check_item_value(
                                        report_item, "cm:compliance-check-name"
                                    )
                                )
                                compliance_check_audit_file = (
                                    nfr.plugin.compliance_check_item_value(
                                        report_item, "cm:compliance-audit-file"
                                    )
                                )
                                compliance_check_check_id = (
                                    nfr.plugin.compliance_check_item_value(
                                        report_item, "cm:compliance-check-id"
                                    )
                                )
                                compliance_check_actual_value = (
                                    nfr.plugin.compliance_check_item_value(
                                        report_item, "cm:compliance-actual-value"
                                    )
                                )
                                compliance_check_uname = (
                                    nfr.plugin.compliance_check_item_value(
                                        report_item, "cm:compliance-uname"
                                    )
                                )
                                compliance_check_info = (
                                    nfr.plugin.compliance_check_item_value(
                                        report_item, "cm:compliance-info"
                                    )
                                )
                                compliance_check_result = (
                                    nfr.plugin.compliance_check_item_value(
                                        report_item, "cm:compliance-result"
                                    )
                                )
                                compliance_check_reference = (
                                    nfr.plugin.compliance_check_item_value(
                                        report_item, "cm:compliance-reference"
                                    )
                                )
                                compliance_check_error = (
                                    nfr.plugin.compliance_check_item_value(
                                        report_item, "cm:compliance-error"
                                    )
                                )

                                plugin_id = int(
                                    nfr.plugin.report_item_value(
                                        report_item, "pluginID"
                                    )
                                )
                                plugin_name = nfr.plugin.report_item_value(
                                    report_item, "pluginName"
                                )
                                plugin_type = nfr.plugin.report_item_value(
                                    report_item, "plugin_type"
                                )
                                risk_factor = nfr.plugin.report_item_value(
                                    report_item, "risk_factor"
                                )
                                plugin_family = nfr.plugin.report_item_value(
                                    report_item, "pluginFamily"
                                )
                                plugin_file_name = nfr.plugin.report_item_value(
                                    report_item, "fname"
                                )
                                plugin_version = nfr.plugin.report_item_value(
                                    report_item, "script_version"
                                )
                                plugin_publication_date = nfr.plugin.report_item_value(
                                    report_item, "plugin_publication_date"
                                )
                                plugin_modification_date = nfr.plugin.report_item_value(
                                    report_item, "plugin_modification_date"
                                )

                                if not self.report_noncompliance_debug_data_enabled:
                                    worksheet.write(row_index, 0, host_report_host_name)
                                    worksheet.write(
                                        row_index, 1, host_resolved_hostname
                                    )
                                    worksheet.write(row_index, 2, host_resolved_fqdn)
                                    worksheet.write(row_index, 3, host_resolved_ip)
                                    worksheet.write(row_index, 4, "yes")
                                    worksheet.write(
                                        row_index, 5, host_credentialed_checks
                                    )
                                    worksheet.write(row_index, 6, plugin_id)
                                    worksheet.write(row_index, 7, plugin_name)
                                    worksheet.write(row_index, 8, plugin_type)
                                    worksheet.write(row_index, 9, plugin_family)
                                    worksheet.write(row_index, 10, plugin_version)
                                    worksheet.write_datetime(
                                        row_index,
                                        11,
                                        nfr.plugin.plugin_date(plugin_publication_date),
                                        date_format,
                                    )
                                    worksheet.write_datetime(
                                        row_index,
                                        12,
                                        nfr.plugin.plugin_date(
                                            plugin_modification_date
                                        ),
                                        date_format,
                                    )
                                    worksheet.write(
                                        row_index, 13, compliance_check_name
                                    )
                                    worksheet.write(
                                        row_index, 14, compliance_check_audit_file
                                    )
                                    worksheet.write(
                                        row_index, 15, compliance_check_actual_value
                                    )
                                    worksheet.write(
                                        row_index, 16, compliance_check_info
                                    )
                                    worksheet.write(
                                        row_index, 17, compliance_check_result
                                    )
                                    worksheet.write(
                                        row_index, 18, compliance_check_reference
                                    )
                                    worksheet.write(
                                        row_index, 19, compliance_check_error
                                    )
                                else:
                                    worksheet.write(row_index, 0, host_scanner_ip)
                                    worksheet.write(row_index, 1, scan_report_name)
                                    worksheet.write(
                                        row_index, 2, nessus_scan_file_name_with_path
                                    )
                                    worksheet.write(row_index, 3, host_report_host_name)
                                    worksheet.write(
                                        row_index, 4, host_resolved_hostname
                                    )
                                    worksheet.write(row_index, 5, host_resolved_fqdn)
                                    worksheet.write(row_index, 6, host_resolved_ip)
                                    worksheet.write(row_index, 7, "yes")
                                    worksheet.write(
                                        row_index, 8, host_credentialed_checks
                                    )
                                    worksheet.write(row_index, 9, scan_policy_name)
                                    worksheet.write(row_index, 10, plugin_id)
                                    worksheet.write(row_index, 11, plugin_name)
                                    worksheet.write(row_index, 12, plugin_type)
                                    worksheet.write(row_index, 13, risk_factor)
                                    worksheet.write(row_index, 14, plugin_family)
                                    worksheet.write(row_index, 15, compliance_plugin)
                                    worksheet.write(row_index, 16, plugin_file_name)
                                    worksheet.write(row_index, 17, plugin_version)
                                    worksheet.write_datetime(
                                        row_index,
                                        18,
                                        nfr.plugin.plugin_date(plugin_publication_date),
                                        date_format,
                                    )
                                    worksheet.write_datetime(
                                        row_index,
                                        19,
                                        nfr.plugin.plugin_date(
                                            plugin_modification_date
                                        ),
                                        date_format,
                                    )
                                    worksheet.write(
                                        row_index, 20, compliance_check_name
                                    )
                                    worksheet.write(
                                        row_index, 21, compliance_check_audit_file
                                    )
                                    worksheet.write(
                                        row_index, 22, compliance_check_check_id
                                    )
                                    worksheet.write(
                                        row_index, 23, compliance_check_actual_value
                                    )
                                    worksheet.write(
                                        row_index, 24, compliance_check_uname
                                    )
                                    worksheet.write(
                                        row_index, 25, compliance_check_info
                                    )
                                    worksheet.write(
                                        row_index, 26, compliance_check_result
                                    )
                                    worksheet.write(
                                        row_index, 27, compliance_check_reference
                                    )
                                    worksheet.write(
                                        row_index, 28, compliance_check_error
                                    )

                    end_time = time.time()
                    elapsed_time = end_time - start_time
                    elapsed_time_parsed = time.strftime(
                        "%H:%M:%S", time.gmtime(elapsed_time)
                    )

                    self.log_emitter(
                        "info ",
                        file_to_pars_full_name,
                        f"[elapsed_time={elapsed_time_parsed}]",
                    )
                    self.log_emitter("end  ", file_to_pars_full_name)

                except Exception as e:
                    number_of_files_with_errors += 1
                    traceback.print_exc()
                    self.log_emitter(
                        "info",
                        "ERROR Parsing ["
                        + str(nessus_scan_file_number)
                        + "/"
                        + str(len(list_of_source_files))
                        + "] nessus files",
                    )
                    self.log_emitter("info", e)

        if number_of_rows > 0:
            worksheet.autofilter(0, 0, number_of_rows, number_of_columns - 1)

    def log_emitter(self, action_name, source_file_name, additional_info=""):
        """
        Function emits information from thread to display it in GUI.

        :param action_name: information about action name
        :param source_file_name: information about related source file
        :param additional_info: add more information if needed
        """
        notification = f"[action={action_name}] [source_file_name={source_file_name}] {additional_info}"
        self.signal.emit(notification)


def main():
    if getattr(sys, 'frozen', False):
        os.chdir(os.path.dirname(sys.executable))
        
    app = QApplication(sys.argv)
    form = MainWindow()

    app_name = nfa.__about__.__title__
    app_version = nfa.__about__.__version__
    app_version_release_date = nfa.__about__.__release_date__
    app.setApplicationName(app_name)
    app.setApplicationVersion(app_version)
    name = app.applicationName()
    # version = app.applicationVersion()

    app_window_title = name
    form.setWindowTitle(app_window_title)
    # print(app_name, app_version, app_version_release_date)

    # app_icon_file_name_png = 'LimberDuck-NFA.png'
    # app_icon_file_name_png_to_ico = nfa.utilities.png_to_ico(app_icon_file_name_png)
    # app_icon_file_name_ico_to_base64 = nfa.utilities.file_to_base64(app_icon_file_name_png_to_ico)
    # app_icon_file_name_ico = nfa.utilities.base64_to_ico(app_icon_file_name_ico_to_base64,app_icon_file_name_png)
    # app_icon_file_name_ico = 'LimberDuck-NFA.ico'

    icon_file_name = nfa.__about__.__icon__
    nfa.utilities.base64_to_ico(nfa.ico, icon_file_name)

    app.setWindowIcon(QIcon(icon_file_name))

    os.remove(icon_file_name)

    form.show()
    QTimer.singleShot(5, form.display_update_window)
    sys.exit(app.exec_())
