<?php

use idoit\AddOn\ActivatableInterface;
use idoit\AddOn\AuthableInterface;
use idoit\AddOn\ExtensionProviderInterface;
use idoit\AddOn\InstallableInterface;
use idoit\AddOn\RoutingAwareInterface;
use idoit\Component\Helper\Ip;
use idoit\Component\Helper\Unserialize;
use idoit\Component\Logger;
use idoit\Context\Context;
use idoit\Module\Cmdb\Model\Matcher\MatchDao;
use idoit\Module\SyneticsJdisc\Auth;
use idoit\Module\SyneticsJdisc\Graphql\Connector;
use idoit\Module\SyneticsJdisc\Helper\JDiscProgressBarInterface;
use idoit\Module\SyneticsJdisc\JDiscExtension;
use idoit\Module\SyneticsJdisc\Model\JDiscCommandQueue;
use idoit\Module\SyneticsJdisc\Model\Mapping;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\Filesystem\Filesystem;
use Symfony\Component\Routing\Loader\PhpFileLoader;

/**
 * i-doit
 *
 * JDisc module
 *
 * @package     i-doit
 * @subpackage  Modules
 * @copyright   synetics GmbH
 * @license     http://www.i-doit.com/license
 * @since       0.9.9-9
 */
class isys_module_synetics_jdisc extends isys_module implements AuthableInterface, InstallableInterface, ActivatableInterface,  RoutingAwareInterface, ExtensionProviderInterface
{
    // Define, if this module shall be displayed in the named menus.
    const DISPLAY_IN_MAIN_MENU = true;
    const MAIN_MENU_REWRITE_LINK = true;

    // Currently supported JDisc Version
    const C__MODULE__JDISC__VERSION = 3.0;

    /**
     * Root node.
     */
    const C__ROOT = 'jdisc';

    /**
     * Node for import.
     */
    const C__IMPORT = 'import';

    /**
     * Import Mode Create
     */
    const C__IMPORT_MODE__CREATE = 1;

    /**
     * Import Mode Update
     */
    const C__IMPORT_MODE__UPDATE = 2;

    /**
     * Import Mode Overwrite
     */
    const C__IMPORT_MODE__OVERWRITE = 3;

    /**
     * Import Mode Update (newly discovered)
     */
    const C__IMPORT_MODE__UPDATE_NEW_DISCOVERY = 4;

    /**
     * Import Mode Overwrite (newly discovered)
     */
    const C__IMPORT_MODE__OVERWRITE_NEW_DISCOVERY = 5;

    /**
     * Import Mode Create newly scanned devices only
     */
    const C__IMPORT_MODE__CREATE_ONLY_NEW_DEVICES = 6;

    /**
     * Import Mode Update exsiting devices only
     */
    const C__IMPORT_MODE__UPDATE_EXISTING_ONLY = 7;

    /**
     * Constant index for matching for export
     */
    const C__EXPORT_MATCHING_PROFILE = 'matching_profile';

    /**
     * @var bool
     */
    private static $m_licenced = true;

    /**
     * Flag to define if all blade connections shall be imported.
     *
     * @var  boolean
     */
    private $m_all_blade_connections;

    /**
     * Flag to define if all clusters shall be imported.
     *
     * @var  boolean
     */
    private $m_all_clusters;

    /**
     * Flag to define if all networks shall be imported.
     *
     * @var  boolean
     */
    private $m_all_networks;

    /**
     * Layer 3 network rules (whitelist|blacklist)
     *
     * @var  integer
     */
    private int $m_l3_rules;

    /**
     * Layer 3 network filter, multiline string with ranges, nets or IPs:
     * - 127.0.0.1-127.0.10.255
     * - 10.40.55.0/24
     * - 10.40.55.7
     *
     * @var  string
     */
    private string $m_l3_filter;

    /**
     * Layer 3 network location mode
     *
     * @var integer
     */
    private int $l3LocationMode;

    /**
     * Layer 3 network location
     *
     * @var integer
     */
    private int $l3Location;

    /**
     * Defines if all objects without title should be imported or not
     */
    private bool $m_all_no_title_objects;

    /**
     * Flag to define if all software shall be imported.
     */
    private bool $m_all_software;

    /**
     * Software location mode
     *
     * @var  int
     */
    private int $softwareLocationMode;

    /**
     * Software network location
     *
     * @var  int
     */
    private int $softwareLocation;

    /**
     * Flag to define if software licences shall be considered while importing software relations
     */
    private bool $m_software_licences;

    /**
     * Flag to define if software services shall be considered while importing software relations
     *
     * @var boolean
     */
    private $importSoftwareServices;

    /**
     * Cache array for profiles.
     *
     * @var  array
     */
    private $m_cached_profile;

    /**
     * All chassis types in JDisc
     */
    private $m_chassis_types = [];

    /**
     * Caches all found clusters
     *
     * @var array
     */
    private $m_cluster_cache;

    /**
     * Instance of module DAO.
     *
     * @var  isys_jdisc_dao
     */
    public $m_dao;

    /**
     * Instance of database component
     *
     * @var isys_component_database
     */
    private $m_db;

    /**
     * @var isys_export_cmdb_object
     */
    private $m_export_obj = null;

    /**
     * Flag to define if custom attributes shall be imported
     */
    private bool $m_import_custom_attributes;

    private bool $m_is_jedi;

    private Logger $m_log;

    /**
     * @var array
     */
    private const NOT_IMPORTED_FIELDS = [
        'id',
        'last_scanned_device',
        'jdisc_server'
    ];

    /**
     * Array with Management Device connections
     *
     * @var array
     */
    private $m_management_device_con_arr = [];

    /**
     * Mode for import
     *
     * @var    integer
     */
    private $m_mode;

    /**
     * Nodes.
     *
     * @var  array
     */
    private $m_nodes = [];

    /**
     * Current jdisc server id
     *
     * @var int|null
     */
    private $m_server_id = null;

    /**
     * Flag do define if import considers default templates from object types
     *
     * @var boolean
     */
    private $m_used_default_templates;

    /**
     * User request.
     *
     * @var  isys_module_request
     */
    private $moduleRequest;

    /**
     * Array with VM connections.
     *
     * @var  array
     */
    private $m_vm_con_arr = [];

    public bool $importCloudSubscriptions = false;

    private bool $importCreateCloudUsers = false;

    private bool $importConnectionEndpoint = false;

    /**
     * Cache objtypes with the template id
     *
     * @var array
     */
    private $m_obj_type_tpls = [];

    /**
     * Cache of unknown device IDs which do not exist in the i-doit system so that we don´t have to search for them again
     *
     * @var array
     */
    private static $m_unknownDeviceIDs = [];

    /**
     * MAC address filter type
     *
     * @var int
     */
    private int $macFilterType = C__LIST_TYPE__BLACKLIST;

    /**
     * MAC address filter, multiline string with addresses
     *
     * @var  string
     */
    private string $macFilterList = '';

    /**
     * Constructor
     */
    public function __construct()
    {
        parent::__construct();

        $this->m_log = new Logger('Import JDisc');
        $this->m_db = isys_application::instance()->container->get('database');
        $this->m_dao = new isys_jdisc_dao($this->m_db, $this->m_log);
    }

    /**
     * @return int|null
     */
    public function getServerId(): ?int
    {
        return $this->m_server_id;
    }

    /**
     * @return bool
     */
    public function isImportCreateCloudUsers(): bool
    {
        return $this->importCreateCloudUsers;
    }

    /**
     * @return bool
     */
    public function isImportConnectionEndpoint(): bool
    {
        return $this->importConnectionEndpoint;
    }

    /**
     * Checks if device ID is unknown
     *
     * @param $p_value
     *
     * @return mixed
     */
    public static function isNonExisting($p_value)
    {
        return self::$m_unknownDeviceIDs[$p_value];
    }

    /**
     * Set device ID as unknown
     *
     * @param $p_value
     */
    public static function setNonExistingID($p_value)
    {
        self::$m_unknownDeviceIDs[$p_value] = true;
    }

    /**
     * Static factory method for instant method chaining.
     *
     * @static
     * @return  isys_module_synetics_jdisc
     */
    public static function factory()
    {
        return new self;
    }

    /**
     * Checks whether all requirements for this module are met.
     */
    public function check_requirements()
    {
        // PDO extension:
        if (!class_exists('PDO')) {
            isys_application::instance()->container->get('notify')->error($this->language->get('LC__PDO__NOT_AVAILABLE'));
        }
    }

    /**
     * @return array
     */
    public function get_cached_profile()
    {
        return $this->m_cached_profile;
    }

    /**
     * Method for retrieving the PDO.
     *
     * @return  isys_component_database_pdo
     */
    public function get_connection($p_config_id = null)
    {
        return $this->m_dao->get_connection($p_config_id);
    }

    /**
     * Setter for m_mode (import mode). Possible constants:
     * - C__APPEND
     * - C__MERGE
     * - C__OVERWRITE
     *
     * @param   integer $p_mode
     *
     * @return  isys_module_synetics_jdisc
     */
    public function set_mode($p_mode)
    {
        $this->m_mode = $p_mode;

        return $this;
    }

    /**
     * @param Logger $logger
     * @return self
     */
    public function setLogger(Logger $logger): self
    {
        $this->m_log = $logger;

        return $this;
    }

    /**
     * Import method.
     *
     * @param   integer $p_group
     * @param   integer $p_profile
     * @param   mixed   $p_id
     * @param   boolean $p_ids_only
     * @param   array   $deviceAssignment
     *
     * @return  PDOStatement|false
     */
    public function retrieve_object_result($p_group, $p_profile, $p_id = null, $p_ids_only = false, $deviceAssignment = [])
    {
        // Create object type assignments by given profile-ID.
        $l_raw_assignments = $this->m_dao->get_object_type_assignments_by_profile($p_profile, null, null, false, false, $deviceAssignment);

        // Ignore empty object type assignments:

        $l_assignments = [];

        $l_dao = isys_cmdb_dao_jdisc::instance($this->m_db);
        $l_chassis_types = [];
        if (defined('C__CATS__CHASSIS')) {
            $l_res = $l_dao->get_objtype_by_cats_id(C__CATS__CHASSIS);
            while ($l_row = $l_res->get_row()) {
                $l_chassis_types[] = $l_row['isys_obj_type__id'];
            }
        }

        $l_activated_objtypes_res = $l_dao->get_objtype(null, false, C__RECORD_STATUS__NORMAL);
        while ($l_row = $l_activated_objtypes_res->get_row()) {
            $l_active_objtypes[$l_row['isys_obj_type__id']] = true;
        }

        foreach ($l_raw_assignments as $l_key => $l_values) {
            if (($l_values['jdisc_type'] === null && $l_values['jdisc_type_customized'] === '' && $l_values['jdisc_os'] === null && $l_values['jdisc_os_customized'] === '' &&
                    $l_values['object_type'] === null) || !isset($l_active_objtypes[$l_values['object_type']])) {
                continue;
            }

            $l_assignments[$l_key] = $l_values;

            if (in_array($l_values['object_type'], $l_chassis_types)) {
                $this->m_chassis_types[] = $l_values['jdisc_type'];
            }

            if ($l_values['port_filter_type'] != null) {
                $l_arr = [];
                if (!empty($l_values['jdisc_type_customized'])) {
                    $l_type = $l_values['jdisc_type_customized'];
                } else {
                    $l_type = $l_values['jdisc_type'];
                }

                if (!empty($l_values['jdisc_os_customized'])) {
                    $l_os = $l_values['jdisc_os_customized'];
                } else {
                    $l_os = (!empty($l_values['jdisc_os'])) ? $l_values['jdisc_os'] : '*';
                    if (is_numeric($l_os) && $l_os > 0) {
                        $l_osdata = $this->m_dao->get_jdisc_operating_systems('osversion', $l_os);
                        if (isset($l_osdata[0]) && is_array($l_osdata[0])) {
                            $l_os = current($l_osdata[0]);
                        }
                    }
                }

                if (is_scalar($l_os)) {
                    $l_cached_arr = isys_jdisc_dao_network::instance($this->m_db)
                        ->get_port_filter();
                    if (is_array($l_cached_arr) && count($l_cached_arr) > 0) {
                        $l_cached_arr[$l_type][$l_os] = $l_values['port_filter'];
                        $l_arr = $l_cached_arr;
                    } else {
                        $l_arr[$l_type][$l_os] = $l_values['port_filter'];
                    }
                    isys_jdisc_dao_network::instance($this->m_db)
                        ->set_port_filter($l_arr);

                    $l_cached_arr = isys_jdisc_dao_network::instance($this->m_db)
                        ->get_port_filter_import_type();
                    if (is_array($l_cached_arr) && count($l_cached_arr) > 0) {
                        $l_cached_arr[$l_type][$l_os] = $l_values['port_filter_type'];
                        $l_arr = $l_cached_arr;
                    } else {
                        $l_arr[$l_type][$l_os] = $l_values['port_filter_type'];
                    }
                    isys_jdisc_dao_network::instance($this->m_db)
                        ->set_port_filter_import_type($l_arr);
                }
            }
        }

        // Retrieve devices to our given group.
        return isys_jdisc_dao_devices::instance($this->m_db)->get_devices_by_profile($p_group, $p_id, $l_assignments, $p_ids_only);
    }

    /**
     * Sets clear mode
     *
     * @param $p_value
     *
     * @return $this
     */
    public function set_clear_mode($p_value = null)
    {
        if ($p_value == isys_import_handler_cmdb::C__OVERWRITE) {
            isys_jdisc_dao_data::activate_clear_mode();
        } else {
            isys_jdisc_dao_data::deactivate_clear_mode();
        }

        return $this;
    }

    /**
     * @param array $categories
     */
    private function getCategoryConst(array $categories)
    {
        $query = 'SELECT GROUP_CONCAT(isysgui_catg__const) as constants FROM isysgui_catg
            WHERE isysgui_catg__id IN (' . implode(',', array_map([$this->m_dao, 'convert_sql_id'], $categories)) . ');';
        $constants = $this->m_dao->retrieve($query)->get_row_value('constants');

        return explode(',', $constants);
    }

    /**
     * Import method.
     *
     * @return  isys_module_synetics_jdisc
     *
     * @param integer $p_profile
     * @param null $unused
     * @param Logger|null $logger
     */
    public function prepare_environment($p_profile, $unused = null, $logger = null)
    {
        Context::instance()->setContextTechnical(Context::CONTEXT_IMPORT_JDISC);

        $this->m_export_obj = new isys_export_cmdb_object('isys_export_type_xml', $this->m_db);

        // Get the profile we shall use.
        $this->m_cached_profile = current($this->m_dao->get_profile($p_profile));
        $l_cached_categories = Unserialize::toArray($this->m_cached_profile['categories']);

        $softwareDao = isys_jdisc_dao_software::instance($this->m_db)->setLogger($logger);
        $deviceDao = isys_jdisc_dao_devices::instance($this->m_db)->setLogger($logger);
        $networkDao = isys_jdisc_dao_network::instance($this->m_db)->setLogger($logger);
        isys_jdisc_dao_cluster::instance($this->m_db)->setLogger($logger);
        isys_jdisc_dao_custom_attributes::instance($this->m_db)->setLogger($logger);
        isys_jdisc_dao_software_database::instance($this->m_db)->setLogger($logger);

        if (isset($this->m_cached_profile['software_filter']) && $this->m_cached_profile['software_filter'] !== '') {
            if (isset($this->m_cached_profile['software_filter_type_regexp']) && (int)$this->m_cached_profile['software_filter_type_regexp'] === 1) {
                $softwareDao->set_software_filter(
                    $this->m_cached_profile['software_filter'],
                    $this->m_cached_profile['software_filter_type'],
                    1
                );
            } else {
                $softwareDao->set_software_filter($this->m_cached_profile['software_filter'], $this->m_cached_profile['software_filter_type']);
            }
        }

        if (isset($this->m_cached_profile['chassis_assigned_modules_objtype']) && $this->m_cached_profile['chassis_assigned_modules_objtype'] > 0) {
            $deviceDao->set_module_objecttype($this->m_cached_profile['chassis_assigned_modules_objtype']);
        }

        if (isset($this->m_cached_profile['chassis_assigned_modules_update_objtype'])) {
            $deviceDao->setModuleUpdateObjtype((bool)$this->m_cached_profile['chassis_assigned_modules_update_objtype']);
        }

        if (is_array($l_cached_categories) && !empty($l_cached_categories)) {
            $this->m_cached_profile['categories'] = array_flip($l_cached_categories);
            $this->m_cached_profile['categoriesConstants'] = $this->getCategoryConst($l_cached_categories);
        } else {
            $this->m_cached_profile['categories'] = null;
            $this->m_cached_profile['categoriesConstants'] = null;
        }

        // Get all virtual machine to host connections only if the category is selected.
        if (is_array($this->m_cached_profile['categories'])) {
            if (defined('C__CATG__VIRTUAL_MACHINE') && isset($this->m_cached_profile['categories'][C__CATG__VIRTUAL_MACHINE])) {
                $this->m_vm_con_arr = $deviceDao->get_virtual_machine_connections();
            }
        }

        // Determine which field will be used for the object title for operating systems
        $softwareDao->setOsFamilyAsObjectTitle($this->m_cached_profile['software_obj_title'] ?? false);
        $softwareDao->setSimpleDatabaseModel($this->m_cached_profile['use_simple_database_mod'] ?? false);

        $this->m_all_software             = (bool)($this->m_cached_profile['import_all_software'] ?? false);
        $this->softwareLocationMode       = $this->m_cached_profile['software_location_import_mode'] ?? 0;
        $this->softwareLocation           = $this->m_cached_profile['software_location'] ?? 0;
        $this->m_all_networks             = (bool)($this->m_cached_profile['import_all_networks'] ?? false);
        $this->m_l3_rules                 = (int)($this->m_cached_profile['network_import_rules'] ?? 0);
        $this->m_l3_filter                = $this->m_cached_profile['network_import_filter'] ?? '';
        $this->l3LocationMode             = $this->m_cached_profile['network_location_import_mode'] ?? 0;
        $this->l3Location                 = $this->m_cached_profile['network_location'] ?? 0;
        $this->m_all_clusters             = (bool)($this->m_cached_profile['import_all_clusters'] ?? false);
        $this->m_all_blade_connections    = (bool)($this->m_cached_profile['import_all_blade_connections'] ?? false);
        $this->m_all_no_title_objects     = (bool)isys_tenantsettings::get('jdisc.import-unidentified-devices', false);
        $this->m_import_custom_attributes = (bool)($this->m_cached_profile['import_custom_attributes'] ?? false);
        $this->m_used_default_templates   = (bool)($this->m_cached_profile['use_default_templates'] ?? false);
        $this->m_is_jedi                  = $this->m_dao->is_jedi_version();
        $this->m_software_licences        = $this->m_is_jedi ? false : ((bool)($this->m_cached_profile['import_software_licences'] ?? false));
        $this->importSoftwareServices     = (bool)($this->m_cached_profile['import_software_services'] ?? false);
        $this->importCloudSubscriptions   = (bool)($this->m_cached_profile['import_cloud_subscriptions'] ?? false);
        $this->importCreateCloudUsers     = (bool)($this->m_cached_profile['import_create_cloud_users'] ?? false);
        $this->importConnectionEndpoint   = (bool)($this->m_cached_profile['import_connection_endpoint'] ?? false);
        $this->macFilterType              = (int)($this->m_cached_profile['mac_filter_type'] ?? 0);
        $this->macFilterList              = $this->m_cached_profile['mac_filter'] ?? '';

        $networkDao->set_import_vlans($this->m_cached_profile['import_all_vlans'] ?? false);
        $networkDao->set_additional_info($this->m_cached_profile['import_type_interfaces'] ?? false);
        $networkDao->setAdoptLocationForAttachedDevices($this->m_cached_profile['adopt_location'] ?? false);

        // Create temporary table mainly for network relevant data
        $networkDao->create_cache_table();
        $deviceDao->prepare_device_environment($this->m_cached_profile);

        // Increase group concat max length to get all attached vlans from cache
        $this->m_db->query('SET SESSION group_concat_max_len = 9999999;');

        $daoData = isys_jdisc_dao_data::instance($this->m_db)->setLogger($logger);

        // Instantiate CiMatcher for jdisc
        isys_jdisc_dao_matching::initialize(($this->m_cached_profile['object_matching'] ?? 1), // Just in case if object_matching is not set for whatever reason
            $this->m_server_id, $daoData);

        return $this;
    }

    /**
     * Check if software licenses can be imported
     *
     * @return bool
     */
    public function check_import_software_licences(): bool
    {
        return $this->m_software_licences;
    }

    /**
     * Set check if software licenses cannot be imported
     *
     * @param bool $p_value
     */
    public function set_check_import_software_licenses(bool $p_value = false)
    {
        $this->m_software_licences = false;
    }

    /**
     * Import method.
     *
     * @param   array $p_row
     * @param   array $p_jdisc_to_idoit Matching from JDisc device id to i-doit object id
     * @param   array $p_object_ids
     *
     * @return  array|false
     */
    public function prepare_object_data(&$p_row, &$p_jdisc_to_idoit, &$p_object_ids = [])
    {
        // We have to reset the logbook entries foreach device
        isys_jdisc_dao_data::reset_logbook_entries();

        $this->m_log->debug('# Start preparing the object data for "' . $p_row['name'] . '"!');

        $l_object = $l_connections = [];
        // use isys_cmdb_dao instead of isys_cmdb_dao_jdisc otherwise devices with the same name but with different data won´t be created
        $l_dao = isys_cmdb_dao_jdisc::instance($this->m_db);

        // Check if object already exists for other mode C__MERGE and C__OVERWRITE.
        $l_object_id = false;
        $l_use_default_template = false;
        $l_default_template = false;
        $newObject = false;
        $l_black_list = $l_cluster = [];

        if ($this->m_mode == isys_import_handler_cmdb::C__MERGE || $this->m_mode == isys_import_handler_cmdb::C__OVERWRITE) {
            $l_object_id = $p_row['identifierObjID'];

            if ($l_object_id > 0) {
                $this->m_log->info('Device id "' . $p_row['id'] . '" found. Using object id "' . $l_object_id . '" for device: "' . $p_row['name'] . '".');
            } else {
                // Last check only if setting jdisc.import-unidentified-devices is on
                if ($p_row['name'] == '' && $this->m_all_no_title_objects === true) {
                    // We have to give the imported device a title otherwise an Object with no title will be created
                    if ($p_row['serialnumber'] != '') {
                        $p_row['name'] = $p_row['serialnumber'];
                    } else {
                        $p_row['name'] = 'JDisc-Device: ' . $p_row['id'];
                    }

                    // last attempt
                    // @see ID-10889 only retrieve object with status normal
                    $l_object_id = $l_dao->get_obj_id_by_title($p_row['name'], $p_row['idoit_obj_type'], C__RECORD_STATUS__NORMAL);
                } elseif ($l_object_id === false && $p_row['name'] === '' && $this->m_all_no_title_objects === false) {
                    $this->m_log->debug('Skipping device with empty title (' . $p_row['type_name'] . ')');
                    isys_ajax_handler_jdisc::$m_additional_stats .= 'INFO: Skipped device with empty title (' . $p_row['type_name'] . ")\n";

                    return false;
                }
            }
        }

        if ($l_object_id && ($status = $l_dao->get_object_status_by_id($l_object_id)) !== C__RECORD_STATUS__NORMAL) {
            $this->m_log->debug($this->getDeviceStatusSkipMessage($p_row['name'] ?? '', $status));
            return false;
        }

        // Default Template
        // Check if the object type has a template
        if ($this->m_used_default_templates === true) {
            if (isset($this->m_obj_type_tpls[$p_row['idoit_obj_type']])) {
                $l_default_template = $this->m_obj_type_tpls[$p_row['idoit_obj_type']];
            } else {
                $l_default_template = $l_dao->get_default_template_by_obj_type($p_row['idoit_obj_type']);
                $this->m_obj_type_tpls[$p_row['idoit_obj_type']] = ($l_default_template === null) ? false : $l_default_template;
            }
        }

        // Build default template
        if ($l_default_template !== false) {
            $l_use_default_template = true;
            // Build array only once per object type
            if (!is_array($l_default_template)) {
                $l_tmp = isys_export_cmdb_object::fetch_exportable_categories();
                $l_transformed = [];
                foreach ($l_tmp as $l_category_type => $l_categories) {
                    foreach ($l_categories as $l_categoryID => $l_crap) {
                        $l_transformed[$l_category_type][] = $l_categoryID;
                    }
                }

                $l_default_template_data = $this->m_export_obj->export($l_default_template, $l_transformed, C__RECORD_STATUS__TEMPLATE)
                    ->get_export();

                $l_template_content = array_pop($l_default_template_data);
                if (is_array($l_template_content)) {
                    unset($l_template_content['head']);
                    foreach ([
                                 C__CMDB__CATEGORY__TYPE_GLOBAL,
                                 C__CMDB__CATEGORY__TYPE_SPECIFIC,
                                 C__CMDB__CATEGORY__TYPE_CUSTOM
                             ] as $l_cattype) {
                        if (isset($l_template_content[$l_cattype])) {
                            $l_template_content_copy = $l_template_content[$l_cattype];
                            foreach ($l_template_content_copy as $l_key => $l_tmp_category) {
                                if (!is_array($l_tmp_category)) {
                                    continue;
                                }

                                if (count($l_tmp_category) === 1) {
                                    unset($l_template_content[$l_cattype][$l_key]);
                                } else {
                                    $l_head = $l_template_content[$l_cattype][$l_key]['head'];
                                    unset($l_template_content[$l_cattype][$l_key]['head']);

                                    foreach ($l_tmp_category as $l_cat_entry => $l_data) {
                                        if ($l_cat_entry === 'head') {
                                            continue;
                                        }

                                        $l_template_content[$l_cattype][$l_key][$l_cat_entry]['data_id'] = null;
                                        foreach ($l_data as $l_prop_index => $l_property) {
                                            unset($l_template_content[$l_cattype][$l_key][$l_cat_entry][$l_prop_index]);
                                            $l_tag = $l_property['tag'];
                                            $l_value = $l_property[C__DATA__VALUE];

                                            if (is_array($l_property[C__DATA__VALUE])) {
                                                if (isset($l_property[C__DATA__VALUE]['ref_id'])) {
                                                    $l_value = $l_property[C__DATA__VALUE]['ref_id'];
                                                } elseif (isset($l_property[C__DATA__VALUE]['id'])) {
                                                    $l_value = $l_property[C__DATA__VALUE]['id'];
                                                    if (isset($l_property[C__DATA__VALUE]['sysid']) && $l_property[C__DATA__VALUE]['type']) {
                                                        // Add object ID so that we know that it exists for the import
                                                        $p_object_ids[$l_value] = (int)$l_value;
                                                    }
                                                } else {
                                                    $l_value = $l_property[C__DATA__VALUE][C__DATA__VALUE];
                                                }
                                            }

                                            if (is_object($l_property[C__DATA__VALUE]) && is_a($l_property[C__DATA__VALUE], 'isys_export_data')) {
                                                $l_value = $l_property[C__DATA__VALUE]->get_data();
                                            }

                                            if (is_array($l_value)) {
                                                if (count($l_value) > 0) {
                                                    foreach ($l_value as $l_value_data) {
                                                        if (isset($l_value_data['sysid'], $l_value_data['id'], $l_value_data['type'])) {
                                                            // Add object ID so that we know that it exists for the import
                                                            $p_object_ids[$l_value_data['id']] = (int)$l_value_data['id'];
                                                        }
                                                    }
                                                } else {
                                                    $currentVal = current($l_value);
                                                    if (is_array($currentVal) && isset($currentVal['sysid'], $currentVal['id'], $currentVal['type'])) {
                                                        // Add object ID so that we know that it exists for the import
                                                        $p_object_ids[$currentVal['id']] = (int)$currentVal['id'];
                                                    }
                                                }
                                            }

                                            $l_property[C__DATA__VALUE] = $l_value;
                                            $l_template_content[$l_cattype][$l_key][$l_cat_entry]['properties'][$l_tag] = $l_property;
                                        }
                                    }

                                    $l_tmp = array_values($l_template_content[$l_cattype][$l_key]);
                                    unset($l_template_content[$l_cattype][$l_key]);

                                    $l_template_content[$l_cattype][$l_key] = [
                                        'title'         => $l_head['title'],
                                        'const'         => $l_head['const'],
                                        'category_type' => $l_head['category_type']
                                    ];
                                    $l_template_content[$l_cattype][$l_key]['category_entities'] = $l_tmp;
                                }
                            }
                        }
                    }
                }
                $this->m_obj_type_tpls[$p_row['idoit_obj_type']] = $l_template_content;
            }
        }

        if (!$l_object_id && $p_row['idoit_obj_type'] && $p_row['name']) {
            if ($p_row['filtered'] === true) {
                $this->m_log->info("Skipping object {$p_row['name']}. Filtered.");
                return false;
            }

            $this->m_log->info('Creating object ' . $p_row['name']);

            $l_object_id = $l_dao->insert_new_obj($p_row['idoit_obj_type'], false, $p_row['name'], null, C__RECORD_STATUS__NORMAL);

            if ($l_dao::object_created_in_current_session($l_object_id)) {
                $eventManager = isys_event_manager::getInstance();
                $eventManager->triggerImportEvent(
                    'C__LOGBOOK_EVENT__OBJECT_CREATED',
                    'Import',
                    $l_object_id,
                    $p_row['idoit_obj_type'],
                    null,
                    serialize([
                        'isys_cmdb_dao_category_g_global::title' => [
                            'from' => '',
                            'to'   => $p_row['name']
                        ]
                    ]),
                    'Created by jdisc import.',
                    null,
                    null,
                    $eventManager->get_import_id(),
                    1,
                    defined_or_default('C__LOGBOOK_SOURCE__JDISC', null)
                );
            }

            $newObject = true;
            if (in_array($l_object_id, $p_jdisc_to_idoit)) {
                $this->m_log->info('Skipping device ' . $p_row['name'] . ' since device with same name already exists and unique checks are enabled.');
                isys_ajax_handler_jdisc::$m_additional_stats .= 'INFO: Skipped device ' . $p_row['name'] .
                    " since device with same name already exists and unique checks are enabled.\n";

                return false;
            }
        }

        if ($l_object_id > 0) {
            // Cache the new object id with the device id as key
            $p_jdisc_to_idoit[$p_row['id']] = $l_object_id;

            $p_object_ids[$l_object_id] = $l_object_id;

            if (!empty($p_row['uniqueid']) && !$p_row['foundByUniqueId']) {
                $jdiDao = isys_cmdb_dao_category_g_jdisc_device_information::instance($this->m_db);
                if (!$jdiDao->getIdentifierDataByUniqueId($p_row['uniqueid'])) {
                    $jdiDao->create_data(
                        [
                            'isys_obj__id' => $l_object_id,
                            'uniqueId'     => $p_row['uniqueid'],
                        ]
                    );
                }
            }

            if ($p_row['name'] == '' && $p_row['serialnumber'] != '') {
                $p_row['name'] = $p_row['serialnumber'];
            } elseif ($p_row['name'] === '' && $p_row['serialnumber'] == '') {
                $p_row['name'] = 'JDisc-Device: ' . $p_row['id'];
            }

            // We now prepare the array for the import.
            $this->m_log->debug('Prepare the core object data');
            $l_object[$l_object_id] = isys_jdisc_dao_devices::instance($this->m_db)
                ->prepare_device_array($p_row, $this->m_mode, $l_object_id);
            $l_object[$l_object_id]['categories'] = [];

            // In case we cannot retrieve the object info
            if ($l_object[$l_object_id] === false) {
                $this->m_log->debug('Skipping device ' . $p_row['name'] . ': Couild not identify object type for jdisc type: ' . $p_row['type_name'] .
                    '. Check your profile.');

                return false;
            }

            $l_activated_categories = $this->m_cached_profile['categories'];
            isys_jdisc_dao_network::instance($this->m_db)
                ->set_object_id($l_object_id);
            // This prepares the location assignment if set
            if (defined('C__CATG__LOCATION') && $p_row['location'] > 0) {
                $l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' .
                C__CATG__LOCATION] = isys_jdisc_dao_devices::instance($this->m_db)
                    ->prepare_location($p_row['location']);
            }

            // This is for object type access point
            if (defined('C__CATS__ACCESS_POINT') && defined('C__OBJTYPE__ACCESS_POINT') && $l_object[$l_object_id]['type']['id'] == C__OBJTYPE__ACCESS_POINT) {
                if (isys_jdisc_dao_data::clear_data() === true) {
                    $this->m_dao->clear_category('isys_cats_access_point_list', $l_object_id, 'C__CATS__ACCESS_POINT', false);
                }

                $this->m_log->debug('Read and preapare access point data');

                $l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_SPECIFIC . '_' .
                C__CATS__ACCESS_POINT] = isys_jdisc_dao_network::instance($this->m_db)
                    ->get_access_point_by_device($p_row['id']);

                if ($l_use_default_template && $newObject) {
                    if (isset($this->m_obj_type_tpls[$p_row['idoit_obj_type']][C__CMDB__CATEGORY__TYPE_SPECIFIC][C__CATS__ACCESS_POINT])) {
                        $l_black_list[C__CMDB__CATEGORY__TYPE_SPECIFIC][C__CATS__ACCESS_POINT] = true;
                        if (isset($l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_SPECIFIC . '_' . C__CATS__ACCESS_POINT]['category_entities'])) {
                            $l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_SPECIFIC . '_' .
                            C__CATS__ACCESS_POINT]['category_entities'] = array_replace_recursive(
                                $this->m_obj_type_tpls[$p_row['idoit_obj_type']][C__CMDB__CATEGORY__TYPE_SPECIFIC][C__CATS__ACCESS_POINT]['category_entities'],
                                $l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_SPECIFIC . '_' . C__CATS__ACCESS_POINT]['category_entities']
                            );
                        } else {
                            $l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_SPECIFIC . '_' .
                            C__CATS__ACCESS_POINT]['category_entities'] = $this->m_obj_type_tpls[$p_row['idoit_obj_type']][C__CMDB__CATEGORY__TYPE_SPECIFIC][C__CATS__ACCESS_POINT]['category_entities'];
                        }
                    }
                }
            }

            // Monitor data processing
            if (defined('C__CATS__MONITOR') && defined('C__OBJTYPE__MONITOR') && $l_object[$l_object_id]['type']['id'] == C__OBJTYPE__MONITOR) {
                $this->m_log->debug('Read and prepare monitor data');

                // Get contents for monitor category.
                $l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_SPECIFIC . '_' .
                C__CATS__MONITOR] = isys_jdisc_dao_devices::instance($this->m_db)
                    ->get_monitor_by_device($p_row['id']);

                if ($l_use_default_template && $newObject) {
                    if (isset($this->m_obj_type_tpls[$p_row['idoit_obj_type']][C__CMDB__CATEGORY__TYPE_SPECIFIC][C__CATS__MONITOR])) {
                        $l_black_list[C__CMDB__CATEGORY__TYPE_SPECIFIC][C__CATS__MONITOR] = true;
                        if (isset($l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_SPECIFIC . '_' . C__CATS__MONITOR]['category_entities'])) {
                            $l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_SPECIFIC . '_' .
                            C__CATS__MONITOR]['category_entities'] = array_replace_recursive(
                                $this->m_obj_type_tpls[$p_row['idoit_obj_type']][C__CMDB__CATEGORY__TYPE_SPECIFIC][C__CATS__MONITOR]['category_entities'],
                                $l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_SPECIFIC . '_' . C__CATS__MONITOR]['category_entities']
                            );
                        } else {
                            $l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_SPECIFIC . '_' .
                            C__CATS__MONITOR]['category_entities'] = $this->m_obj_type_tpls[$p_row['idoit_obj_type']][C__CMDB__CATEGORY__TYPE_SPECIFIC][C__CATS__MONITOR]['category_entities'];
                        }
                    }
                }
            }

            if ($this->m_all_blade_connections && count($this->m_chassis_types) > 0) {
                $this->m_log->debug('Check if object has connection to a blade');
                $l_blade_info = isys_jdisc_dao_devices::instance($this->m_db)
                    ->get_blade_connection($p_row['id'], $this->m_chassis_types);
                if ($l_blade_info !== false) {
                    isys_jdisc_dao_devices::instance($this->m_db)
                        ->set_blade_connection($l_blade_info, $p_row['id']);
                }
            }

            // Special handling with module interfaces
            isys_jdisc_dao_devices::instance($this->m_db)
                ->prepare_modules($p_row['id']);

            // And here we fill the global categories.
            // This order matters!
            if (is_array($l_activated_categories)) {
                // Set Object-ID and Object-Type-ID
                isys_jdisc_dao_devices::instance($this->m_db)
                    ->set_current_object_id($l_object_id);
                isys_jdisc_dao_devices::instance($this->m_db)
                    ->set_current_object_type_id($p_row['idoit_obj_type']);

                // @See ID-5038 Import from SNMP Syslocation
                if (defined('C__CATG__LOCATION') && isset($l_activated_categories[C__CATG__LOCATION])) {
                    $this->m_log->debug('Read and prepare snmp syslocation');
                    $locationData = [];

                    if (isset($l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' . C__CATG__LOCATION])) {
                        $locationData = $l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' . C__CATG__LOCATION];
                    }

                    $l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' .
                    C__CATG__LOCATION] = isys_jdisc_dao_devices::instance($this->m_db)
                        ->prepareSnmpSysLocation($p_row['id'], $l_object_id, $locationData);

                    // @see ID-7685
                    $value = $l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' . C__CATG__LOCATION]['category_entities'][0]['properties']['parent']['value'];

                    if ($value) {
                        $p_object_ids[] = $value;
                    }
                }

                if (defined('C__CATG__GRAPHIC')) {
                    if (isset($l_activated_categories[C__CATG__GRAPHIC])) {
                        // Reset category graphic card, data will be deleted before dataretrieval
                        if (isys_jdisc_dao_data::clear_data() === true) {
                            $this->m_dao->clear_category('isys_catg_graphic_list', $l_object_id, 'C__CATG__GRAPHIC', false);
                        }

                        $this->m_log->debug('Read and prepare graphic card data');
                        // Get the videocontroller(s) of each device.
                        $l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' .
                        C__CATG__GRAPHIC] = isys_jdisc_dao_devices::instance($this->m_db)
                            ->get_videocontroller_by_device($p_row['id']);
                    }

                    if ($l_use_default_template && $newObject) {
                        if (isset($this->m_obj_type_tpls[$p_row['idoit_obj_type']][C__CMDB__CATEGORY__TYPE_GLOBAL][C__CATG__GRAPHIC])) {
                            $l_black_list[C__CMDB__CATEGORY__TYPE_GLOBAL][C__CATG__GRAPHIC] = true;
                            if (isset($l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' . C__CATG__GRAPHIC]['category_entities'])) {
                                $l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' .
                                C__CATG__GRAPHIC]['category_entities'] = array_merge(
                                    $this->m_obj_type_tpls[$p_row['idoit_obj_type']][C__CMDB__CATEGORY__TYPE_GLOBAL][C__CATG__GRAPHIC]['category_entities'],
                                    $l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' . C__CATG__GRAPHIC]['category_entities']
                                );
                            } else {
                                $l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' .
                                C__CATG__GRAPHIC]['category_entities'] = $this->m_obj_type_tpls[$p_row['idoit_obj_type']][C__CMDB__CATEGORY__TYPE_GLOBAL][C__CATG__GRAPHIC]['category_entities'];
                            }
                        }
                    }
                }

                if (defined('C__CATG__UNIVERSAL_INTERFACE')) {
                    if (isset($l_activated_categories[C__CATG__UNIVERSAL_INTERFACE]) && isys_jdisc_dao_software::instance($this->m_db)
                            ->check_table('devicedeviceconnection')) {
                        // Reset category universal interface
                        if (isys_jdisc_dao_data::clear_data() === true) {
                            $this->m_dao->clear_category('isys_catg_ui_list', $l_object_id, 'C__CATG__UNIVERSAL_INTERFACE', false);
                        }

                        $this->m_log->debug('Read and prepare universal interface data');
                        // Get directly attached connections of each device
                        $l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' .
                        C__CATG__UNIVERSAL_INTERFACE] = isys_jdisc_dao_network::instance($this->m_db)
                            ->get_universal_interface_by_device($p_row['id'], $p_jdisc_to_idoit);
                    }

                    if ($l_use_default_template && $newObject) {
                        if (isset($this->m_obj_type_tpls[$p_row['idoit_obj_type']][C__CMDB__CATEGORY__TYPE_GLOBAL][C__CATG__UNIVERSAL_INTERFACE])) {
                            $l_black_list[C__CMDB__CATEGORY__TYPE_GLOBAL][C__CATG__UNIVERSAL_INTERFACE] = true;
                            if (isset($l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' . C__CATG__UNIVERSAL_INTERFACE]['category_entities'])) {
                                $l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' .
                                C__CATG__UNIVERSAL_INTERFACE]['category_entities'] = array_merge(
                                    $this->m_obj_type_tpls[$p_row['idoit_obj_type']][C__CMDB__CATEGORY__TYPE_GLOBAL][C__CATG__UNIVERSAL_INTERFACE]['category_entities'],
                                    $l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' . C__CATG__UNIVERSAL_INTERFACE]['category_entities']
                                );
                            } else {
                                $l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' .
                                C__CATG__UNIVERSAL_INTERFACE]['category_entities'] = $this->m_obj_type_tpls[$p_row['idoit_obj_type']][C__CMDB__CATEGORY__TYPE_GLOBAL][C__CATG__UNIVERSAL_INTERFACE]['category_entities'];
                            }
                        }
                    }
                }

                if (defined('C__CATG__CPU') && isset($l_activated_categories[C__CATG__CPU])) {
                    // Reset category cpu, data will be deleted before dataretrieval
                    if (isys_jdisc_dao_data::clear_data() === true) {
                        $this->m_dao->clear_category('isys_catg_cpu_list', $l_object_id, 'C__CATG__CPU', false);
                    }

                    $this->m_log->debug('Read and prepare CPU data');
                    // Get the processor(s) of each device.
                    $l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' .
                    C__CATG__CPU] = isys_jdisc_dao_devices::instance($this->m_db)
                        ->get_processor_by_device($p_row['id']);
                }

                if ($l_use_default_template && $newObject && defined('C__CATG__CPU')) {
                    if (isset($this->m_obj_type_tpls[$p_row['idoit_obj_type']][C__CMDB__CATEGORY__TYPE_GLOBAL][C__CATG__CPU])) {
                        $l_black_list[C__CMDB__CATEGORY__TYPE_GLOBAL][C__CATG__CPU] = true;
                        if (isset($l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' . C__CATG__CPU]['category_entities'])) {
                            $l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' .
                            C__CATG__CPU]['category_entities'] = array_merge(
                                $this->m_obj_type_tpls[$p_row['idoit_obj_type']][C__CMDB__CATEGORY__TYPE_GLOBAL][C__CATG__CPU]['category_entities'],
                                $l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' . C__CATG__CPU]['category_entities']
                            );
                        } else {
                            $l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' .
                            C__CATG__CPU]['category_entities'] = $this->m_obj_type_tpls[$p_row['idoit_obj_type']][C__CMDB__CATEGORY__TYPE_GLOBAL][C__CATG__CPU]['category_entities'];
                        }
                    }
                }

                if (defined('C__CATG__MEMORY')) {
                    if (isset($l_activated_categories[C__CATG__MEMORY])) {
                        // Reset category memory, data will be deleted before dataretrieval
                        if (isys_jdisc_dao_data::clear_data() === true) {
                            $this->m_dao->clear_category('isys_catg_memory_list', $l_object_id, 'C__CATG__MEMORY', false);
                        }

                        $this->m_log->debug('Read and prepare memory (RAM) data');
                        // Get the memory of each device.
                        $l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' .
                        C__CATG__MEMORY] = isys_jdisc_dao_devices::instance($this->m_db)
                            ->get_memory_by_device($p_row['id']);
                    }

                    if ($l_use_default_template && $newObject) {
                        if (isset($this->m_obj_type_tpls[$p_row['idoit_obj_type']][C__CMDB__CATEGORY__TYPE_GLOBAL][C__CATG__MEMORY])) {
                            $l_black_list[C__CMDB__CATEGORY__TYPE_GLOBAL][C__CATG__MEMORY] = true;
                            if (isset($l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' . C__CATG__MEMORY]['category_entities'])) {
                                $l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' .
                                C__CATG__MEMORY]['category_entities'] = array_merge(
                                    $this->m_obj_type_tpls[$p_row['idoit_obj_type']][C__CMDB__CATEGORY__TYPE_GLOBAL][C__CATG__MEMORY]['category_entities'],
                                    $l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' . C__CATG__MEMORY]['category_entities']
                                );
                            } else {
                                $l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' .
                                C__CATG__MEMORY]['category_entities'] = $this->m_obj_type_tpls[$p_row['idoit_obj_type']][C__CMDB__CATEGORY__TYPE_GLOBAL][C__CATG__MEMORY]['category_entities'];
                            }
                        }
                    }
                }

                if (defined('C__CATG__STORAGE') && defined('C__CATG__STORAGE_DEVICE')) {
                    // Constant C__CATG__STORAGE is the root category for devices. For the import use C__CMDB__SUBCAT__STORAGE__DEVICE
                    if (isset($l_activated_categories[C__CATG__STORAGE])) {
                        // Reset category storage, data will be deleted before dataretrieval
                        if (isys_jdisc_dao_data::clear_data() === true) {
                            $this->m_dao->clear_category('isys_catg_stor_list', $l_object_id, 'C__CATG__STORAGE', false);
                        }

                        $this->m_log->debug('Read and prepare storage (HDD) data');
                        // Get the local storage devices (HDD, ...).
                        $l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' .
                        C__CATG__STORAGE_DEVICE] = isys_jdisc_dao_devices::instance($this->m_db)
                            ->get_physicaldisk_by_device($p_row['id']);
                    }

                    if ($l_use_default_template && $newObject) {
                        if (isset($this->m_obj_type_tpls[$p_row['idoit_obj_type']][C__CMDB__CATEGORY__TYPE_GLOBAL][C__CATG__STORAGE_DEVICE])) {
                            $l_black_list[C__CMDB__CATEGORY__TYPE_GLOBAL][C__CATG__STORAGE_DEVICE] = true;
                            if (isset($l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' . C__CATG__STORAGE_DEVICE]['category_entities'])) {
                                $l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' .
                                C__CATG__STORAGE_DEVICE]['category_entities'] = array_merge(
                                    $this->m_obj_type_tpls[$p_row['idoit_obj_type']][C__CMDB__CATEGORY__TYPE_GLOBAL][C__CATG__STORAGE_DEVICE]['category_entities'],
                                    $l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' . C__CATG__STORAGE_DEVICE]['category_entities']
                                );
                            } else {
                                $l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' .
                                C__CATG__STORAGE_DEVICE]['category_entities'] = $this->m_obj_type_tpls[$p_row['idoit_obj_type']][C__CMDB__CATEGORY__TYPE_GLOBAL][C__CATG__STORAGE_DEVICE]['category_entities'];
                            }
                        }
                    }
                }

                if (defined('C__CATG__DRIVE')) {
                    if (isset($l_activated_categories[C__CATG__DRIVE])) {
                        // Reset category drive, data will be deleted before dataretrieval
                        if (isys_jdisc_dao_data::clear_data() === true) {
                            $this->m_dao->clear_category('isys_catg_drive_list', $l_object_id, 'C__CATG__DRIVE', false);
                        }

                        $this->m_log->debug('Read and prepare drives (HDD, CD, DVD, ...) data');
                        // Get the drives (HDD, CD, DVD, ...).
                        $l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' .
                        C__CATG__DRIVE] = isys_jdisc_dao_devices::instance($this->m_db)
                            ->get_logicaldisk_by_device($p_row['id']);
                    }

                    if ($l_use_default_template && $newObject) {
                        if (isset($this->m_obj_type_tpls[$p_row['idoit_obj_type']][C__CMDB__CATEGORY__TYPE_GLOBAL][C__CATG__DRIVE])) {
                            $l_black_list[C__CMDB__CATEGORY__TYPE_GLOBAL][C__CATG__DRIVE] = true;
                            if (isset($l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' . C__CATG__DRIVE]['category_entities'])) {
                                $l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' .
                                C__CATG__DRIVE]['category_entities'] = array_merge(
                                    $this->m_obj_type_tpls[$p_row['idoit_obj_type']][C__CMDB__CATEGORY__TYPE_GLOBAL][C__CATG__DRIVE]['category_entities'],
                                    $l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' . C__CATG__DRIVE]['category_entities']
                                );
                            } else {
                                $l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' .
                                C__CATG__DRIVE]['category_entities'] = $this->m_obj_type_tpls[$p_row['idoit_obj_type']][C__CMDB__CATEGORY__TYPE_GLOBAL][C__CATG__DRIVE]['category_entities'];
                            }
                        }
                    }
                }

                isys_jdisc_dao_network::instance($this->m_db)
                    ->reset_filtered_ports_logical_ports($p_row['type'], $p_row['type_name'], $p_row['osid'], $p_row['osversion'], $p_row['idoit_obj_type'], $p_row['name']);

                if (defined('C__CATG__NETWORK_PORT') && defined('C__CATG__NETWORK_LOG_PORT')) {
                    if (isset($l_activated_categories[C__CATG__NETWORK_PORT])) {
                        if ($this->importConnectionEndpoint) {
                            $this->m_dao->clear_category(
                                'isys_catg_connection_endpoint_list',
                                $l_object_id,
                                'C__CATG__CONNECTION_ENDPOINT',
                                true
                            );
                        } else {
                            // Reset category network port, data will be deleted before dataretrieval
                            if (isys_jdisc_dao_data::clear_data() === true) {
                                $this->m_dao->clear_category(
                                    'isys_catg_port_list',
                                    $l_object_id,
                                    'C__CATG__NETWORK_PORT',
                                    true
                                );
                            }
                        }

                        // Reset category logical port, data will be deleted before dataretrieval
                        if (isys_jdisc_dao_data::clear_data() === true) {
                            $this->m_dao->clear_category('isys_catg_log_port_list', $l_object_id, 'C__CATG__NETWORK_LOG_PORT', false);
                        }

                        $this->m_log->debug('Read and prepare network port data');
                        isys_jdisc_dao_network::instance($this->m_db)
                            ->reset_filtered_ports_logical_ports(
                                $p_row['type'],
                                $p_row['type_name'],
                                $p_row['osid'],
                                $p_row['osversion'],
                                $p_row['idoit_obj_type'],
                                $p_row['name']
                            );

                        if ($this->importConnectionEndpoint) {
                            $l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' . C__CATG__CONNECTION_ENDPOINT] = isys_jdisc_dao_category_connection_endpoint::instance($this->m_db)
                                ->getDataForImport($p_row['id'], false, $p_jdisc_to_idoit, $p_object_ids, $l_object_id);
                        } else {
                            // Get Ports
                            $l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' . C__CATG__NETWORK_PORT] = isys_jdisc_dao_network::instance($this->m_db)
                                ->setMacFilter($this->macFilterType, $this->macFilterList)
                                ->get_ports_by_device($p_row['id']);
                        }

                        // Get Logical Ports
                        $l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' .
                        C__CATG__NETWORK_LOG_PORT] = isys_jdisc_dao_network::instance($this->m_db)
                            ->setMacFilter($this->macFilterType, $this->macFilterList)
                            ->get_logical_ports_by_device($p_row['id']);
                    }

                    if ($l_use_default_template && $newObject) {
                        if (isset($this->m_obj_type_tpls[$p_row['idoit_obj_type']][C__CMDB__CATEGORY__TYPE_GLOBAL][C__CATG__NETWORK_PORT])) {
                            $l_black_list[C__CMDB__CATEGORY__TYPE_GLOBAL][C__CATG__NETWORK_PORT] = true;
                            if (isset($l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' . C__CATG__NETWORK_PORT]['category_entities'])) {
                                $l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' .
                                C__CATG__NETWORK_PORT]['category_entities'] = array_merge(
                                    $this->m_obj_type_tpls[$p_row['idoit_obj_type']][C__CMDB__CATEGORY__TYPE_GLOBAL][C__CATG__NETWORK_PORT]['category_entities'],
                                    $l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' . C__CATG__NETWORK_PORT]['category_entities']
                                );
                            } else {
                                $l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' .
                                C__CATG__NETWORK_PORT]['category_entities'] = $this->m_obj_type_tpls[$p_row['idoit_obj_type']][C__CMDB__CATEGORY__TYPE_GLOBAL][C__CATG__NETWORK_PORT]['category_entities'];
                            }
                        }

                        if (isset($this->m_obj_type_tpls[$p_row['idoit_obj_type']][C__CMDB__CATEGORY__TYPE_GLOBAL][C__CATG__NETWORK_LOG_PORT])) {
                            $l_black_list[C__CMDB__CATEGORY__TYPE_GLOBAL][C__CATG__NETWORK_LOG_PORT] = true;
                            if (isset($l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' . C__CATG__NETWORK_LOG_PORT]['category_entities'])) {
                                $l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' .
                                C__CATG__NETWORK_LOG_PORT]['category_entities'] = array_merge(
                                    $this->m_obj_type_tpls[$p_row['idoit_obj_type']][C__CMDB__CATEGORY__TYPE_GLOBAL][C__CATG__NETWORK_LOG_PORT]['category_entities'],
                                    $l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' . C__CATG__NETWORK_LOG_PORT]['category_entities']
                                );
                            } else {
                                $l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' .
                                C__CATG__NETWORK_LOG_PORT]['category_entities'] = $this->m_obj_type_tpls[$p_row['idoit_obj_type']][C__CMDB__CATEGORY__TYPE_GLOBAL][C__CATG__NETWORK_LOG_PORT]['category_entities'];
                            }
                        }
                    }
                }

                if (defined('C__CATG__CONTROLLER_FC_PORT')) {
                    if (isset($l_activated_categories[C__CATG__CONTROLLER_FC_PORT])) {
                        if (isys_jdisc_dao_data::clear_data() === true) {
                            $this->m_dao->clear_category('isys_catg_fc_port_list', $l_object_id, 'C__CATG__CONTROLLER_FC_PORT', true);
                        }

                        $this->m_log->debug('Read and prepare fc port data');

                        // Get FC Ports
                        $l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' .
                        C__CATG__CONTROLLER_FC_PORT] = isys_jdisc_dao_network::instance($this->m_db)
                            ->setMacFilter($this->macFilterType, $this->macFilterList)
                            ->get_fc_ports_by_device($p_row['id']);
                    }

                    if ($l_use_default_template && $newObject) {
                        if (isset($this->m_obj_type_tpls[$p_row['idoit_obj_type']][C__CMDB__CATEGORY__TYPE_GLOBAL][C__CATG__CONTROLLER_FC_PORT])) {
                            $l_black_list[C__CMDB__CATEGORY__TYPE_GLOBAL][C__CATG__CONTROLLER_FC_PORT] = true;
                            if (isset($l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' . C__CATG__CONTROLLER_FC_PORT]['category_entities'])) {
                                $l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' .
                                C__CATG__CONTROLLER_FC_PORT]['category_entities'] = array_merge(
                                    $this->m_obj_type_tpls[$p_row['idoit_obj_type']][C__CMDB__CATEGORY__TYPE_GLOBAL][C__CATG__CONTROLLER_FC_PORT]['category_entities'],
                                    $l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' . C__CATG__CONTROLLER_FC_PORT]['category_entities']
                                );
                            } else {
                                $l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' .
                                C__CATG__CONTROLLER_FC_PORT]['category_entities'] = $this->m_obj_type_tpls[$p_row['idoit_obj_type']][C__CMDB__CATEGORY__TYPE_GLOBAL][C__CATG__CONTROLLER_FC_PORT]['category_entities'];
                            }
                        }
                    }
                }

                if (defined('C__CATG__NETWORK_INTERFACE')) {
                    if (isset($l_activated_categories[C__CATG__NETWORK_INTERFACE])) {
                        // Reset category interface, data will be deleted before dataretrieval
                        if (isys_jdisc_dao_data::clear_data() === true) {
                            $this->m_dao->clear_category('isys_catg_netp_list', $l_object_id, 'C__CATG__NETWORK_INTERFACE', false);
                        }

                        $this->m_log->debug('Read and prepare network interface data');
                        $l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' .
                        C__CATG__NETWORK_INTERFACE] = isys_jdisc_dao_network::instance($this->m_db)
                            ->get_interfaces_by_device($p_row['id']);
                    }

                    if ($l_use_default_template && $newObject) {
                        if (isset($this->m_obj_type_tpls[$p_row['idoit_obj_type']][C__CMDB__CATEGORY__TYPE_GLOBAL][C__CATG__NETWORK_INTERFACE])) {
                            $l_black_list[C__CMDB__CATEGORY__TYPE_GLOBAL][C__CATG__NETWORK_INTERFACE] = true;
                            if (isset($l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' . C__CATG__NETWORK_INTERFACE]['category_entities'])) {
                                $l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' .
                                C__CATG__NETWORK_INTERFACE]['category_entities'] = array_merge(
                                    $this->m_obj_type_tpls[$p_row['idoit_obj_type']][C__CMDB__CATEGORY__TYPE_GLOBAL][C__CATG__NETWORK_INTERFACE]['category_entities'],
                                    $l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' . C__CATG__NETWORK_INTERFACE]['category_entities']
                                );
                            } else {
                                $l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' .
                                C__CATG__NETWORK_INTERFACE]['category_entities'] = $this->m_obj_type_tpls[$p_row['idoit_obj_type']][C__CMDB__CATEGORY__TYPE_GLOBAL][C__CATG__NETWORK_INTERFACE]['category_entities'];
                            }
                        }
                    }
                }

                if (defined('C__CATG__OPERATING_SYSTEM')) {
                    if (isset($l_activated_categories[C__CATG__OPERATING_SYSTEM])) {
                        // We need this data more than once, so we create these variables.
                        $this->m_log->debug('Prepare the operating system data');

                        $this->m_log->debug('Read and prepare operating system data');
                        // Get the assigned operating systems - This implementation differs from the others!!
                        $l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' .
                        C__CATG__OPERATING_SYSTEM] = isys_jdisc_dao_software::instance($this->m_db)
                            ->get_os_by_device($p_row['id'], false, $this->m_all_software, $p_object_ids, $l_connections, $this->m_software_licences);
                    }

                    if ($l_use_default_template && $newObject) {
                        if (isset($this->m_obj_type_tpls[$p_row['idoit_obj_type']][C__CMDB__CATEGORY__TYPE_GLOBAL][C__CATG__OPERATING_SYSTEM])) {
                            $l_black_list[C__CMDB__CATEGORY__TYPE_GLOBAL][C__CATG__OPERATING_SYSTEM] = true;
                            if (isset($l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' . C__CATG__OPERATING_SYSTEM]['category_entities'])) {
                                $l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' .
                                C__CATG__OPERATING_SYSTEM]['category_entities'] = array_replace_recursive(
                                    $this->m_obj_type_tpls[$p_row['idoit_obj_type']][C__CMDB__CATEGORY__TYPE_GLOBAL][C__CATG__OPERATING_SYSTEM]['category_entities'],
                                    $l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' . C__CATG__OPERATING_SYSTEM]['category_entities']
                                );
                            } else {
                                $l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' .
                                C__CATG__OPERATING_SYSTEM]['category_entities'] = $this->m_obj_type_tpls[$p_row['idoit_obj_type']][C__CMDB__CATEGORY__TYPE_GLOBAL][C__CATG__OPERATING_SYSTEM]['category_entities'];
                            }
                        }
                    }
                }

                $this->m_log->debug('Prepare the network data');

                if (defined('C__CATG__IP')) {
                    if (isset($l_activated_categories[C__CATG__IP])) {
                        $l_profile = $this->get_cached_profile();
                        $l_profileNetworkAddressesFilter = Unserialize::toArray($l_profile['network_adresses'] ?? Unserialize::EMPTY_ARRAY);
                        $importDhcpType = (int)$l_profile['import_type_dhcp_addresses'];

                        // Reset category hostaddress, data will be deleted before dataretrieval
                        if ((isys_jdisc_dao_data::clear_data() === true) && ($importDhcpType !== 1)) {
                            $this->m_dao->clear_category('isys_catg_ip_list', $l_object_id, 'C__CATG__IP', true);
                        }

                        if ($importDhcpType === 1) {
                            $this->m_dao->clear_ip_addresses($l_object_id, true);
                        }

                        $this->m_log->debug('Read and prepare network (hostaddress) data');

                        // Get the hostaddresses - This implementation differs from the others!!
                        $l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' . C__CATG__IP] = isys_jdisc_dao_network::instance($this->m_db)
                            ->get_layer3_by_device(
                                $p_row['id'],
                                false, // raw
                                $this->m_all_networks,
                                $l_connections,
                                $l_profileNetworkAddressesFilter,
                                $importDhcpType,
                                $this->m_l3_rules,
                                $this->m_l3_filter
                            );
                    }

                    if ($l_use_default_template && $newObject) {
                        if (isset($this->m_obj_type_tpls[$p_row['idoit_obj_type']][C__CMDB__CATEGORY__TYPE_GLOBAL][C__CATG__IP])) {
                            $l_black_list[C__CMDB__CATEGORY__TYPE_GLOBAL][C__CATG__IP] = true;
                            if (isset($l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' . C__CATG__IP]['category_entities'])) {
                                $l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' .
                                C__CATG__IP]['category_entities'] = array_merge(
                                    $this->m_obj_type_tpls[$p_row['idoit_obj_type']][C__CMDB__CATEGORY__TYPE_GLOBAL][C__CATG__IP]['category_entities'],
                                    $l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' . C__CATG__IP]['category_entities']
                                );
                            } else {
                                $l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' .
                                C__CATG__IP]['category_entities'] = $this->m_obj_type_tpls[$p_row['idoit_obj_type']][C__CMDB__CATEGORY__TYPE_GLOBAL][C__CATG__IP]['category_entities'];
                            }
                        }
                    }
                }

                if (defined('C__CATG__APPLICATION')) {
                    if (isset($l_activated_categories[C__CATG__APPLICATION])) {
                        // Reset category application, data will be deleted before dataretrieval
                        if (isys_jdisc_dao_data::clear_data() === true) {
                            $this->m_dao->clear_category('isys_catg_application_list', $l_object_id, 'C__CATG__APPLICATION', true);
                        }

                        // We need this data more than once, so we create these variables.
                        $this->m_log->debug('Prepare the software data');
                        isys_jdisc_dao_software::instance($this->m_db)
                            ->set_current_object_id($l_object_id);

                        $this->m_log->debug('Read and prepare application data');
                        // Get the assigned applications - This implementation differs from the others!!
                        $l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' .
                        C__CATG__APPLICATION] = isys_jdisc_dao_software::instance($this->m_db)
                            ->get_software_by_device($p_row['id'], false, $this->m_all_software, $p_object_ids, $l_connections, $this->m_software_licences, $this->importSoftwareServices);
                    }

                    if ($l_use_default_template && $newObject) {
                        if (isset($this->m_obj_type_tpls[$p_row['idoit_obj_type']][C__CMDB__CATEGORY__TYPE_GLOBAL][C__CATG__APPLICATION])) {
                            $l_black_list[C__CMDB__CATEGORY__TYPE_GLOBAL][C__CATG__APPLICATION] = true;
                            if (isset($l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' . C__CATG__APPLICATION]['category_entities'])) {
                                $l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' .
                                C__CATG__APPLICATION]['category_entities'] = array_merge(
                                    $this->m_obj_type_tpls[$p_row['idoit_obj_type']][C__CMDB__CATEGORY__TYPE_GLOBAL][C__CATG__APPLICATION]['category_entities'],
                                    $l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' . C__CATG__APPLICATION]['category_entities']
                                );
                            } else {
                                $l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' .
                                C__CATG__APPLICATION]['category_entities'] = $this->m_obj_type_tpls[$p_row['idoit_obj_type']][C__CMDB__CATEGORY__TYPE_GLOBAL][C__CATG__APPLICATION]['category_entities'];
                            }
                        }
                    }

                    if (isys_jdisc_dao_software::instance($this->m_db)->isSimpleDatabaseModel()) {
                        isys_jdisc_dao_software_database::instance($this->m_db)
                            ->reset()
                            ->set_current_object_id($l_object_id);

                        if (isys_jdisc_dao_data::clear_data() === true) {
                            $this->m_dao
                                ->clear_category('isys_catg_database_list', $l_object_id, 'C__CATG__DATABASE', false);
                            $this->m_dao
                                ->clear_category('isys_catg_database_sa_list', $l_object_id, 'C__CATG__DATABASE_SA', false);
                            $this->m_dao
                                ->clear_category('isys_catg_database_table_list', $l_object_id, 'C__CATG__DATABASE_TABLE', false);
                        }

                        $this->m_log->debug('Read and prepare database data');
                        $l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' . C__CATG__DATABASE] =
                            isys_jdisc_dao_software_database::instance($this->m_db)
                                ->getDbmsByDevice($p_row['id'], false, $p_object_ids, $l_connections);

                        if ($l_use_default_template && $newObject) {
                            if (isset($this->m_obj_type_tpls[$p_row['idoit_obj_type']][C__CMDB__CATEGORY__TYPE_GLOBAL][C__CATG__DATABASE])) {
                                $l_black_list[C__CMDB__CATEGORY__TYPE_GLOBAL][C__CATG__DATABASE] = true;
                                if (isset($l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' . C__CATG__DATABASE]['category_entities'])) {
                                    $l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' .
                                    C__CATG__DATABASE]['category_entities'] = array_merge(
                                        $this->m_obj_type_tpls[$p_row['idoit_obj_type']][C__CMDB__CATEGORY__TYPE_GLOBAL][C__CATG__DATABASE]['category_entities'],
                                        $l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' . C__CATG__DATABASE]['category_entities']
                                    );
                                } else {
                                    $l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' .
                                    C__CATG__DATABASE]['category_entities'] = $this->m_obj_type_tpls[$p_row['idoit_obj_type']][C__CMDB__CATEGORY__TYPE_GLOBAL][C__CATG__DATABASE]['category_entities'];
                                }
                            }
                        }


                        $this->m_log->debug('Read and prepare database schemas data');
                        $l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' . C__CATG__DATABASE_SA] =
                            isys_jdisc_dao_software_database::instance($this->m_db)
                                ->getDatabasesByDevice($p_row['id'], false, $p_object_ids, $l_connections);

                        if ($l_use_default_template && $newObject) {
                            if (isset($this->m_obj_type_tpls[$p_row['idoit_obj_type']][C__CMDB__CATEGORY__TYPE_GLOBAL][C__CATG__DATABASE_SA])) {
                                $l_black_list[C__CMDB__CATEGORY__TYPE_GLOBAL][C__CATG__DATABASE_SA] = true;
                                if (isset($l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' . C__CATG__DATABASE_SA]['category_entities'])) {
                                    $l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' .
                                    C__CATG__DATABASE_SA]['category_entities'] = array_merge(
                                        $this->m_obj_type_tpls[$p_row['idoit_obj_type']][C__CMDB__CATEGORY__TYPE_GLOBAL][C__CATG__DATABASE_SA]['category_entities'],
                                        $l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' . C__CATG__DATABASE_SA]['category_entities']
                                    );
                                } else {
                                    $l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' .
                                    C__CATG__DATABASE_SA]['category_entities'] = $this->m_obj_type_tpls[$p_row['idoit_obj_type']][C__CMDB__CATEGORY__TYPE_GLOBAL][C__CATG__DATABASE_SA]['category_entities'];
                                }
                            }
                        }

                        $this->m_log->debug('Read and prepare database table data');
                        $l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' . C__CATG__DATABASE_TABLE] =
                            isys_jdisc_dao_software_database::instance($this->m_db)
                                ->getDatabaseTablesByDevice($p_row['id'], false, $p_object_ids, $l_connections);

                        if ($l_use_default_template && $newObject) {
                            if (isset($this->m_obj_type_tpls[$p_row['idoit_obj_type']][C__CMDB__CATEGORY__TYPE_GLOBAL][C__CATG__DATABASE_TABLE])) {
                                $l_black_list[C__CMDB__CATEGORY__TYPE_GLOBAL][C__CATG__DATABASE_TABLE] = true;
                                if (isset($l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' . C__CATG__DATABASE_TABLE]['category_entities'])) {
                                    $l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' .
                                    C__CATG__DATABASE_TABLE]['category_entities'] = array_merge(
                                        $this->m_obj_type_tpls[$p_row['idoit_obj_type']][C__CMDB__CATEGORY__TYPE_GLOBAL][C__CATG__DATABASE_TABLE]['category_entities'],
                                        $l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' . C__CATG__DATABASE_TABLE]['category_entities']
                                    );
                                } else {
                                    $l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' .
                                    C__CATG__DATABASE_TABLE]['category_entities'] = $this->m_obj_type_tpls[$p_row['idoit_obj_type']][C__CMDB__CATEGORY__TYPE_GLOBAL][C__CATG__DATABASE_TABLE]['category_entities'];
                                }
                            }
                        }
                    }
                }

                if (defined('C__CATG__NET_LISTENER')) {
                    if (isset($l_activated_categories[C__CATG__NET_LISTENER]) && isys_jdisc_dao_software::instance($this->m_db)
                            ->check_table('applicationinstanceport')) {
                        // Reset category application, data will be deleted before dataretrieval
                        if (isys_jdisc_dao_data::clear_data() === true) {
                            $this->m_dao->clear_category('isys_catg_net_listener_list', $l_object_id, 'C__CATG__NET_LISTENER', false);
                        }

                        $this->m_log->debug('Prepare net listener data');
                        $this->m_log->debug('Read and prepare net listener data');

                        isys_jdisc_dao_software::instance($this->m_db)
                            ->handle_net_listener($p_row['id'], $l_object_id);
                    }

                    if ($l_use_default_template && $newObject) {
                        if (defined('C__CATG__APPLICATION') && isset($this->m_obj_type_tpls[$p_row['idoit_obj_type']][C__CMDB__CATEGORY__TYPE_GLOBAL][C__CATG__APPLICATION])) {
                            $l_black_list[C__CMDB__CATEGORY__TYPE_GLOBAL][C__CATG__NET_LISTENER] = true;
                            if (isset($l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' . C__CATG__NET_LISTENER]['category_entities'])) {
                                $l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' .
                                C__CATG__NET_LISTENER]['category_entities'] = array_merge(
                                    $this->m_obj_type_tpls[$p_row['idoit_obj_type']][C__CMDB__CATEGORY__TYPE_GLOBAL][C__CATG__NET_LISTENER]['category_entities'],
                                    $l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' . C__CATG__NET_LISTENER]['category_entities']
                                );
                            } else {
                                $l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' .
                                C__CATG__NET_LISTENER]['category_entities'] = $this->m_obj_type_tpls[$p_row['idoit_obj_type']][C__CMDB__CATEGORY__TYPE_GLOBAL][C__CATG__NET_LISTENER]['category_entities'];
                            }
                        }
                    }
                }

                if (defined('C__CATG__MODEL')) {
                    if (isset($l_activated_categories[C__CATG__MODEL])) {
                        $this->m_log->debug('Read and prepare model data');
                        // Get contents for model category.
                        $l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' .
                        C__CATG__MODEL] = isys_jdisc_dao_devices::instance($this->m_db)
                            ->prepare_model($p_row);
                    }

                    if ($l_use_default_template && $newObject) {
                        if (isset($this->m_obj_type_tpls[$p_row['idoit_obj_type']][C__CMDB__CATEGORY__TYPE_GLOBAL][C__CATG__MODEL])) {
                            $l_black_list[C__CMDB__CATEGORY__TYPE_GLOBAL][C__CATG__MODEL] = true;
                            if (isset($l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' . C__CATG__MODEL]['category_entities'])) {
                                $l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' .
                                C__CATG__MODEL]['category_entities'] = array_replace_recursive(
                                    $this->m_obj_type_tpls[$p_row['idoit_obj_type']][C__CMDB__CATEGORY__TYPE_GLOBAL][C__CATG__MODEL]['category_entities'],
                                    $l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' . C__CATG__MODEL]['category_entities']
                                );
                            } else {
                                $l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' .
                                C__CATG__MODEL]['category_entities'] = $this->m_obj_type_tpls[$p_row['idoit_obj_type']][C__CMDB__CATEGORY__TYPE_GLOBAL][C__CATG__MODEL]['category_entities'];
                            }
                        }
                    }
                }

                if (defined('C__CATG__LAST_LOGIN_USER')) {
                    // Last logged in user
                    if (isset($l_activated_categories[C__CATG__LAST_LOGIN_USER])) {
                        $this->m_log->debug('Read and prepare last logged in user data');
                        // Get contents for model category.
                        $l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' .
                        C__CATG__LAST_LOGIN_USER] = isys_jdisc_dao_devices::instance($this->m_db)
                            ->prepare_last_login_user($p_row);
                    }

                    if ($l_use_default_template && $newObject) {
                        if (isset($this->m_obj_type_tpls[$p_row['idoit_obj_type']][C__CMDB__CATEGORY__TYPE_GLOBAL][C__CATG__LAST_LOGIN_USER])) {
                            $l_black_list[C__CMDB__CATEGORY__TYPE_GLOBAL][C__CATG__LAST_LOGIN_USER] = true;
                            if (isset($l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' . C__CATG__LAST_LOGIN_USER]['category_entities'])) {
                                $l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' .
                                C__CATG__LAST_LOGIN_USER]['category_entities'] = array_replace_recursive(
                                    $this->m_obj_type_tpls[$p_row['idoit_obj_type']][C__CMDB__CATEGORY__TYPE_GLOBAL][C__CATG__LAST_LOGIN_USER]['category_entities'],
                                    $l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' . C__CATG__LAST_LOGIN_USER]['category_entities']
                                );
                            } else {
                                $l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' .
                                C__CATG__LAST_LOGIN_USER]['category_entities'] = $this->m_obj_type_tpls[$p_row['idoit_obj_type']][C__CMDB__CATEGORY__TYPE_GLOBAL][C__CATG__LAST_LOGIN_USER]['category_entities'];
                            }
                        }
                    }
                }

                if (defined('C__CATG__VIRTUAL_MACHINE')) {
                    // Check if object is a virtual machine or not
                    if (isset($l_activated_categories[C__CATG__VIRTUAL_MACHINE])) {
                        $this->m_log->debug('Read and prepare virtual machine (VM) data');

                        if (isset($this->m_vm_con_arr[$p_row['id']])) {
                            if (!isset($this->m_cluster_cache[$this->m_vm_con_arr[$p_row['id']]])) {
                                $l_cluster = isys_jdisc_dao_cluster::instance($this->m_db)
                                    ->get_cluster_by_device($this->m_vm_con_arr[$p_row['id']], false, $this->m_all_clusters);
                                if (!empty($l_cluster)) {
                                    $this->m_cluster_cache[$this->m_vm_con_arr[$p_row['id']]] = $l_cluster;
                                }
                            } else {
                                $l_cluster = $this->m_cluster_cache[$this->m_vm_con_arr[$p_row['id']]];
                            }
                            if (!isset($p_jdisc_to_idoit[$this->m_vm_con_arr[$p_row['id']]])) {
                                isys_jdisc_dao_devices::instance($this->m_db)
                                    ->set_vm_host_by_device($this->m_vm_con_arr[$p_row['id']], $p_jdisc_to_idoit, $p_object_ids);
                            }
                            $l_category_vm = isys_jdisc_dao_devices::instance($this->m_db)
                                ->prepare_virtual_machine($p_row, $this->m_vm_con_arr[$p_row['id']], $p_jdisc_to_idoit, $l_cluster);
                            if ($l_category_vm) {
                                $l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' . C__CATG__VIRTUAL_MACHINE] = $l_category_vm;
                            } else {
                                $this->m_log->debug('Virtual host with device id "' . $this->m_vm_con_arr[$p_row['id']] . '" does not exist.');
                                isys_ajax_handler_jdisc::$m_additional_stats .= 'INFO: VM host for device ' . $p_row['name'] .
                                    " does not exist and is not imported with your profile configuration. Please specify an object-type mapping for device type " .
                                    $p_row['type'] . " in order to import this host.\n";
                            }
                        } else {
                            // If no virtual host is defined than remove the entry
                            $l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' .
                            C__CATG__VIRTUAL_MACHINE] = isys_jdisc_dao_devices::instance($this->m_db)
                                ->prepare_virtual_machine($p_row, null, $p_jdisc_to_idoit);
                            $this->m_log->debug('No Virtual host with device id "' . $this->m_vm_con_arr[$p_row['id']] . '" found.');
                        }
                    }

                    if ($l_use_default_template && $newObject) {
                        if (isset($this->m_obj_type_tpls[$p_row['idoit_obj_type']][C__CMDB__CATEGORY__TYPE_GLOBAL][C__CATG__VIRTUAL_MACHINE])) {
                            $l_black_list[C__CMDB__CATEGORY__TYPE_GLOBAL][C__CATG__VIRTUAL_MACHINE] = true;
                            if (isset($l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' . C__CATG__VIRTUAL_MACHINE]['category_entities'])) {
                                $l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' .
                                C__CATG__VIRTUAL_MACHINE]['category_entities'] = array_replace_recursive(
                                    $this->m_obj_type_tpls[$p_row['idoit_obj_type']][C__CMDB__CATEGORY__TYPE_GLOBAL][C__CATG__VIRTUAL_MACHINE]['category_entities'],
                                    $l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' . C__CATG__VIRTUAL_MACHINE]['category_entities']
                                );
                            } else {
                                $l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' .
                                C__CATG__VIRTUAL_MACHINE]['category_entities'] = $this->m_obj_type_tpls[$p_row['idoit_obj_type']][C__CMDB__CATEGORY__TYPE_GLOBAL][C__CATG__VIRTUAL_MACHINE]['category_entities'];
                            }
                        }
                    }
                }

                if (defined('C__CATG__CLUSTER_MEMBERSHIPS')) {
                    $this->m_log->debug('Prepare the cluster data');
                    $l_cluster = isys_jdisc_dao_cluster::instance($this->m_db)
                        ->get_cluster_by_device($p_row['id'], false, $this->m_all_clusters);

                    if (isset($l_activated_categories[C__CATG__CLUSTER_MEMBERSHIPS])) {
                        // Reset category cluster memberships, data will be deleted before dataretrieval
                        if (isys_jdisc_dao_data::clear_data() === true) {
                            isys_jdisc_dao_cluster::instance($this->m_db)
                                ->clear_cluster_memberships($l_object_id);
                        }

                        $this->m_log->debug('Prepare cluster assignments');

                        $l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' . C__CATG__CLUSTER_MEMBERSHIPS] = $l_cluster;
                    }

                    if ($l_use_default_template && $newObject) {
                        if (isset($this->m_obj_type_tpls[$p_row['idoit_obj_type']][C__CMDB__CATEGORY__TYPE_GLOBAL][C__CATG__CLUSTER_MEMBERSHIPS])) {
                            $l_black_list[C__CMDB__CATEGORY__TYPE_GLOBAL][C__CATG__CLUSTER_MEMBERSHIPS] = true;
                            if (isset($l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' . C__CATG__CLUSTER_MEMBERSHIPS]['category_entities'])) {
                                $l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' .
                                C__CATG__CLUSTER_MEMBERSHIPS]['category_entities'] = array_merge(
                                    $this->m_obj_type_tpls[$p_row['idoit_obj_type']][C__CMDB__CATEGORY__TYPE_GLOBAL][C__CATG__CLUSTER_MEMBERSHIPS]['category_entities'],
                                    $l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' . C__CATG__CLUSTER_MEMBERSHIPS]['category_entities']
                                );
                            } else {
                                $l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' .
                                C__CATG__CLUSTER_MEMBERSHIPS]['category_entities'] = $this->m_obj_type_tpls[$p_row['idoit_obj_type']][C__CMDB__CATEGORY__TYPE_GLOBAL][C__CATG__CLUSTER_MEMBERSHIPS]['category_entities'];
                            }
                        }
                    }
                }

                if (defined('C__CATG__GUEST_SYSTEMS')) {
                    // Handle category guest system only if no cluster is assigned
                    if (isset($l_activated_categories[C__CATG__GUEST_SYSTEMS]) && count($l_cluster) === 0) {
                        $this->m_log->debug('Read and prepare virtual computers assignment (guest systems) for the host system.');
                        // Get contents for model category.
                        isys_jdisc_dao_devices::instance($this->m_db)
                            ->handle_guest_systems($p_row['id'], $l_object_id, $l_object[$l_object_id]['type'], $p_jdisc_to_idoit, $this->m_mode, false);
                    }

                    if ($l_use_default_template && $newObject && count($l_cluster) === 0) {
                        if (isset($this->m_obj_type_tpls[$p_row['idoit_obj_type']][C__CMDB__CATEGORY__TYPE_GLOBAL][C__CATG__GUEST_SYSTEMS])) {
                            $l_black_list[C__CMDB__CATEGORY__TYPE_GLOBAL][C__CATG__GUEST_SYSTEMS] = true;
                            $l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' .
                            C__CATG__GUEST_SYSTEMS]['category_entities'] = $this->m_obj_type_tpls[$p_row['idoit_obj_type']][C__CMDB__CATEGORY__TYPE_GLOBAL][C__CATG__GUEST_SYSTEMS]['category_entities'];
                        }
                    }
                }

                if (defined('C__CATG__RM_CONTROLLER')) {
                    if (isset($l_activated_categories[C__CATG__RM_CONTROLLER])) {
                        // Reset category Remote Management Controller, data will be deleted before dataretrieval
                        if (isys_jdisc_dao_data::clear_data() === true) {
                            $this->m_dao->clear_category('isys_catg_rm_controller_list', $l_object_id, 'C__CATG__RM_CONTROLLER', true);
                        }

                        $this->m_log->debug('Read and prepare remote management controller data');

                        // Get contents for model category.
                        if ($managementDeviceId = isys_jdisc_dao_devices::instance($this->m_db)
                            ->getManagementDeviceConnection($p_row['id'])
                        ) {
                            $l_rm_controller = isys_jdisc_dao_devices::instance($this->m_db)
                                ->prepare_rm_controller($p_row['id'], $p_jdisc_to_idoit, $managementDeviceId);
                            if (!empty($l_rm_controller)) {
                                $l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' . C__CATG__RM_CONTROLLER] = $l_rm_controller;
                            } else {
                                $this->m_log->debug('Management Device with device id "' . $this->m_management_device_con_arr[$p_row['id']] . '" does not exist.');
                                isys_ajax_handler_jdisc::$m_additional_stats .= 'INFO: Management Device for device ' . $p_row['name'] .
                                    " does not exist and is not imported with your profile configuration. Please specify an object-type mapping for Management Devices in order to import the Management Device connection.\n";
                            }
                        } else {
                            $this->m_log->debug('No Management Device found for device "' . $p_row['name'] . '" (' . $this->m_vm_con_arr[$p_row['id']] . ') .');
                        }
                    }

                    if ($l_use_default_template && $newObject) {
                        if (isset($this->m_obj_type_tpls[$p_row['idoit_obj_type']][C__CMDB__CATEGORY__TYPE_GLOBAL][C__CATG__RM_CONTROLLER])) {
                            $l_black_list[C__CMDB__CATEGORY__TYPE_GLOBAL][C__CATG__RM_CONTROLLER] = true;
                            if (isset($l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' . C__CATG__RM_CONTROLLER]['category_entities'])) {
                                $l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' .
                                C__CATG__RM_CONTROLLER]['category_entities'] = array_replace_recursive(
                                    $this->m_obj_type_tpls[$p_row['idoit_obj_type']][C__CMDB__CATEGORY__TYPE_GLOBAL][C__CATG__RM_CONTROLLER]['category_entities'],
                                    $l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' . C__CATG__RM_CONTROLLER]['category_entities']
                                );
                            } else {
                                $l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' .
                                C__CATG__RM_CONTROLLER]['category_entities'] = $this->m_obj_type_tpls[$p_row['idoit_obj_type']][C__CMDB__CATEGORY__TYPE_GLOBAL][C__CATG__RM_CONTROLLER]['category_entities'];
                            }
                        }
                    }
                }

                if (defined('C__CATG__STACK_MEMBER')) {
                    if (isset($l_activated_categories[C__CATG__STACK_MEMBER])) {
                        // Reset category Remote Management Controller, data will be deleted before dataretrieval
                        if (isys_jdisc_dao_data::clear_data() === true) {
                            $this->m_dao->clear_category('isys_catg_stack_member_list', $l_object_id, 'C__CATG__STACK_MEMBER', true);
                        }

                        $this->m_log->debug('Read and prepare stack members in user data');
                        // Get contents for model category.
                        $l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' .
                        C__CATG__STACK_MEMBER] = isys_jdisc_dao_devices::instance($this->m_db)
                            ->prepare_stack_member($p_row['id'], $p_jdisc_to_idoit);
                    }

                    if ($l_use_default_template && $newObject) {
                        if (isset($this->m_obj_type_tpls[$p_row['idoit_obj_type']][C__CMDB__CATEGORY__TYPE_GLOBAL][C__CATG__STACK_MEMBER])) {
                            $l_black_list[C__CMDB__CATEGORY__TYPE_GLOBAL][C__CATG__STACK_MEMBER] = true;
                            if (isset($l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' . C__CATG__STACK_MEMBER]['category_entities'])) {
                                $l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' .
                                C__CATG__STACK_MEMBER]['category_entities'] = array_replace_recursive(
                                    $this->m_obj_type_tpls[$p_row['idoit_obj_type']][C__CMDB__CATEGORY__TYPE_GLOBAL][C__CATG__STACK_MEMBER]['category_entities'],
                                    $l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' . C__CATG__STACK_MEMBER]['category_entities']
                                );
                            } else {
                                $l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' .
                                C__CATG__STACK_MEMBER]['category_entities'] = $this->m_obj_type_tpls[$p_row['idoit_obj_type']][C__CMDB__CATEGORY__TYPE_GLOBAL][C__CATG__STACK_MEMBER]['category_entities'];
                            }
                        }
                    }
                }

                if (defined('C__CATG__SUPPORT_ENTITLEMENT')) {
                    if (isset($l_activated_categories[C__CATG__SUPPORT_ENTITLEMENT])) {
                        // Reset category support entitlements, data will be deleted before dataretrieval
                        if (isys_jdisc_dao_data::clear_data() === true) {
                            $this->m_dao->clear_category('isys_catg_support_entitlement_list', $l_object_id, 'C__CATG__SUPPORT_ENTITLEMENT', false);
                        }

                        $this->m_log->debug('Read and prepare support entitlement data');
                        $l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' .
                        C__CATG__SUPPORT_ENTITLEMENT] = isys_jdisc_dao_category_support_entitlement::instance($this->m_db)
                            ->getDataForImport($p_row['id'], false, $p_jdisc_to_idoit, $p_object_ids);
                    }

                    if ($l_use_default_template && $newObject) {
                        if (isset($this->m_obj_type_tpls[$p_row['idoit_obj_type']][C__CMDB__CATEGORY__TYPE_GLOBAL][C__CATG__SUPPORT_ENTITLEMENT])) {
                            $l_black_list[C__CMDB__CATEGORY__TYPE_GLOBAL][C__CATG__SUPPORT_ENTITLEMENT] = true;
                            if (isset($l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' . C__CATG__SUPPORT_ENTITLEMENT]['category_entities'])) {
                                $l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' .
                                C__CATG__SUPPORT_ENTITLEMENT]['category_entities'] = array_merge(
                                    $this->m_obj_type_tpls[$p_row['idoit_obj_type']][C__CMDB__CATEGORY__TYPE_GLOBAL][C__CATG__SUPPORT_ENTITLEMENT]['category_entities'],
                                    $l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' . C__CATG__SUPPORT_ENTITLEMENT]['category_entities']
                                );
                            } else {
                                $l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' .
                                C__CATG__SUPPORT_ENTITLEMENT]['category_entities'] = $this->m_obj_type_tpls[$p_row['idoit_obj_type']][C__CMDB__CATEGORY__TYPE_GLOBAL][C__CATG__SUPPORT_ENTITLEMENT]['category_entities'];
                            }
                        }
                    }
                }

                if (defined('C__CATG__SOUND')) {
                    if (isset($l_activated_categories[C__CATG__SOUND])) {
                        // Reset category support entitlements, data will be deleted before dataretrieval
                        if (isys_jdisc_dao_data::clear_data() === true) {
                            $this->m_dao->clear_category('isys_catg_sound_list', $l_object_id, 'C__CATG__SOUND', false);
                        }

                        $this->m_log->debug('Read and prepare soundcard data');
                        $l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' .
                        C__CATG__SOUND] = isys_jdisc_dao_category_sound::instance($this->m_db)
                            ->getDataForImport($p_row['id'], false, $p_jdisc_to_idoit, $p_object_ids);
                    }

                    if ($l_use_default_template && $newObject) {
                        if (isset($this->m_obj_type_tpls[$p_row['idoit_obj_type']][C__CMDB__CATEGORY__TYPE_GLOBAL][C__CATG__SOUND])) {
                            $l_black_list[C__CMDB__CATEGORY__TYPE_GLOBAL][C__CATG__SOUND] = true;
                            if (isset($l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' . C__CATG__SOUND]['category_entities'])) {
                                $l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' .
                                C__CATG__SOUND]['category_entities'] = array_merge(
                                    $this->m_obj_type_tpls[$p_row['idoit_obj_type']][C__CMDB__CATEGORY__TYPE_GLOBAL][C__CATG__SOUND]['category_entities'],
                                    $l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' . C__CATG__SOUND]['category_entities']
                                );
                            } else {
                                $l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' .
                                C__CATG__SOUND]['category_entities'] = $this->m_obj_type_tpls[$p_row['idoit_obj_type']][C__CMDB__CATEGORY__TYPE_GLOBAL][C__CATG__SOUND]['category_entities'];
                            }
                        }
                    }
                }

                if (defined('C__CATG__AP_CONTROLLER')) {
                    if (isset($l_activated_categories[C__CATG__AP_CONTROLLER])) {
                        // Reset category Access Point Controller, data will be deleted before data retrieval
                        if (isys_jdisc_dao_data::clear_data() === true) {
                            $this->m_dao->clear_category('isys_catg_ap_controller_list', $l_object_id, true);
                        }

                        $this->m_log->debug('Read and prepare access point controller data');

                        // Get contents for model category.
                        $apDeviceId = isys_jdisc_dao_devices::instance($this->m_db)->getAPConnection($p_row['id']);
                        if ($apDeviceId) {
                            $apController = isys_jdisc_dao_devices::instance($this->m_db)->prepareAPController($apDeviceId, $p_jdisc_to_idoit);
                            if (!empty($apController)) {
                                $l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' . C__CATG__AP_CONTROLLER] = $apController;
                            } else {
                                $this->m_log->debug("AP Device with device id {$apDeviceId} does not exist.");
                                isys_ajax_handler_jdisc::$m_additional_stats .= "INFO: AP for device {$p_row['name']} does not exist and is not imported with your profile configuration. Please specify an object-type mapping for AP Devices in order to import the AP Device connection.\n";
                            }
                        } else {
                            $this->m_log->debug("No Management Device found for device {$p_row['name']} ({$p_row['id']}.");
                        }
                    }

                    if ($l_use_default_template && $newObject) {
                        if (isset($this->m_obj_type_tpls[$p_row['idoit_obj_type']][C__CMDB__CATEGORY__TYPE_GLOBAL][C__CATG__AP_CONTROLLER])) {
                            $l_black_list[C__CMDB__CATEGORY__TYPE_GLOBAL][C__CATG__AP_CONTROLLER] = true;
                            if (isset($l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' . C__CATG__AP_CONTROLLER]['category_entities'])) {
                                $l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' .
                                C__CATG__AP_CONTROLLER]['category_entities'] = array_replace_recursive(
                                    $this->m_obj_type_tpls[$p_row['idoit_obj_type']][C__CMDB__CATEGORY__TYPE_GLOBAL][C__CATG__AP_CONTROLLER]['category_entities'],
                                    $l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' . C__CATG__AP_CONTROLLER]['category_entities']
                                );
                            } else {
                                $l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' .
                                C__CATG__AP_CONTROLLER]['category_entities'] = $this->m_obj_type_tpls[$p_row['idoit_obj_type']][C__CMDB__CATEGORY__TYPE_GLOBAL][C__CATG__AP_CONTROLLER]['category_entities'];
                            }
                        }
                    }
                }
            }

            $l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' .
            C__CATG__JDISC_DEVICE_INFORMATION] = isys_jdisc_dao_devices::instance($this->m_db)
                ->prepareDeviceInformation($p_row, $l_object_id);

            if ($l_use_default_template && $newObject && isset($this->m_obj_type_tpls[$p_row['idoit_obj_type']])) {
                // Merge missing categories from the default template
                isys_jdisc_dao_devices::instance($this->m_db)
                    ->merge_default_template($l_object[$l_object_id], $this->m_obj_type_tpls[$p_row['idoit_obj_type']], $l_black_list);
            }

            // prepare custom attributes
            if (!$this->m_is_jedi && $this->m_import_custom_attributes) {
                // Reset category cluster memberships
                if (isys_jdisc_dao_data::clear_data() === true) {
                    $this->m_dao->clear_category('isys_catg_jdisc_ca_list', $l_object_id, 'C__CATG__JDISC_CA', false);
                }

                $this->m_log->debug('Prepare custom attributes');
                $l_object[$l_object_id]['categories'][C__CMDB__CATEGORY__TYPE_GLOBAL . '_' .
                defined_or_default('C__CATG__JDISC_CA')] = isys_jdisc_dao_custom_attributes::instance($this->m_db)
                    ->get_custom_attributes_by_device($p_row['id'], $l_object_id, false);
            }

            // Mark object as non-dummy:
            $l_connections[$l_object_id][isys_import_handler_cmdb::C__DUMMY] = false;
            $p_object_ids = $p_object_ids + isys_jdisc_dao_devices::instance($this->m_db)
                    ->get_object_ids();
            $p_jdisc_to_idoit = $p_jdisc_to_idoit + isys_jdisc_dao_devices::instance($this->m_db)
                    ->get_jdisc_to_idoit_objects();
            $this->m_log->debug('# Preparation finished!');

            isys_jdisc_dao_network::instance($this->m_db)
                ->cache_data($l_object_id, null, null);
            isys_jdisc_dao_software::instance($this->m_db)
                ->cache_data($l_object_id, null, null);

            isys_jdisc_dao_software_database::instance($this->m_db)
                ->cache_data($l_object_id, null, null);

            isys_jdisc_dao_category_connection_endpoint::instance($this->m_db)
                ->cache_data($l_object_id, null, null);

            // create Mapping
            if (!empty($p_row['mappingData'])) {
                $l_mapping = Mapping::instance($this->m_db)->invalidateMapping();
                try {
                    $l_mapping->createMapping($l_object_id, $p_row['mappingData']);
                } catch (Throwable $e) {
                    $this->m_log->error("Could not create mapping for Device {$p_row['name']} {$l_object_id}.");
                    $this->m_log->debug("Please check following mapping data: " . implode(', ', $p_row['mappingData']));
                    // Delete mapping as it is apparently not unique so it is better to remove it
                    $l_mapping->deleteMappingByMatchingData($p_row['mappingData']);
                }
            }

            $l_return = [
                'object'      => $l_object,
                'connections' => $l_connections
            ];
            unset($l_object);
            unset($l_connections);

            return $l_return;
        }

        return false;
    }

    /**
     * "mod.cmdb.objectDeleted" event handler, clear JDisc -> i-doit mapping
     *
     * @param int           $p_objID
     * @param isys_cmdb_dao $p_dao
     *
     * @return void
     * @throws Exception
     */
    public function clearMapping($p_objID, $p_dao): void
    {
        Mapping::instance($p_dao->get_database_component())
            ->deleteMappingByReferenceId($p_objID);
    }

    /**
     * Starts module. Acts as a dispatcher for nodes and actions.
     *
     * @return $this
     * @deprecated will be removed after all routes are working
     */
    public function start()
    {
        return $this;
    }

    /**
     * @param string $name
     *
     * @return array|null
     * @throws isys_exception_database
     */
    public function getProfileByName(string $name): ?array
    {
        return $this->m_dao
            ->retrieve('SELECT * FROM isys_jdisc_profile WHERE isys_jdisc_profile__title = ' . $this->m_dao->convert_sql_text($name) . ' LIMIT 1;')
            ->get_row();
    }

    /**
     * @param int $profile_id
     * @param array $data
     *
     * @return void
     * @throws isys_exception_dao
     */
    public function assignObjectTypes($profile_id, $data, ?array $objectMatching = null): void
    {
        $sql = "DELETE FROM isys_jdisc_object_type_assignment
            WHERE isys_jdisc_object_type_assignment__isys_jdisc_profile__id =  " . $this->m_dao->convert_sql_int($profile_id) . ";";

        $this->m_dao->update($sql) && $this->m_dao->apply_update();

        foreach ($data as $value) {
            $locationId = $this->getLocation((int) $value['location']);

            if (is_array($objectMatching) && !isset($value['matching_profile'])) {
                $value['matching_profile'] = $objectMatching;
            }
            if (is_int($value['matching_profile'])) {
                $matchDao = MatchDao::instance(isys_application::instance()->container->get('database'));
                // Use default if not matching profile has been found
                $value['matching_profile'] = $matchDao->getMatchProfileById($value['matching_profile']) ??
                    $matchDao->getMatchProfileById(1);
            }
            $matchingProfileId = $this->getOrCreateObjectMatching($value['matching_profile']);

            $query = 'INSERT INTO isys_jdisc_object_type_assignment SET
                isys_jdisc_object_type_assignment__isys_jdisc_profile__id = ' . $this->m_dao->convert_sql_int($profile_id) . ',
                isys_jdisc_object_type_assignment__jdisc_type = ' . $this->m_dao->convert_sql_int($value['jdisc_type']) . ',
                isys_jdisc_object_type_assignment__jdisc_type_customized = ' . $this->m_dao->convert_sql_text($value['jdisc_type_customized']) . ',
                isys_jdisc_object_type_assignment__jdisc_os = ' . $this->m_dao->convert_sql_int($value['jdisc_os']) . ',
                isys_jdisc_object_type_assignment__jdisc_os_customized = ' . $this->m_dao->convert_sql_text($value['jdisc_os_customized']) . ',
                isys_jdisc_object_type_assignment__title_transform = ' . $this->m_dao->convert_sql_int($value['title_transform']) . ',
                isys_jdisc_object_type_assignment__fqdn_addition = ' . $this->m_dao->convert_sql_int($value['fqdn_addition']) . ',
                isys_jdisc_object_type_assignment__isys_obj_type__id = ' . $this->m_dao->convert_sql_int($value['object_type']) . ',
                isys_jdisc_object_type_assignment__port_filter = ' . $this->m_dao->convert_sql_text($value['port_filter']) . ',
                isys_jdisc_object_type_assignment__port_filter_type = ' . $this->m_dao->convert_sql_text($value['port_filter_type']) . ',
                isys_jdisc_object_type_assignment__object_location__id = ' . ($locationId ?: 'NULL') . ',
                isys_jdisc_object_type_assignment__isys_obj_match__id = ' . $this->m_dao->convert_sql_int($matchingProfileId) . ';';

            $this->m_dao->update($query) && $this->m_dao->apply_update();
        }
    }

    /**
     * @param array $objectMatching
     *
     * @return int|null
     * @throws isys_exception_dao
     * @throws isys_exception_database
     */
    private function getOrCreateObjectMatching(array $objectMatching): ?int
    {
        $matchDao = new MatchDao(isys_application::instance()->container->get('database'));
        $matchingProfileData = $matchDao->getMatchProfileByTitle($objectMatching['isys_obj_match__title']);
        $matchingProfileId = null;

        if (!empty($matchingProfileData)) {
            $matchFound = true;
            foreach ($objectMatching as $key => $value) {
                if (!array_key_exists($key, $matchingProfileData) || $value !== $matchingProfileData[$key]) {
                    if ($key == 'isys_obj_match__id') {
                        continue;
                    }
                    $matchFound = false;
                    break;
                }
            }

            if ($matchFound) {
                return (int)$matchingProfileData['isys_obj_match__id'];
            }
        }

        if ($matchingProfileId === null) {
            $matchDao->createMatchProfile($objectMatching['isys_obj_match__title'], $objectMatching['isys_obj_match__bits'], $objectMatching['isys_obj_match__min_match']);
            $matchDao->apply_update();

            return $matchDao->get_last_insert_id();
        }

        return null;
    }

    /**
     * @param array $data
     *
     * @return int|bool
     * @throws isys_exception_dao
     */
    public function saveRawProfileData(array $data)
    {
        $values = [];
        $properties = $this->m_dao->get_properties(isys_jdisc_dao::C__PROFILES);

        foreach ($data as $key => $value) {
            if (in_array($key, self::NOT_IMPORTED_FIELDS)) {
                continue;
            }

            $column = $properties[$key][C__PROPERTY__DATA][C__PROPERTY__DATA__FIELD];

            if ($properties[$key][C__PROPERTY__DATA][C__PROPERTY__DATA__TYPE] == 'int') {
                $value = $this->m_dao->convert_sql_int($value);
            } else {
                $value = $this->m_dao->convert_sql_text($value);
            }

            $values[] = "{$column} = {$value}";
        }

        $query = 'INSERT INTO isys_jdisc_profile SET ' . implode(', ', $values) . ';';

        if ($this->m_dao->update($query) && $this->m_dao->apply_update()) {
            return $this->m_dao->get_last_insert_id();
        }

        return false;
    }

    /**
     * @param int   $id
     * @param array $data
     *
     * @return bool
     * @throws isys_exception_dao
     */
    public function updateRawProfileData(int $id, array $data): bool
    {
        $values = [];
        $properties = $this->m_dao->get_properties(isys_jdisc_dao::C__PROFILES);

        foreach ($data as $key => $value) {
            if (in_array($key, self::NOT_IMPORTED_FIELDS)) {
                continue;
            }

            if ($column = $properties[$key][C__PROPERTY__DATA][C__PROPERTY__DATA__FIELD]) {
                if ($properties[$key][C__PROPERTY__DATA][C__PROPERTY__DATA__TYPE] == 'int') {
                    $value = $this->m_dao->convert_sql_int($value);
                } else {
                    $value = $this->m_dao->convert_sql_text($value);
                }

                $values[] = "{$column} = {$value}";
            }
        }

        if (empty($values)) {
            return true;
        }

        $query = 'UPDATE isys_jdisc_profile SET ' . implode(', ', $values) . ' WHERE isys_jdisc_profile__id = ' . $this->m_dao->convert_sql_id($id) . ';';

        return $this->m_dao->update($query) && $this->m_dao->apply_update();
    }

    /**
     * Initiates module.
     *
     * @param   isys_module_request &$p_req
     *
     * @return  isys_module_synetics_jdisc
     */
    public function init(isys_module_request $p_req)
    {
        // Set request information:
        $this->moduleRequest = $p_req;

        $this->check_requirements();

        return $this;
    }

    /**
     * Checks for the current JDisc Version
     */
    public function check_version($p_jdisc_servier_id): bool
    {
        $l_current_version = $this->m_dao->get_version($p_jdisc_servier_id);
        $configuration = $this->m_dao->get_configuration(null, ['id' => $p_jdisc_servier_id]);
        $l_config = array_pop($configuration);

        if ((bool)$l_config['version_check'] === false) {
            return true;
        }

        return $l_current_version >= self::C__MODULE__JDISC__VERSION;
    }

    /**
     * Gets the currently installed JDisc version
     *
     * @return float
     */
    public function get_version($p_jdisc_server_id)
    {
        return $this->m_dao->get_version($p_jdisc_server_id);
    }

    /**
     * Prepares the filter for the device query
     *
     * @param $p_filter_type
     * @param $p_filter_data
     */
    public function prepare_filter($p_filter_type, $p_filter_data): void
    {
        switch ($p_filter_type) {
            case 'filter_hostaddress':
                $this->prepare_ip_filter($p_filter_data);
                break;
        }
    }

    /**
     * Checks if jdisc profile with specified id exists
     *
     * @param $p_id
     *
     * @return bool
     */
    public function check_profile($p_id)
    {
        return $this->m_dao->profile_exists($p_id);
    }

    /**
     * Switches the database
     *
     * @param $p_id
     * @return bool
     */
    public function switch_database($p_id)
    {
        $this->m_server_id = $p_id;

        return $this->m_dao->switch_database($p_id);
    }

    /**
     * Gets jdisc operating systems
     *
     * @return array
     */
    public function get_jdisc_operating_systems()
    {
        return $this->m_dao->get_jdisc_operating_systems();
    }

    /**
     * Gets counts for software, network, cluster and blade chassis connections
     *
     * @return array
     * @throws Exception
     */
    public function get_count_for_options(): array
    {
        return [
            // Import software:
            'software_counter'          => isys_jdisc_dao_software::instance($this->m_db)->count_software(),
            // Import layer 3 nets:
            'network_counter'           => isys_jdisc_dao_network::instance($this->m_db)->count_networks(),
            // Import cluster:
            'cluster_counter'           => isys_jdisc_dao_cluster::instance($this->m_db)->count_cluster(),
            // Import blade chassis:
            'blade_connections_counter' => isys_jdisc_dao_devices::instance($this->m_db)->count_chassis_connections(),
        ];
    }

    /**
     * Gets all jdisc groups
     *
     * @return array
     */
    public function get_jdisc_groups()
    {
        return $this->m_dao->get_jdisc_groups();
    }

    /**
     * Gets all or one specific jdisc profile
     *
     * @param int  $p_id
     * @param bool $p_default_server
     *
     * @return array|isys_component_dao_result
     */
    public function get_jdisc_profiles($p_id = null, $p_default_server = false)
    {
        if ($p_id !== null) {
            $l_condition = ['jdisc_server' => [$p_id]];
        } else {
            $l_condition = null;
        }

        if ($p_default_server) {
            $l_condition['jdisc_server'][] = 'null';
        }

        return $this->m_dao->get_profiles(null, $l_condition);
    }

    /**
     * Checks if the selected profile is assigned to the specified jdisc server
     *
     * @param $p_profile_id
     * @param $p_jdisc_server
     *
     * @return bool
     */
    public function check_profile_in_server($p_profile_id, $p_jdisc_server)
    {
        $l_condition = [
            'id'           => $p_profile_id,
            'jdisc_server' => $p_jdisc_server
        ];

        $serverDataFromProfile = $this->get_jdisc_server_by_profile($p_profile_id);
        $serverIsDefault = $this->get_jdisc_servers($p_jdisc_server, true)->count();
        if ($serverDataFromProfile === null && $serverIsDefault > 0) {
            return true;
        }

        return count($this->m_dao->get_profiles(null, $l_condition)) > 0;
    }

    /**
     * Get jdisc discovery data
     *
     * @param      $p_id
     *
     * @param bool $p_default
     *
     * @return mixed
     */
    public function get_jdisc_discovery_data($p_id = null, $p_default = false)
    {
        return $this->m_dao->get_jdisc_discovery_data($p_id, $p_default);
    }

    /**
     * Gets all jdisc servers
     *
     * @param null $p_id
     * @param bool $p_default_server
     *
     * @return isys_component_dao_result
     */
    public function get_jdisc_servers($p_id = null, $p_default_server = false)
    {
        return $this->m_dao->get_jdisc_servers($p_id, $p_default_server);
    }

    /**
     * Gets all jdisc servers as array
     *
     * @param bool $raw
     *
     * @return array
     */
    public function get_jdisc_servers_as_array($raw = false)
    {
        $l_res = $this->get_jdisc_servers();
        $l_return = [];

        while ($l_row = $l_res->get_row()) {
            if ($raw) {
                $l_return[$l_row['isys_jdisc_db__id']] = $l_row;
            } else {
                $l_return[$l_row['isys_jdisc_db__id']] = $l_row['isys_jdisc_db__host'] . ':' . $l_row['isys_jdisc_db__port'] . ' (' .
                    ($l_row['isys_jdisc_db__title'] ? $l_row['isys_jdisc_db__title'] . ' -> ' . $l_row['isys_jdisc_db__database'] : $l_row['isys_jdisc_db__database']) . ')';
            }
        }

        return $l_return;
    }

    /**
     * Callback method for retrieving the all jdisc servers as an array
     *
     * @param isys_request $p_request
     *
     * @return string
     */
    public function callback_get_jdisc_servers_as_array(isys_request $p_request)
    {
        $l_module = isys_module_synetics_jdisc::factory();
        $l_arr = $l_module->get_jdisc_servers_as_array();

        return serialize($l_arr);
    }

    /**
     * Retrieve mac addresses used for identification for the selected device
     *
     * @param int  $p_device_id
     *
     * @return array
     */
    public function get_mac_addresses_4_identification($p_device_id)
    {
        return isys_jdisc_dao_network::instance($this->m_db)
            ->get_mac_addresses_4_identification($p_device_id);
    }

    /**
     * Wrapper for checking if the current selected jdisc-server is a jedi version or not
     *
     * @return bool
     */
    public function is_jedi()
    {
        return $this->m_dao->is_jedi_version();
    }

    /**
     * Wrapper for retrieving the jdisc server by jdisc profile
     *
     * @param int $p_jdisc_profile_id
     *
     * @return array
     * @throws isys_exception_database
     */
    public function get_jdisc_server_by_profile($p_jdisc_profile_id)
    {
        return $this->m_dao->get_jdisc_server_by_profile($p_jdisc_profile_id)
            ->get_row();
    }

    /**
     * Check if web service is active or not
     *
     * @param $p_jdisc_server_id
     *
     * @return bool
     */
    public function web_service_active($p_jdisc_server_id)
    {
        $l_discovery_data = $this->m_dao->get_jdisc_discovery_data($p_jdisc_server_id)
            ->get_row();
        if (empty($l_discovery_data) || empty($l_discovery_data['isys_jdisc_db__discovery_username'])) {
            return false;
        }
        try {
            isys_jdisc_dao_discovery::get_instance($p_jdisc_server_id)->connect(
                $l_discovery_data['isys_jdisc_db__host'],
                $l_discovery_data['isys_jdisc_db__discovery_username'],
                isys_helper_crypt::decrypt($l_discovery_data['isys_jdisc_db__discovery_password']),
                $l_discovery_data['isys_jdisc_db__discovery_port'],
                $l_discovery_data['isys_jdisc_db__discovery_protocol']
            )
                ->disconnect();
            return true;
        } catch (Exception $e) {
            // do nothing
        }
        return false;
    }

    /**
     * @param $serverId
     *
     * @return bool
     */
    public function isGraphqlAvailable($serverId)
    {
        try {
            $connector = Connector::instance($serverId);
            $connector->connect();
            return true;
        } catch (Exception $e) {
            // do nothing
        }
        return false;
    }

    /**
     * Called after a view was processed
     *
     * @param isys_cmdb_view $p_cmdb_view
     * @param mixed          $p_process_result
     */
    public function slot_view_proceessed($p_cmdb_view, $p_process_result)
    {
        global $index_includes, $g_absdir;

        if ($p_cmdb_view->get_id() == C__CMDB__VIEW__CONFIG_OBJECTTYPE) {
            $l_object_type = ($_POST['id'][0]) ?: $_GET[C__CMDB__GET__OBJECTTYPE];
            if ($l_object_type > 0 || $_POST[C__GET__NAVMODE] == C__NAVMODE__NEW) {
                $l_dao = isys_jdisc_dao::instance($this->m_db, $this->m_log);
                $l_profiles = $l_dao->get_profiles();
                $l_dialog_data = $l_jdisc_servers = [];
                $l_default_jdisc_profile = null;
                $l_default_jdisc_server = null;
                $l_jdisc_servers_res = $l_dao->get_jdisc_server_list();
                while ($l_row = $l_jdisc_servers_res->get_row()) {
                    if ($l_row['isys_jdisc_db__default_server'] > 0) {
                        $l_default_jdisc_server = $l_row['isys_jdisc_db__id'];
                    }
                    $l_jdisc_servers[$l_row['isys_jdisc_db__id']] = $l_row['isys_jdisc_db__host'] . ':' . $l_row['isys_jdisc_db__database'] .
                        ($l_row['isys_jdisc_db__title'] ? ' (' . $l_row['isys_jdisc_db__title'] . ')' : '');
                }

                if (is_array($l_profiles) && count($l_profiles)) {
                    foreach ($l_profiles as $l_id => $l_profile) {
                        $l_key = $l_profile['jdisc_server'] ?: $l_default_jdisc_server;
                        if ($l_key) {
                            $l_dialog_data[$l_jdisc_servers[$l_key]][$l_id] = $l_profile['title'];
                        }
                    }
                }

                if ($l_object_type) {
                    $l_default_jdisc_profile = $p_cmdb_view->get_dao_cmdb()
                        ->get_object_types($l_object_type)
                        ->get_row_value('isys_obj_type__isys_jdisc_profile__id');
                }
                $l_rules["C__JDISC_DEFAULT__PROFILE"] = [
                    'p_arData'        => $l_dialog_data,
                    'p_strSelectedID' => ($l_default_jdisc_profile) ? $l_default_jdisc_profile : null
                ];
                $index_includes['contentbottomcontentaddition'][] = $g_absdir . '/src/classes/modules/synetics_jdisc/templates/obj_type_config.tpl';
                isys_application::instance()->template->smarty_tom_add_rules("tom.content.bottom.content", $l_rules);
            }
        }
    }

    /**
     * Called after an object type is saved
     *
     * @param int     $p_objtype_id
     * @param array   $p_posts
     * @param boolean $p_was_saved
     */
    public function slot_after_obj_type_save($p_objtype_id, $p_posts, $p_was_saved = true)
    {
        // If object type was saved correctly, go further
        if ($p_was_saved && isset($p_posts['C__JDISC_DEFAULT__PROFILE'])) {
            $l_dao = isys_jdisc_dao::instance($this->m_db);
            $l_dao->set_jdisc_default_profile($p_objtype_id, $p_posts['C__JDISC_DEFAULT__PROFILE']);
        }
    }

    /**
     * Callback method for retrieving the all jdisc servers as an array
     *
     * @param isys_request $p_request
     *
     * @return array
     */
    public function callback_get_cmdb_status_as_array(isys_request $p_request)
    {
        $l_dao = isys_cmdb_dao_status::factory($this->m_db);
        $blacklistedStatuses = filter_defined_constants([
            'C__CMDB_STATUS__IDOIT_STATUS',
            'C__CMDB_STATUS__IDOIT_STATUS_TEMPLATE'
        ]);
        $blacklistedCondition = '';
        if (!empty($blacklistedStatuses)) {
            $blacklistedCondition = ' AND isys_cmdb_status__id NOT IN (' . implode(', ', $blacklistedStatuses) . ')';
        }
        $l_res = $l_dao->get_cmdb_status(null, $blacklistedCondition);
        $l_arr = [
            '-1' => $this->language->get('LC__MODULE__JDISC__PROFILES__KEEP_CMDB_STATUS')
        ];
        while ($l_row = $l_res->get_row()) {
            $l_arr[$l_row['isys_cmdb_status__id']] = $this->language->get($l_row['isys_cmdb_status__title']);
        }

        return $l_arr;
    }

    /**
     * Callback method for retrieving the all CMDB statuses as an array, plus the "–" as a special null value
     *
     * @param  isys_request $p_request
     * @return array
     */
    public function cmdbStatusesListForNewObjects(isys_request $p_request): array
    {
        $statuses = $this->callback_get_cmdb_status_as_array($p_request);
        $statuses[-1] = '–';
        return $statuses;
    }

    /**
     * Loads profile from database.
     *
     * @param int $p_id Identifier
     *
     * @return array Associative array
     */
    public function load_profile($p_id)
    {
        $l_data = [];
        $profile = current($this->m_dao->get_profile($p_id));
        $l_data[isys_jdisc_dao::C__PROFILES] = $profile;
        $l_data[isys_jdisc_dao::C__OBJECT_TYPE_ASSIGNMENTS] = $this->m_dao->get_object_type_assignments_by_profile($p_id);

        foreach ($l_data[isys_jdisc_dao::C__OBJECT_TYPE_ASSIGNMENTS] as $key => $value) {
            $l_data[isys_jdisc_dao::C__OBJECT_TYPE_ASSIGNMENTS][$key][self::C__EXPORT_MATCHING_PROFILE] =
                (new MatchDao(isys_application::instance()->container->get('database')))->getMatchProfileById($value['matching_profile']);
        }

        return $l_data;
    }

    /**
     * Saves profile.
     *
     * @param int $p_id (optional) Profile identifier. If set, an existing
     *                  profile will be updated, otherwise a new one will be created. Defaults to
     *                  null (create).
     */
    public function save_profile($p_id = null)
    {
        $l_data = [];
        $l_result = [];
        $l_validation_failed = false;
        $l_id = null;

        // Profile:

        $l_property_type = isys_jdisc_dao::C__PROFILES;
        $l_properties = $this->m_dao->get_properties($l_property_type);
        $l_data[$l_property_type] = $this->m_dao->transformDataByProperties($l_properties, $this->moduleRequest->get_posts());

        $l_result[$l_property_type] = $this->validate_property_data($l_properties, $l_data[$l_property_type]);

        $l_save_data = [];

        foreach ($l_properties as $l_property_id => $l_property_info) {
            // If identifier is not valid, just ignore it. A new entity will be
            // created.
            if ($l_property_id === 'id' && ((isset($l_data[$l_property_type]['id']) && $l_data[$l_property_type]['id'] < 1) || !isset($l_data[$l_property_id]['id']))) {
                $l_result[$l_property_type]['id'] = isys_module_dao::C__VALIDATION_RESULT__IGNORED;
                continue;
            }

            if (array_key_exists($l_property_id, $l_data[$l_property_type]) && array_key_exists($l_property_id, $l_result[$l_property_type]) &&
                $l_result[$l_property_type][$l_property_id] > isys_module_dao::C__VALIDATION_RESULT__IGNORED) {
                $l_validation_failed = true;
                break;
            }

            // Serialize categories:
            if ($l_property_id === 'categories') {
                $l_data[$l_property_type][$l_property_id] = serialize($l_data[$l_property_type][$l_property_id]);
            }
            if ($l_property_id === 'network_adresses') {
                $l_data[$l_property_type][$l_property_id] = serialize($l_data[$l_property_type][$l_property_id]);
            }

            // format location properties
            $locationProperties = ['software_location', 'network_location'];
            if (in_array($l_property_id, $locationProperties) && is_array($l_data[$l_property_type][$l_property_id]) && !empty($l_data[$l_property_type][$l_property_id])) {
                // get first element of location array
                $l_data[$l_property_type][$l_property_id] = $l_data[$l_property_type][$l_property_id][0];
            }

            // Save property only if create and save are provided:
            if (array_key_exists(C__PROPERTY__PROVIDES, $l_property_info)) {
                if ((isys_module_dao::C__PROPERTY__PROVIDES__CREATE & $l_property_info[C__PROPERTY__PROVIDES]) ||
                    (isys_module_dao::C__PROPERTY__PROVIDES__SAVE & $l_property_info[C__PROPERTY__PROVIDES])) {
                    $l_save_data[$l_property_id] = $l_data[$l_property_type][$l_property_id];
                }
            }
        }

        if (isset($p_id)) {
            // Update identifier:
            $l_save_data['id'] = $p_id;
        }

        if ($l_validation_failed === false) {
            $l_id = $this->m_dao->save($l_property_type, $l_save_data);

            // Update identifier:
            $l_data[$l_property_type]['id'] = $l_id;
            $l_result[$l_property_type]['id'] = isys_module_dao::C__VALIDATION_RESULT__NOTHING;
        }

        // Object Type Assignments:

        $l_property_type = isys_jdisc_dao::C__OBJECT_TYPE_ASSIGNMENTS;
        $l_properties = $this->m_dao->get_properties($l_property_type);
        $l_data[$l_property_type] = $this->m_dao->transformDataByProperties($l_properties, $this->moduleRequest->get_posts());
        $l_result[$l_property_type] = $this->validate_property_data($l_properties, $l_data[$l_property_type]);

        if ($l_validation_failed === false) {
            // Update notification identifier:
            $l_data[$l_property_type]['profile'] = $l_id;
            $l_result[$l_property_type]['profile'] = isys_module_dao::C__VALIDATION_RESULT__NOTHING;
        }

        foreach ($l_result[$l_property_type] as $l_property_id => $l_property_result) {
            // Ignore some properties which are unnecessary here:
            if (in_array($l_property_id, [
                'id',
                'profile'
            ])) {
                continue;
            }

            if ($l_property_result > isys_module_dao::C__VALIDATION_RESULT__IGNORED) {
                $l_validation_failed = true;
                break;
            }
        }

        $l_matrix = [
            'jdisc_type',
            'jdisc_type_customized',
            'jdisc_os',
            'jdisc_os_customized',
            'title_transform',
            'fqdn_addition',
            'object_type',
            'port_filter',
            'port_filter_type',
            'location',
            'matching_profile',
        ];

        if ($l_validation_failed === false) {
            $l_append = ['profile' => $l_id];

            $l_entities = $this->matrix_2_entities($l_data[$l_property_type], $l_matrix, $l_append);
            // Cleanup before save:
            $this->m_dao->delete($l_property_type, ['profile' => $l_id]);

            foreach ($l_entities as $l_save_data) {
                $this->m_dao->save($l_property_type, $l_save_data);
            }
        }

        // Re-build matrix:
        $l_rebuilt_data = [];
        foreach ($l_data[$l_property_type] as $l_property => $l_values) {
            if (in_array($l_property, $l_matrix) && is_array($l_values)) {
                foreach ($l_values as $l_key => $l_value) {
                    $l_rebuilt_data[$l_key][$l_property] = $l_value;
                }
            }
        }
        $l_counter = 0;
        $l_rerebuilt_data = [];
        foreach ($l_rebuilt_data as $l_rebuilt_datum) {
            $l_rerebuilt_data[$l_counter] = $l_rebuilt_datum;
            // Add profile identifier and temporary entity identifier (this is
            // just helpful for identifying entities via JavaScript):
            $l_rerebuilt_data[$l_counter]['id'] = $l_counter;
            $l_rerebuilt_data[$l_counter]['profile'] = $l_id;
            $l_rerebuilt_data[$l_counter]['matching_profile'] =
                is_numeric($l_rerebuilt_data[$l_counter]['matching_profile']) ?
                    (new MatchDao(isys_application::instance()->container->get('database')))
                        ->getMatchProfileById($l_rerebuilt_data[$l_counter]['matching_profile']) : $l_rerebuilt_data[$l_counter]['matching_profile'];
            $l_counter++;
        }
        $l_data[$l_property_type] = $l_rerebuilt_data;
    }

    /**
     * Builds entities based on properties out of a combination of properties
     * called 'matrix'.
     *
     * @param array $p_data       Associative array of property names as keys and data
     *                            content as values
     * @param array $p_properties Array of property names (strings) which will
     *                            be handled
     * @param array $p_append     (optional) Associative array of other property
     *                            names as keys and some value as values to enrich the entities. Useful to
     *                            assign identifiers. Defaults to null.
     *
     * @return array Empty array or array of entities
     */
    private function matrix_2_entities(&$p_data, $p_properties, $p_append = null)
    {
        assert(is_array($p_data));
        assert(is_array($p_properties));

        $l_entities = [];

        // Fetch property with maximum amount of values:
        $l_count = 0;

        foreach ($p_properties as $l_property) {
            if (is_array($p_data[$l_property])) {
                $l_count_property = count($p_data[$l_property]);
                if (!empty($p_data['port_filter_type'][0])) {
                    $p_data['port_filter'] = array_values($p_data['port_filter']);
                    $p_data['port_filter_type'] = array_values($p_data['port_filter_type']);
                }
                if ($l_count < $l_count_property) {
                    $l_count = $l_count_property;
                }
            }
        }

        for ($l_i = 0;$l_i < $l_count;$l_i++) {
            $l_entity = [];

            foreach ($p_properties as $l_property) {
                if (($l_property == 'port_filter' || $l_property == 'port_filter_type')) {
                    $p_data[$l_property][$l_i] = isys_format_json::encode($p_data[$l_property][$l_i]);
                }

                if (isset($p_data[$l_property][$l_i])) {
                    $l_entity[$l_property] = $p_data[$l_property][$l_i];
                } else {
                    $l_entity[$l_property] = null;
                }
            }

            if (isset($p_append)) {
                assert(is_array($p_append));

                foreach ($p_append as $l_key => $l_value) {
                    $l_entity[$l_key] = $l_value;
                }
            }

            $l_entities[] = $l_entity;
        }

        return $l_entities;
    }

    /**
     * Prepares device filter by hostaddresses
     *
     * @param $p_filter_data
     */
    private function prepare_ip_filter($p_filter_data)
    {
        if (empty($p_filter_data)) {
            return;
        }

        // Join
        $l_device_filter_join = ' LEFT JOIN ip4transport AS ip4 ON ip4.deviceid = d.id ';
        // Condition start
        $l_device_filter_condition = ' AND (';

        $l_ip_arr = [];
        $l_ip_list = null;

        if (strpos($p_filter_data, '|') !== false) {
            $l_ip_arr = explode('|', $p_filter_data);
            $l_ip_list = (!empty($l_ip_arr[0]) ? $l_ip_arr[0] : null);

            $l_single_ip = str_replace('*', '', $l_ip_arr[1]);
            if (Ip::validate_ipv6($l_single_ip)) {
                $l_single_ip = str_replace('*', '%', $l_ip_arr[1]);
                $l_device_filter_join .= ' LEFT JOIN ip6transport AS ip6 ON ip6.deviceid = d.id ';
                $l_device_filter_condition .= 'ip6.address = ' . $this->m_dao->convert_sql_text($l_single_ip) . ' ';
            } else {
                $l_single_ip_long = Ip::ip2long($l_single_ip);
                $l_device_filter_condition .= 'ip4.address = ' . $this->m_dao->convert_sql_text((string)$l_single_ip_long) . ' ';
            }
        }

        if (count($l_ip_arr) == 0) {
            $l_ip_list = $p_filter_data;
        } else {
            $l_device_filter_condition .= ' OR ';
        }

        if (!empty($l_ip_list)) {
            $l_arr = explode(',', $l_ip_list);
            $l_new_arr = [];
            foreach ($l_arr as $l_ip) {
                if (Ip::validate_ipv6($l_ip)) {
                    $l_new_arr['ipv6'][] = $l_ip;
                } elseif (Ip::validate_ip($l_ip)) {
                    $l_new_arr['ipv4'][] = Ip::ip2long($l_ip);
                }
            }

            if (!empty($l_new_arr['ipv4']) && count($l_new_arr['ipv4']) > 0) {
                $l_device_filter_condition .= ' ( ';

                foreach ($l_new_arr['ipv4'] as $l_ip) {
                    $l_device_filter_condition .= "ip4.address = " . $this->m_dao->convert_sql_text((string)$l_ip) . " OR ";
                }
                $l_device_filter_condition = rtrim($l_device_filter_condition, 'OR ');
                $l_device_filter_condition .= ' ) ';
            }

            if (!empty($l_new_arr['ipv6']) && count($l_new_arr['ipv6']) > 0) {
                $l_device_filter_join .= ' LEFT JOIN ip6transport AS ip6 ON ip6.deviceid = d.id ';
                if (!empty($l_new_arr['ipv4']) && count($l_new_arr['ipv4']) > 0) {
                    $l_device_filter_condition .= ' OR ';
                }

                $l_device_filter_condition .= ' ( ';

                foreach ($l_new_arr['ipv6'] as $l_ip) {
                    $l_device_filter_condition .= "ip6.address = " . $this->m_dao->convert_sql_text($l_ip) . " OR ";
                }
                $l_device_filter_condition = rtrim($l_device_filter_condition, 'OR ');
                $l_device_filter_condition .= ' ) ';
            }
        }
        $l_device_filter_condition = rtrim($l_device_filter_condition, 'OR ');
        $l_device_filter_condition .= ' ) ';

        isys_jdisc_dao_devices::instance($this->m_db)
            ->set_device_filter_join($l_device_filter_join);
        isys_jdisc_dao_devices::instance($this->m_db)
            ->set_device_filter_condition($l_device_filter_condition);
    }

    /**
     * Wrapper method which prepares the identifierObjID and identifierID
     *
     * @param PDOStatement                   $p_obj_res
     * @param JDiscProgressBarInterface      $progressBar
     * @param array                          $p_options
     * @param array                          $p_raw_assignments
     * @param JDiscCommandQueue|null         $commandQueue
     * @param callable|null                  $errorCallback
     *
     * @return array
     * @throws Exception
     * @throws isys_exception_general
     */
    public function prepare_devices(PDOStatement $p_obj_res, JDiscProgressBarInterface $progressBar, $p_options = [], $p_raw_assignments = [], ?JDiscCommandQueue $commandQueue = null, ?callable $errorCallback = null)
    {
        $l_mapping    = Mapping::instance($this->m_db)->invalidateMapping();
        $l_matching   = isys_jdisc_dao_matching::instance();
        $l_device_arr = [];
        try {
            while ($l_obj_jdisc_row = $this->m_dao->get_connection()->fetch_row_assoc($p_obj_res)) {
                if ($commandQueue) {
                    $commandQueue->processCommands();
                }

                $l_obj_jdisc_row['identifierObjID'] = null;
                $l_obj_jdisc_row['identifierID'] = null;
                $l_obj_jdisc_row['foundByUniqueId'] = false;
                $l_group_name = null;
                $l_group_arr = null;

                // set matching profile to device by object matching profile or by type
                if ($p_raw_assignments) {
                    if (!empty($l_obj_jdisc_row['matching_profile'])) {
                        $l_matching->setMatcher($l_obj_jdisc_row['matching_profile']);
                    } else {
                        foreach ($p_raw_assignments as $assignment) {
                            if ($l_obj_jdisc_row['type'] == $assignment['jdisc_type']) {
                                $l_matching->setMatcher($assignment['matching_profile'] ?? 1);
                                break;
                            }
                        }
                    }
                }

                if ($l_obj_jdisc_row['group_name']) {
                    $l_group_name = $l_obj_jdisc_row['group_name'];
                    // No Group has been selected for the import but the device is in several groups
                    if (strpos($l_group_name, ',')) {
                        $l_group_arr = explode(',', $l_group_name);
                        $l_group_name = $l_group_arr;
                    }
                }

                if (is_array($l_group_name)) {
                    $l_candidates = [];
                    // We have to check the id with every Group
                    foreach ($l_group_name as $l_group_part) {
                        $l_obj_id = $l_matching
                            ->get_object_id_by_device_id($l_obj_jdisc_row['id'], $l_group_part);
                        if ($l_obj_id) {
                            $l_candidates[$l_obj_id]++;
                        }
                    }

                    $l_candidates_amount = count($l_candidates);
                    if ($l_candidates_amount >= 1) {
                        if ($l_candidates_amount === 1) {
                            // We only have one candidate
                            $l_obj_jdisc_row['identifierObjID'] = key($l_candidates);
                        } else {
                            // We have several candidates
                            $foundCounter = max($l_candidates);
                            $possibleCandidates = array_keys($l_candidates, $foundCounter);
                            $l_obj_jdisc_row['identifierObjID'] = end($possibleCandidates);
                        }
                    }
                    $l_obj_jdisc_row['group_name'] = $l_group_arr;
                } else {
                    $l_obj_jdisc_row['identifierObjID'] = $l_matching
                        ->get_object_id_by_device_id($l_obj_jdisc_row['id'], $l_group_name);
                }

                if (!$l_obj_jdisc_row['identifierObjID'] && !empty($l_obj_jdisc_row['uniqueid'])) {
                    // Check by uniqueid
                    $identifierData = isys_jdisc_dao_matching::instance()->getIdentifierDataByUniqueId(
                        $l_obj_jdisc_row['uniqueid']
                    );
                    $l_obj_jdisc_row['identifierObjID'] = $identifierData['objectId'];
                    $l_obj_jdisc_row['identifierID'] = $identifierData['id'];
                    $l_obj_jdisc_row['foundByUniqueId'] = true;
                }

                // JDisc Mapping manipulation block
                $l_mac_address  = $this->get_mac_addresses_4_identification($l_obj_jdisc_row['id']);
                $l_mapping_data = [
                    'type'         => $l_obj_jdisc_row['type'],
                    'serialnumber' => $l_obj_jdisc_row['serialnumber'],
                    'mac'          => $l_mac_address,
                ];
                $l_reference_id = $l_mapping->findReferenceId($l_mapping_data);

                // @see ID-11022, we will check the object status later
                if ($l_reference_id) {
                    // set the identifierObjID if it's not already set
                    if (!$l_obj_jdisc_row['identifierObjID']) {
                        $l_obj_jdisc_row['identifierObjID'] = $l_reference_id;
                    }

                    // update Mapping if needed
                    $l_current_mapping_data = $l_mapping->findMapping($l_reference_id);
                    if ($l_current_mapping_data != $l_mapping_data) {
                        try {
                            $l_mapping->updateMapping($l_reference_id, $l_mapping_data);
                        } catch (Throwable $e) {
                            if ($errorCallback !== null) {
                                // Communicate that we'll skip this un-unique object.
                                $errorCallback("Attention! The device mapping is not unique for this entry. The device will be skipped.");
                            }

                            continue;
                        }
                    }
                } else {
                    // prepare the Mapping data to save in prepare_object_data
                    $l_obj_jdisc_row['mappingData'] = $l_mapping_data;
                }

                $l_obj_jdisc_row['filtered'] = $this->isDeviceFiltered(
                    $l_obj_jdisc_row['id'],
                    $l_matching
                );

                $l_device_arr[] = $l_obj_jdisc_row;

                // Update progress bar
                $deviceName = $l_obj_jdisc_row['name'] ? "({$l_obj_jdisc_row['name']})" : '';
                $progressBar->setMessage("Preparing device #{$l_obj_jdisc_row['id']} {$deviceName}");
                $progressBar->advance();
            }
            //             Flush log for device preparation
            //            isys_jdisc_dao_data::instance($this->m_db)->get_log()->flush_log(true, false);
        } catch (Exception $e) {
            throw new Exception($e->getMessage());
        } // try/catch

        return $l_device_arr;
    }

    /**
     * Method for transformation of jdisc objects titles according to i-doit import profile options
     * @see ID-7391 transform imported objects titles
     * @param $p_profile
     * @param $p_device_arr
     * @return array
     */
    public function transformJdiscObjectsTitles($p_profile, $p_device_arr)
    {
        $l_raw_assignments = $this->m_dao->get_object_type_assignments_by_profile($p_profile);
        foreach ($l_raw_assignments as $l_raw_assignment) {
            $jdiscType = $l_raw_assignment['jdisc_type'];
            $fqdnAddition = $l_raw_assignment['fqdn_addition'];
            $titleTransform = $l_raw_assignment['title_transform'];
            if ((!$fqdnAddition) && (($titleTransform === 1) || !$titleTransform)) {
                continue;
            }

            $l_device_arr_processed = [];
            foreach ($p_device_arr as $device) {
                if ($device['type'] != $jdiscType) {
                    $l_device_arr_processed[] = $device;
                    continue;
                }

                $objectTitle = $device['name'];
                switch ($titleTransform) {
                    case isys_ajax_handler_jdisc::C__JDISC__TITLE_TRANSFORM__UPPERCASE:
                        $objectTitle = strtoupper($objectTitle);
                        break;
                    case isys_ajax_handler_jdisc::C__JDISC__TITLE_TRANSFORM__LOWERCASE:
                        $objectTitle = strtolower($objectTitle);
                        break;
                    default:
                        break;
                }
                if ($fqdnAddition) {
                    if (stripos($objectTitle, $fqdnAddition) === false) {
                        $objectTitle .= $fqdnAddition;
                    }
                }
                $device['name'] = $objectTitle;
                $l_device_arr_processed[] = $device;
            }
            $p_device_arr = $l_device_arr_processed;
        }
        return $p_device_arr;
    }

    /**
     * @return Auth|isys_auth_interface
     */
    public static function getAuth()
    {
        return Auth::instance();
    }

    /**
     * @param      $expired
     *
     * @param bool $withText
     *
     * @return string
     * @throws Exception
     */
    public static function getStatusMarker($expired, $withText = true)
    {
        $color = '33C20A';
        $stateText = 'LC__CMDB__CATG__SUPPORT_ENTITLEMENT__STATE_ACTIVE';
        if (!$expired) {
            $color = 'BC0A19';
            $stateText = 'LC__CMDB__CATG__SUPPORT_ENTITLEMENT__STATE_INACTIVE';
        }

        return '<div class="cmdb-marker support-entitlement" style="background-color:#' . $color . ';"></div>' .
            ($withText ? isys_application::instance()->container->get('language')->get($stateText) : '') . '';
    }

    /**
     * Calculates the expiration of the support entitlement in days
     *
     * @param DateTime $startDate
     * @param DateTime $endDate
     *
     * @return string|int
     * @throws Exception
     */
    public static function expiresInDays(DateTime $startDate, DateTime $endDate)
    {
        $now = new DateTime();
        $dateDiffStart = $startDate->diff($now);
        $addition = 1;

        if ($dateDiffStart->invert > 0) {
            // Start date not reached use $startDate for $now
            return isys_application::instance()->container->get('language')->get('LC__CMDB__CATG__SUPPORT_ENTITLEMENT__STATE_NOT_STARTED');
        }
        $dateDiff = $endDate->diff($now);

        if ($dateDiff->invert < 1 && $dateDiff->days > 0) {
            // already expired
            return isys_application::instance()->container->get('language')->get('LC__CMDB__CATG__SUPPORT_ENTITLEMENT__EXPIRED');
        }

        return $dateDiff->days + $addition;
    }

    /**
     * @return string
     */
    public function getLastScannedItem()
    {
        return $this->get_cached_profile()['last_scanned_device'];
    }

    /**
     * @param $scanTime
     *
     * @return void
     */
    public function updateLastScannedDeviceTime($scanTime)
    {
        $profile = $this->get_cached_profile();

        // @see ID-9316 Fix the save logic.
        $id = $this->m_dao->convert_sql_id($profile['id']);
        $scanTime = $this->m_dao->convert_sql_datetime($scanTime);

        $sql = "UPDATE isys_jdisc_profile
            SET isys_jdisc_profile__last_scanned_device = {$scanTime}
            WHERE isys_jdisc_profile__id = {$id}
            LIMIT 1;";

        $this->m_dao->update($sql) && $this->m_dao->apply_update();
    }

    /**
     * Get all software flag
     *
     * @return bool
     */
    public function getAllSoftwareFlag(): bool
    {
        return $this->m_all_software;
    }

    /**
     * Get software location mode
     *
     * @return int
     */
    public function getSoftwareLocationMode(): int
    {
        return $this->softwareLocationMode;
    }

    /**
     * Get software location
     *
     * @return int
     */
    public function getSoftwareLocation(): int
    {
        return $this->softwareLocation;
    }

    /**
     * Get all network flag
     *
     * @return bool
     */
    public function getAllNetworkFlag(): bool
    {
        return $this->m_all_networks;
    }

    /**
     * Get network location mode
     *
     * @return int
     */
    public function getNetworkLocationMode(): int
    {
        return $this->l3LocationMode;
    }

    /**
     * Get network location
     *
     * @return int
     */
    public function getNetworkLocation(): int
    {
        return $this->l3Location;
    }

    /**
     * @param  int $locationId
     * @return int|null
     * @throws isys_exception_database
     */
    private function getLocation(int $locationId): ?int
    {
        if ($locationId <= 0) {
            return null;
        }

        $objectId = (int) $this->m_dao
            ->retrieve("SELECT isys_obj__id AS id FROM isys_obj WHERE isys_obj__id = {$locationId} LIMIT 1;")
            ->get_row_value('id');

        if ($objectId <= 0) {
            return null;
        }

        return $objectId;
    }

    /**
     * @param int $deviceId
     * @param isys_jdisc_dao_matching $matching
     *
     * @return bool
     */
    private function isDeviceFiltered(int $deviceId, isys_jdisc_dao_matching $matching): bool
    {
        $filters  = $matching->getMatcher()->getFilters()->getIdentifiers();
        $keywords = $matching->build_device_keywords($deviceId);
        if (!$filters || !$keywords) {
            return false;
        }
        $minFilter   = $matching->getMatcher()->getConfig()->getFilterMin();
        $filterCount = 0;
        foreach ($filters as $filter) {
            foreach ($keywords as $keyword) {
                if ($keyword->getKey() == $filter::KEY && $keyword->getValue()) {
                    $filterCount++;
                }
            }
        }
        return $filterCount < $minFilter;
    }

    /**
     * Generates a skip message for devices with non-normal status
     *
     * @param string $deviceName
     * @param int $status
     * @return string
     */
    private function getDeviceStatusSkipMessage(string $deviceName, int $status): string
    {
        $statusLabel = match ($status) {
            C__RECORD_STATUS__ARCHIVED => 'archived',
            C__RECORD_STATUS__DELETED => 'deleted',
            C__RECORD_STATUS__BIRTH => 'incomplete',
            default => 'in non-noraml state'
        };

        return "Skipping device $deviceName: device is $statusLabel.";
    }

    /**
     * Checks if a add-on is installed.
     *
     * @return int|bool
     */
    public static function isInstalled()
    {
        return isys_module_manager::instance()
            ->is_installed('jdisc');
    }

    /**
     * Basic installation process for all mandators.
     *
     * @param isys_component_database $tenantDatabase
     * @param isys_component_database $systemDatabase
     * @param int                     $moduleId
     * @param string                  $type
     * @param int                     $tenantId
     * @return bool
     * @throws isys_exception_dao
     */
    public static function install($tenantDatabase, $systemDatabase, $moduleId, $type, $tenantId)
    {
        // @see ID-11999 Here we remove the old JDisc sources.
        $oldJdiscDirectory = BASE_DIR . 'src/classes/modules/jdisc';

        (new Filesystem())->remove($oldJdiscDirectory);

        // Also remove the outdated entry from 'isys_module'. This gets called AFTER the XML files where processed.
        $dao = new isys_cmdb_dao($tenantDatabase);
        $dao->update("DELETE FROM isys_module WHERE isys_module__const = 'C__MODULE__JDISC' LIMIT 1;");
        $dao->apply_update();

        return true;
    }

    /**
     * Uninstall add-on for all mandators.
     *
     * @param isys_component_database $tenantDatabase
     * @return bool
     * @throws isys_exception_dao
     * @throws isys_exception_database
     */
    public static function uninstall($tenantDatabase)
    {
        $dao = new isys_cmdb_dao($tenantDatabase);

        $getModuleQuery = "SELECT isys_module__id AS id
            FROM isys_module
            WHERE isys_module__const = 'C__MODULE__SYNETICS_JDISC'
            LIMIT 1;";
        $moduleId = (int)$dao->retrieve($getModuleQuery)->get_row_value('id');

        // Remove categories.
        $deleteCategoriesQuery = "DELETE FROM isysgui_catg WHERE isysgui_catg__const IN (
            'C__CATG__ASSIGNED_SUBSCRIPTIONS',
            'C__CATG__ASSIGNED_USERS',
            'C__CATG__CLOUD_SUBSCRIPTIONS',
            'C__CATG__CONNECTION_ENDPOINT',
            'C__CATG__JDISC_CA',
            'C__CATG__JDISC_DEVICE_INFORMATION',
            'C__CATG__JDISC_DISCOVERY',
            'C__CATG__SUPPORT_ENTITLEMENT');";

        $dao->update($deleteCategoriesQuery);
        $dao->apply_update();

        // Remove rights.
        $deleteRightsQuery = "DELETE FROM isys_auth WHERE isys_auth__isys_module__id = {$moduleId};";

        $dao->update($deleteRightsQuery);
        $dao->apply_update();

        // FORCEFULLY remove tables.
        $tables = [
            'isys_catg_assigned_subscriptions_list',
            'isys_catg_assigned_users_list',
            'isys_catg_cloud_subscriptions_list',
            'isys_catg_connection_endpoint_list',
            'isys_catg_jdisc_ca_list',
            'isys_catg_jdisc_device_information_list',
            'isys_catg_support_entitlement_list',
            'isys_jdisc_ca_type',
            'isys_jdisc_db',
            'isys_jdisc_device_type',
            'isys_jdisc_log',
            'isys_jdisc_mapping',
            'isys_jdisc_object_type_assignment',
            'isys_jdisc_profile',
            'isys_jdisc_status_list',
        ];

        $dao->update('SET FOREIGN_KEY_CHECKS = 0;');
        $dao->apply_update();

        foreach ($tables as $table) {
            if (!$dao->table_exists($table)) {
                continue;
            }

            try {
                $dao->update("TRUNCATE TABLE {$table};") && $dao->apply_update();
            } catch (Throwable $e) {
            }

            try {
                $dao->update("DROP TABLE {$table};") && $dao->apply_update();
            } catch (Throwable $e) {
            }
        }

        return true;
    }

    /**
     * Checks if a add-on is active.
     *
     * @return int|bool
     */
    public static function isActive()
    {
        return isys_module_manager::instance()
            ->is_installed('jdisc', true);
    }

    /**
     * Method that is called after clicking "activate" in admin center for specific mandator.
     *
     * @param isys_component_database $tenantDatabase
     * @return bool
     */
    public static function activate($tenantDatabase)
    {
        return true;
    }

    /**
     * Method that is called after clicking "deactivate" in admin center for specific mandator.
     *
     * @param isys_component_database $tenantDatabase
     * @return bool
     */
    public static function deactivate($tenantDatabase)
    {
        return true;
    }

    public function validateData(array $properties, array $data): array
    {
        return $this->validate_property_data($properties, $data);
    }

    public static function registerRouting(): void
    {
        isys_application::instance()->container->get('routes')
            ->addCollection((new PhpFileLoader(new FileLocator(__DIR__)))->load('config/routes.php'));
    }

    public function getContainerExtension()
    {
        return new JDiscExtension();
    }

    /**
     * @param bool $allow
     * @return void
     */
    public function allowImportUnnamedDevices(bool $allow = true): void
    {
        $this->m_all_no_title_objects = $allow;
    }
}
