<?php

/*
 * Copyright 2005 - 2025 Centreon (https://www.centreon.com/)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * For more information : contact@centreon.com
 *
 */

namespace CentreonClapi;

use Centreon_Object_DependencyHostParent;
use Centreon_Object_Host;
use Centreon_Object_Host_Category;
use Centreon_Object_Host_Extended;
use Centreon_Object_Host_Group;
use Centreon_Object_Host_Macro_Custom;
use Centreon_Object_Instance;
use Centreon_Object_Relation_Contact_Group_Host;
use Centreon_Object_Relation_Contact_Host;
use Centreon_Object_Relation_Host_Category_Host;
use Centreon_Object_Relation_Host_Group_Host;
use Centreon_Object_Relation_Host_Parent_Host;
use Centreon_Object_Relation_Host_Service;
use Centreon_Object_Relation_Host_Template_Host;
use Centreon_Object_Relation_Instance_Host;
use Centreon_Object_Service;
use Centreon_Object_Service_Extended;
use Centreon_Object_Timezone;
use Core\Host\Domain\Model\NewHost;
use Exception;
use PDOException;
use Pimple\Container;

require_once 'centreonObject.class.php';
require_once 'centreonConfigurationChange.class.php';
require_once 'centreonUtils.class.php';
require_once 'centreonTimePeriod.class.php';
require_once 'centreonACL.class.php';
require_once 'centreonCommand.class.php';
require_once 'centreonExported.class.php';
require_once 'centreonTimezone.class.php';
require_once 'Centreon/Object/Instance/Instance.php';
require_once 'Centreon/Object/Command/Command.php';
require_once 'Centreon/Object/Timeperiod/Timeperiod.php';
require_once 'Centreon/Object/Host/Host.php';
require_once 'Centreon/Object/Host/Extended.php';
require_once 'Centreon/Object/Host/Group.php';
require_once 'Centreon/Object/Host/Category.php';
require_once 'Centreon/Object/Host/Template.php';
require_once 'Centreon/Object/Host/Macro/Custom.php';
require_once 'Centreon/Object/Service/Service.php';
require_once 'Centreon/Object/Service/Extended.php';
require_once 'Centreon/Object/Contact/Contact.php';
require_once 'Centreon/Object/Contact/Group.php';
require_once 'Centreon/Object/Relation/Host/Template/Host.php';
require_once 'Centreon/Object/Relation/Host/Parent/Host.php';
require_once 'Centreon/Object/Relation/Host/Group/Host.php';
require_once 'Centreon/Object/Relation/Host/Child/Host.php';
require_once 'Centreon/Object/Relation/Host/Category/Host.php';
require_once 'Centreon/Object/Relation/Instance/Host.php';
require_once 'Centreon/Object/Relation/Contact/Host.php';
require_once 'Centreon/Object/Relation/Contact/Group/Host.php';
require_once 'Centreon/Object/Relation/Host/Service.php';
require_once 'Centreon/Object/Timezone/Timezone.php';
require_once 'Centreon/Object/Media/Media.php';
require_once 'Centreon/Object/Dependency/DependencyHostParent.php';

/**
 * Class
 *
 * @class CentreonHost
 * @package CentreonClapi
 */
class CentreonHost extends CentreonObject
{
    public const ORDER_UNIQUENAME = 0;
    public const ORDER_ALIAS = 1;
    public const ORDER_ADDRESS = 2;
    public const ORDER_TEMPLATE = 3;
    public const ORDER_POLLER = 4;
    public const ORDER_HOSTGROUP = 5;
    public const MISSING_INSTANCE = 'Instance name is mandatory';
    public const UNKNOWN_NOTIFICATION_OPTIONS = 'Invalid notifications options';
    public const INVALID_GEO_COORDS = 'Invalid geo coords';
    public const UNKNOWN_TIMEZONE = 'Invalid timezone';
    public const HOST_LOCATION = 'timezone';
    public const NAME_IS_EMPTY = 'Host name is mandatory and cannot be left empty';

    /** @var string[] */
    public static $aDepends = ['CMD', 'TP', 'TRAP', 'INSTANCE', 'HTPL'];

    /**
     * @var array
     *            Contains : list of authorized notifications_options for this object
     */
    public static $aAuthorizedNotificationsOptions = ['d' => 'Down', 'u' => 'Unreachable', 'r' => 'Recovery', 'f' => 'Flapping', 's' => 'Downtime Scheduled', 'n' => 'None'];

    /** @var Centreon_Object_Timezone */
    protected $timezoneObject;

    /** @var int */
    protected $register = 1;

    /** @var array */
    protected $templateIds;

    /** @var array */
    protected $hostgroupIds;

    /** @var mixed */
    protected $instanceId;

    /**
     * CentreonHost constructor
     *
     * @param Container $dependencyInjector
     *
     * @throws PDOException
     */
    public function __construct(Container $dependencyInjector)
    {
        parent::__construct($dependencyInjector);
        $this->object = new Centreon_Object_Host($dependencyInjector);
        $this->timezoneObject = new Centreon_Object_Timezone($dependencyInjector);
        $this->params = ['host_active_checks_enabled' => '2', 'host_passive_checks_enabled' => '2', 'host_checks_enabled' => '2', 'host_obsess_over_host' => '2', 'host_check_freshness' => '2', 'host_event_handler_enabled' => '2', 'host_flap_detection_enabled' => '2', 'host_process_perf_data' => '2', 'host_retain_status_information' => '2', 'host_retain_nonstatus_information' => '2', 'host_notifications_enabled' => '2', 'host_register' => '1', 'host_activate' => '1'];
        $this->insertParams = ['host_name', 'host_alias', 'host_address', 'template', 'instance', 'hostgroup'];
        $this->exportExcludedParams = array_merge(
            $this->insertParams,
            [$this->object->getPrimaryKey()],
            ['host_template_model_htm_id']
        );
        $this->action = 'HOST';
        $this->nbOfCompulsoryParams = count($this->insertParams);
        $this->activateField = 'host_activate';
    }

    /**
     * Magic method
     *
     * @param string $name
     * @param array $arg
     * @throws CentreonClapiException
     * @return void
     */
    public function __call($name, $arg)
    {
        // Get the method name
        $name = strtolower($name);
        // Get the action and the object
        if (preg_match('/^(get|set|add|del)([a-zA-Z_]+)/', $name, $matches)) {
            switch ($matches[2]) {
                case 'contact':
                    $class = 'Centreon_Object_Contact';
                    $relclass = 'Centreon_Object_Relation_Contact_Host';
                    break;
                case 'contactgroup':
                    $class = 'Centreon_Object_Contact_Group';
                    $relclass = 'Centreon_Object_Relation_Contact_Group_Host';
                    break;
                case 'hostgroup':
                    $class = 'Centreon_Object_Host_Group';
                    $relclass = 'Centreon_Object_Relation_Host_Group_Host';
                    break;
                case 'template':
                    $class = 'Centreon_Object_Host_Template';
                    $relclass = 'Centreon_Object_Relation_Host_Template_Host';
                    break;
                case 'parent':
                    $class = 'Centreon_Object_Host';
                    $relclass = 'Centreon_Object_Relation_Host_Parent_Host';
                    break;
                case 'child':
                    $class = 'Centreon_Object_Host';
                    $relclass = 'Centreon_Object_Relation_Host_Child_Host';
                    break;
                case 'hostcategory':
                    $class = 'Centreon_Object_Host_Category';
                    $relclass = 'Centreon_Object_Relation_Host_Category_Host';
                    break;
                default:
                    throw new CentreonClapiException(self::UNKNOWN_METHOD);
                    break;
            }
            if (class_exists($relclass) && class_exists($class)) {
                // Test and get the first arguments
                if (! isset($arg[0])) {
                    throw new CentreonClapiException(self::MISSINGPARAMETER);
                }
                $args = explode($this->delim, $arg[0]);
                $hostIds = $this->object->getIdByParameter($this->object->getUniqueLabelField(), [$args[0]]);
                if (! count($hostIds)) {
                    throw new CentreonClapiException(self::OBJECT_NOT_FOUND . ':' . $args[0]);
                }
                $hostId = $hostIds[0];

                $relobj = new $relclass($this->dependencyInjector);
                $obj = new $class($this->dependencyInjector);
                if ($matches[1] == 'get') {
                    $tab = $relobj->getTargetIdFromSourceId($relobj->getFirstKey(), $relobj->getSecondKey(), $hostId);
                    echo 'id' . $this->delim . 'name' . "\n";
                    foreach ($tab as $value) {
                        $tmp = $obj->getParameters($value, [$obj->getUniqueLabelField()]);
                        echo $value . $this->delim . $tmp[$obj->getUniqueLabelField()] . "\n";
                    }
                } else {
                    if (! isset($args[1])) {
                        throw new CentreonClapiException(self::MISSINGPARAMETER);
                    }
                    $relation = $args[1];
                    $relations = explode('|', $relation);
                    $relationTable = [];
                    foreach ($relations as $rel) {
                        if ($matches[2] == 'contact') {
                            $tab = $obj->getIdByParameter('contact_alias', [$rel]);
                        } else {
                            $tab = $obj->getIdByParameter($obj->getUniqueLabelField(), [$rel]);
                        }

                        if (! count($tab)) {
                            throw new CentreonClapiException(self::OBJECT_NOT_FOUND . ':' . $rel);
                        }
                        $relationTable[] = $tab[0];
                    }
                    if ($matches[1] == 'set') {
                        $relobj->delete(null, $hostId);
                    }
                    $existingRelationIds = $relobj->getTargetIdFromSourceId(
                        $relobj->getFirstKey(),
                        $relobj->getSecondKey(),
                        $hostId
                    );
                    foreach ($relationTable as $relationId) {
                        if ($matches[1] == 'del') {
                            $relobj->delete($relationId, $hostId);
                        } elseif ($matches[1] == 'set' || $matches[1] == 'add') {
                            if (! in_array($relationId, $existingRelationIds)) {
                                $relobj->insert($relationId, $hostId);
                            }
                        }
                    }
                    if ($matches[2] == 'hostgroup') {
                        $aclObj = new CentreonACL($this->dependencyInjector);
                        $aclObj->reload(true);
                    }
                    $this->addAuditLog(
                        'c',
                        $hostId,
                        $args[0],
                        [$matches[2] => str_replace('|', ',', $args[1])]
                    );
                }
            } else {
                throw new CentreonClapiException(self::UNKNOWN_METHOD);
            }
        } else {
            throw new CentreonClapiException(self::UNKNOWN_METHOD);
        }
    }

    /**
     * We keep this method for retro compatibility with other objects
     *
     * @param string $name
     * @return int
     */
    public function getHostID($name)
    {
        return $this->getObjectId($name);
    }

    /**
     * We keep this method for retro compatibility with other objects
     *
     * @param int $hostId
     * @return string
     */
    public function getHostName($hostId)
    {
        return $this->getObjectName($hostId);
    }

    /**
     * @param null $parameters
     * @param array $filters
     *
     * @throws Exception
     */
    public function show($parameters = null, $filters = []): void
    {
        $filters = ['host_register' => $this->register];

        $labelField = $this->object->getUniqueLabelField();
        if (isset($parameters)) {
            $filters[$labelField] = '%' . $parameters . '%';
        }
        $params = ['host_id', 'host_name', 'host_alias', 'host_address', 'host_activate'];
        $paramString = str_replace('host_', '', implode($this->delim, $params));
        echo $paramString . "\n";
        $elements = $this->object->getList(
            $params,
            -1,
            0,
            null,
            null,
            $filters,
            'AND'
        );
        foreach ($elements as $tab) {
            echo implode($this->delim, $tab) . "\n";
        }
    }

    /**
     * @param null $parameters
     * @param array $filters
     *
     * @throws Exception
     */
    public function showbyaddress($parameters = null, $filters = []): void
    {
        $filters = ['host_register' => $this->register];

        if (isset($parameters)) {
            $filters['host_address'] = '%' . $parameters . '%';
        }
        $params = ['host_id', 'host_name', 'host_alias', 'host_address', 'host_activate'];
        $paramString = str_replace('host_', '', implode($this->delim, $params));
        echo $paramString . "\n";
        $elements = $this->object->getList(
            $params,
            -1,
            0,
            null,
            null,
            $filters,
            'AND'
        );
        foreach ($elements as $tab) {
            echo implode($this->delim, $tab) . "\n";
        }
    }

    /**
     * @param $parameters
     *
     * @throws CentreonClapiException
     * @throws PDOException
     * @return void
     */
    public function initInsertParameters($parameters): void
    {
        $params = explode($this->delim, $parameters);
        if (count($params) < $this->nbOfCompulsoryParams) {
            throw new CentreonClapiException(self::MISSINGPARAMETER);
        }
        $addParams = [];
        $addParams[$this->object->getUniqueLabelField()] = NewHost::formatName($this->checkIllegalChar($params[self::ORDER_UNIQUENAME]));
        if ($addParams[$this->object->getUniqueLabelField()] === '') {
            throw new CentreonClapiException(self::NAME_IS_EMPTY);
        }
        $addParams['host_alias'] = $params[self::ORDER_ALIAS];
        $addParams['host_address'] = $params[self::ORDER_ADDRESS];
        $templates = explode('|', $params[self::ORDER_TEMPLATE]);
        $this->templateIds = [];
        foreach ($templates as $template) {
            if ($template) {
                $tmp = $this->object->getIdByParameter($this->object->getUniqueLabelField(), $template);
                if (count($tmp)) {
                    $this->templateIds[] = $tmp[0];
                } else {
                    throw new CentreonClapiException(self::OBJECT_NOT_FOUND . ':' . $template);
                }
            }
        }
        $instanceName = $params[self::ORDER_POLLER];
        $instanceObject = new Centreon_Object_Instance($this->dependencyInjector);
        $this->instanceId = null;
        if ($this->action == 'HOST') {
            if ($instanceName) {
                $tmp = $instanceObject->getIdByParameter($instanceObject->getUniqueLabelField(), $instanceName);
                if (! count($tmp)) {
                    $defaultInstanceName = $instanceObject->getDefaultInstance();
                    $tmp = $instanceObject->getIdByParameter(
                        $instanceObject->getUniqueLabelField(),
                        $defaultInstanceName
                    );
                    if (! count($tmp)) {
                        throw new CentreonClapiException(self::OBJECT_NOT_FOUND . ' :' . $instanceName);
                    }
                }
                $this->instanceId = $tmp[0];
            } else {
                throw new CentreonClapiException(self::MISSING_INSTANCE);
            }
        }
        $hostgroups = explode('|', $params[self::ORDER_HOSTGROUP]);
        $this->hostgroupIds = [];
        $hostgroupObject = new Centreon_Object_Host_Group($this->dependencyInjector);
        foreach ($hostgroups as $hostgroup) {
            if ($hostgroup) {
                $tmp = $hostgroupObject->getIdByParameter($hostgroupObject->getUniqueLabelField(), $hostgroup);
                if (count($tmp)) {
                    $this->hostgroupIds[] = $tmp[0];
                } else {
                    throw new CentreonClapiException(self::OBJECT_NOT_FOUND . ':' . $hostgroup);
                }
            }
        }
        $this->params = array_merge($this->params, $addParams);
        $this->checkParameters();
    }

    /**
     * @param $hostId
     *
     * @return void
     */
    public function insertRelations($hostId): void
    {
        $i = 1;
        $templateRelationObject = new Centreon_Object_Relation_Host_Template_Host($this->dependencyInjector);
        foreach ($this->templateIds as $templateId) {
            $templateRelationObject->insert($templateId, $hostId);
            $i++;
        }
        $hostgroupRelationObject = new Centreon_Object_Relation_Host_Group_Host($this->dependencyInjector);
        foreach ($this->hostgroupIds as $hostgroupId) {
            $hostgroupRelationObject->insert($hostgroupId, $hostId);
        }
        if (! is_null($this->instanceId)) {
            $instanceRelationObject = new Centreon_Object_Relation_Instance_Host($this->dependencyInjector);
            $instanceRelationObject->insert($this->instanceId, $hostId);
        }
        $extended = new Centreon_Object_Host_Extended($this->dependencyInjector);
        $extended->insert([$extended->getUniqueLabelField() => $hostId]);
    }

    /**
     * @param $parameters
     *
     * @throws Exception
     * @return void
     */
    public function add($parameters): void
    {
        parent::add($parameters);

        $centreonConfig = new CentreonConfigurationChange($this->dependencyInjector['configuration_db']);
        $hostId = $this->getObjectId($this->params[$this->object->getUniqueLabelField()]);
        $centreonConfig->signalConfigurationChange(CentreonConfigurationChange::RESOURCE_TYPE_HOST, $hostId);
    }

    /**
     * Del Action
     * Must delete services as well
     *
     * @param string $objectName
     *
     * @throws CentreonClapiException
     * @throws PDOException
     * @return void
     */
    public function del($objectName): void
    {
        $centreonConfig = new CentreonConfigurationChange($this->dependencyInjector['configuration_db']);
        $hostId = $this->getObjectId($objectName);
        $previousPollerIds = $centreonConfig->findPollersForConfigChangeFlagFromHostIds([$hostId]);

        $parentDependency = new Centreon_Object_DependencyHostParent($this->dependencyInjector);
        $parentDependency->removeRelationLastHostDependency($hostId);

        parent::del($objectName);
        $this->db->query(
            "DELETE FROM service WHERE service_register = '1' "
            . 'AND service_id NOT IN (SELECT service_service_id FROM host_service_relation)'
        );

        $centreonConfig->signalConfigurationChange(
            CentreonConfigurationChange::RESOURCE_TYPE_HOST,
            $hostId,
            $previousPollerIds
        );
    }

    /**
     * @param array $parameters
     *
     * @throws CentreonClapiException
     * @throws PDOException
     * @return void
     */
    public function setParam($parameters = []): void
    {
        $params = method_exists($this, 'initUpdateParameters') ? $this->initUpdateParameters($parameters) : $parameters;
        if (! empty($params)) {
            $hostId = $params['objectId'];

            $centreonConfig = new CentreonConfigurationChange($this->dependencyInjector['configuration_db']);
            $previousPollerIds = $centreonConfig->findPollersForConfigChangeFlagFromHostIds([$hostId]);

            parent::setparam($parameters);

            $centreonConfig->signalConfigurationChange(
                CentreonConfigurationChange::RESOURCE_TYPE_HOST,
                $hostId,
                $previousPollerIds
            );
        }
    }

    /**
     * @param $objectName
     *
     * @throws CentreonClapiException
     * @return void
     */
    public function enable($objectName): void
    {
        parent::enable($objectName);

        $centreonConfig = new CentreonConfigurationChange($this->dependencyInjector['configuration_db']);
        $hostId = $this->getObjectId($objectName);
        $centreonConfig->signalConfigurationChange(CentreonConfigurationChange::RESOURCE_TYPE_HOST, $hostId);
    }

    /**
     * @param $objectName
     *
     * @throws CentreonClapiException
     * @return void
     */
    public function disable($objectName): void
    {
        parent::disable($objectName);

        $centreonConfig = new CentreonConfigurationChange($this->dependencyInjector['configuration_db']);
        $hostId = $this->getObjectId($objectName);
        $centreonConfig->signalConfigurationChange(CentreonConfigurationChange::RESOURCE_TYPE_HOST, $hostId, [], false);
    }

    /**
     * List instance (poller) for host
     *
     * @param string $parameters
     * @throws CentreonClapiException
     */
    public function showinstance($parameters): void
    {
        $params = explode($this->delim, $parameters);
        if ($parameters == '') {
            throw new CentreonClapiException(self::MISSINGPARAMETER);
        }
        if (($hostId = $this->getObjectId($params[self::ORDER_UNIQUENAME])) != 0) {
            $relObj = new Centreon_Object_Relation_Instance_Host($this->dependencyInjector);
            $fields = ['id', 'name'];
            $elements = $relObj->getMergedParameters(
                $fields,
                [],
                -1,
                0,
                'host_name',
                'ASC',
                ['host_id' => $hostId],
                'AND'
            );

            echo 'id' . $this->delim . 'name' . "\n";
            foreach ($elements as $elem) {
                echo $elem['id'] . $this->delim . $elem['name'] . "\n";
            }
        } else {
            throw new CentreonClapiException(self::OBJECT_NOT_FOUND . ':' . $params[self::ORDER_UNIQUENAME]);
        }
    }

    /**
     * Tie host to instance (poller)
     *
     * @param string $parameters
     * @throws CentreonClapiException
     */
    public function setinstance($parameters): void
    {
        $params = explode($this->delim, $parameters);
        if (count($params) < 2) {
            throw new CentreonClapiException(self::MISSINGPARAMETER);
        }
        $hostId = $this->getObjectId($params[self::ORDER_UNIQUENAME]);
        $instanceObj = new Centreon_Object_Instance($this->dependencyInjector);
        $tmp = $instanceObj->getIdByParameter($instanceObj->getUniqueLabelField(), $params[1]);
        if (! count($tmp)) {
            throw new CentreonClapiException(self::OBJECT_NOT_FOUND . ':' . $params[1]);
        }
        $instanceId = $tmp[0];
        $relationObj = new Centreon_Object_Relation_Instance_Host($this->dependencyInjector);
        $relationObj->delete(null, $hostId);
        $relationObj->insert($instanceId, $hostId);
    }

    /**
     * Get a parameter
     *
     * @param null $parameters
     *
     * @throws CentreonClapiException
     * @throws PDOException
     */
    public function getparam($parameters = null): void
    {
        $params = explode($this->delim, $parameters);
        if (count($params) < 2) {
            throw new CentreonClapiException(self::MISSINGPARAMETER);
        }
        $authorizeParam = ['2d_coords', '3d_coords', 'action_url', 'activate', 'active_checks_enabled', 'acknowledgement_timeout', 'address', 'alias', 'check_command', 'check_command_arguments', 'check_interval', 'check_freshness', 'check_period', 'comment', 'contact_additive_inheritance', 'cg_additive_inheritance', 'event_handler', 'event_handler_arguments', 'event_handler_enabled', 'first_notification_delay', 'flap_detection_enabled', 'flap_detection_options', 'freshness_threshold', 'geo_coords', 'host_high_flap_threshold', 'host_low_flap_threshold', 'host_notification_options', 'high_flap_threshold', 'icon_image', 'icon_image_alt', 'low_flap_threshold', 'max_check_attempts', 'name', 'notes', 'notes_url', 'notifications_enabled', 'notification_interval', 'notification_options', 'notification_period', 'recovery_notification_delay', 'obsess_over_host', 'passive_checks_enabled', 'process_perf_data', 'retain_nonstatus_information', 'retain_status_information', 'retry_check_interval', 'snmp_community', 'snmp_version', 'stalking_options', 'statusmap_image', 'timezone'];
        $unknownParam = [];

        if (($objectId = $this->getObjectId($params[self::ORDER_UNIQUENAME])) != 0) {
            $listParam = explode('|', $params[1]);
            $exportedFields = [];
            $resultString = '';
            foreach ($listParam as $paramSearch) {
                $paramString = ! isset($paramString) || ! $paramString ? $paramSearch : $paramString . $this->delim . $paramSearch;
                $field = $paramSearch;
                if (! in_array($field, $authorizeParam)) {
                    $unknownParam[] = $field;
                } else {
                    $extended = false;
                    switch ($paramSearch) {
                        case 'check_command':
                            $field = 'command_command_id';
                            break;
                        case 'check_command_arguments':
                            $field = 'command_command_id_arg1';
                            break;
                        case 'event_handler':
                            $field = 'command_command_id2';
                            break;
                        case 'event_handler_arguments':
                            $field = 'command_command_id_arg2';
                            break;
                        case 'check_period':
                            $field = 'timeperiod_tp_id';
                            break;
                        case 'notification_period':
                            $field = 'timeperiod_tp_id2';
                            break;
                        case 'contact_additive_inheritance':
                        case 'cg_additive_inheritance':
                        case 'flap_detection_options':
                        case 'geo_coords':
                            break;
                        case 'notes':
                        case 'notes_url':
                        case 'action_url':
                        case 'icon_image':
                        case 'icon_image_alt':
                        case 'vrml_image':
                        case 'statusmap_image':
                        case '2d_coords':
                        case '3d_coords':
                            $extended = true;
                            break;
                        case self::HOST_LOCATION:
                            $field = 'host_location';
                            break;
                        default:
                            if (! preg_match('/^host_/', $paramSearch)) {
                                $field = 'host_' . $paramSearch;
                            }
                            break;
                    }

                    if (! $extended) {
                        $ret = $this->object->getParameters($objectId, $field);
                        $ret = $ret[$field];
                    } else {
                        $field = 'ehi_' . $field;
                        $extended = new Centreon_Object_Host_Extended($this->dependencyInjector);
                        $ret = $extended->getParameters($objectId, $field);
                        $ret = $ret[$field];
                    }

                    switch ($paramSearch) {
                        case 'check_command':
                        case 'event_handler':
                            $commandObject = new CentreonCommand($this->dependencyInjector);
                            $field = $commandObject->object->getUniqueLabelField();
                            $ret = $commandObject->object->getParameters($ret, $field);
                            $ret = $ret[$field];
                            break;
                        case 'check_period':
                        case 'notification_period':
                            $tpObj = new CentreonTimePeriod($this->dependencyInjector);
                            $field = $tpObj->object->getUniqueLabelField();
                            $ret = $tpObj->object->getParameters($ret, $field);
                            $ret = $ret[$field];
                            break;
                        case self::HOST_LOCATION:
                            $field = $this->timezoneObject->getUniqueLabelField();
                            $ret = $this->timezoneObject->getParameters($ret, $field);
                            $ret = $ret[$field];
                            break;
                    }
                    if (! isset($exportedFields[$paramSearch])) {
                        $resultString .= $this->csvEscape($ret) . $this->delim;
                        $exportedFields[$paramSearch] = 1;
                    }
                }
            }
        } else {
            throw new CentreonClapiException(self::OBJECT_NOT_FOUND . ':' . $params[self::ORDER_UNIQUENAME]);
        }

        if ($unknownParam !== []) {
            throw new CentreonClapiException(self::OBJECT_NOT_FOUND . ':' . implode('|', $unknownParam));
        }
        echo implode(';', array_unique(explode(';', $paramString))) . "\n";
        echo substr($resultString, 0, -1) . "\n";
    }

    /**
     * @param null $parameters
     *
     * @throws CentreonClapiException
     * @throws PDOException
     * @return array
     */
    public function initUpdateParameters($parameters = null)
    {
        $params = explode($this->delim, $parameters);
        if (count($params) < self::NB_UPDATE_PARAMS) {
            throw new CentreonClapiException(self::MISSINGPARAMETER);
        }

        $objectId = $this->getObjectId($params[self::ORDER_UNIQUENAME]);
        if ($objectId != 0) {
            $extended = false;
            $commandObject = new CentreonCommand($this->dependencyInjector);
            switch ($params[1]) {
                case 'check_command':
                    $params[1] = 'command_command_id';
                    $params[2] = $commandObject->getId($params[2]);
                    break;
                case 'check_command_arguments':
                    $params[1] = 'command_command_id_arg1';
                    break;
                case 'event_handler':
                    $params[1] = 'command_command_id2';
                    $params[2] = $commandObject->getId($params[2]);
                    break;
                case 'event_handler_arguments':
                    $params[1] = 'command_command_id_arg2';
                    break;
                case 'check_period':
                    $params[1] = 'timeperiod_tp_id';
                    $tpObj = new CentreonTimePeriod($this->dependencyInjector);
                    $params[2] = $tpObj->getTimeperiodId($params[2]);
                    break;
                case 'notification_period':
                    $params[1] = 'timeperiod_tp_id2';
                    $tpObj = new CentreonTimePeriod($this->dependencyInjector);
                    $params[2] = $tpObj->getTimeperiodId($params[2]);
                    break;
                case 'geo_coords':
                    if (! CentreonUtils::validateGeoCoords($params[2])) {
                        throw new CentreonClapiException(self::INVALID_GEO_COORDS);
                    }
                    break;
                case 'contact_additive_inheritance':
                case 'cg_additive_inheritance':
                case 'flap_detection_options':
                    break;
                case 'notes':
                case 'notes_url':
                case 'action_url':
                case 'icon_image':
                case 'icon_image_alt':
                case 'statusmap_image':
                case '2d_coords':
                case '3d_coords':
                    $extended = true;
                    break;
                case 'host_notification_options':
                    $aNotifs = explode(',', $params[2]);
                    foreach ($aNotifs as $notif) {
                        if (! array_key_exists($notif, self::$aAuthorizedNotificationsOptions)) {
                            throw new CentreonClapiException(self::UNKNOWN_NOTIFICATION_OPTIONS);
                        }
                    }
                    break;
                case self::HOST_LOCATION:
                    $iIdTimezone = $this->timezoneObject->getIdByParameter(
                        $this->timezoneObject->getUniqueLabelField(),
                        $params[2]
                    );
                    if (count($iIdTimezone)) {
                        $iIdTimezone = $iIdTimezone[0];
                    } else {
                        throw new CentreonClapiException(self::UNKNOWN_TIMEZONE);
                    }
                    $params[1] = 'host_location';
                    $params[2] = $iIdTimezone;
                    break;
                default:
                    if (! preg_match('/^host_/', $params[1])) {
                        $params[1] = 'host_' . $params[1];
                    }
                    break;
            }

            if ($extended == false) {
                $updateParams = [$params[1] => $params[2]];
                $updateParams['objectId'] = $objectId;

                return $updateParams;
            }
            $params[1] = 'ehi_' . $params[1];
            if ($params[1] == 'ehi_icon_image' || $params[1] == 'ehi_statusmap_image') {
                if ($params[2]) {
                    $id = CentreonUtils::getImageId($params[2], $this->db);
                    if (is_null($id)) {
                        throw new CentreonClapiException(self::OBJECT_NOT_FOUND . ':' . $params[2]);
                    }
                    $params[2] = $id;
                } else {
                    $params[2] = null;
                }
            }

            $extended = new Centreon_Object_Host_Extended($this->dependencyInjector);
            $extended->update($objectId, [$params[1] => $params[2]]);
            $centreonConfig = new CentreonConfigurationChange($this->dependencyInjector['configuration_db']);
            $previousPollerIds = $centreonConfig->findPollersForConfigChangeFlagFromHostIds([$objectId]);
            $centreonConfig->signalConfigurationChange(
                CentreonConfigurationChange::RESOURCE_TYPE_HOST,
                $objectId,
                $previousPollerIds
            );

            return [];
        }

        throw new CentreonClapiException(self::OBJECT_NOT_FOUND . ':' . $params[self::ORDER_UNIQUENAME]);
    }

    /**
     * Set severity
     *
     * @param $parameters
     *
     * @throws CentreonClapiException
     * @throws PDOException
     */
    public function setseverity($parameters): void
    {
        $params = explode($this->delim, $parameters);
        if (count($params) < 2) {
            throw new CentreonClapiException(self::MISSINGPARAMETER);
        }
        if (($hostId = $this->getObjectId($params[self::ORDER_UNIQUENAME])) == 0) {
            throw new CentreonClapiException(self::OBJECT_NOT_FOUND . ':' . $params[self::ORDER_UNIQUENAME]);
        }
        $severityObj = new Centreon_Object_Host_Category($this->dependencyInjector);
        $severity = $severityObj->getIdByParameter(
            $severityObj->getUniqueLabelField(),
            $params[1]
        );
        if (! isset($severity[0])) {
            throw new CentreonClapiException(self::OBJECT_NOT_FOUND . ':' . $params[1]);
        }
        $severityId = $severity[0];
        $severity = $severityObj->getParameters(
            $severityId,
            ['level']
        );
        if ($severity['level']) {
            // can't delete with generic method
            $this->db->query(
                'DELETE FROM hostcategories_relation
                 WHERE host_host_id = ?
                 AND hostcategories_hc_id IN (SELECT hc_id FROM hostcategories WHERE level > 0)',
                $hostId
            );
            $rel = new Centreon_Object_Relation_Host_Category_Host($this->dependencyInjector);
            $rel->insert($severityId, $hostId);
        } else {
            throw new CentreonClapiException(self::OBJECT_NOT_FOUND . ':' . $params[1]);
        }
    }

    /**
     * @param $parameters
     *
     * @throws CentreonClapiException
     * @throws PDOException
     */
    public function unsetseverity($parameters): void
    {
        $params = explode($this->delim, $parameters);
        if (count($params) < 1) {
            throw new CentreonClapiException(self::MISSINGPARAMETER);
        }
        if (($hostId = $this->getObjectId($params[self::ORDER_UNIQUENAME])) == 0) {
            throw new CentreonClapiException(self::OBJECT_NOT_FOUND . ':' . $params[self::ORDER_UNIQUENAME]);
        }
        // can't delete with generic method
        $this->db->query(
            'DELETE FROM hostcategories_relation
             WHERE host_host_id = ?
             AND hostcategories_hc_id IN (SELECT hc_id FROM hostcategories WHERE level > 0)',
            $hostId
        );
    }

    /**
     * Get macro list of a host
     *
     * @param string $hostName
     *
     * @throws CentreonClapiException
     * @throws PDOException
     * @return void
     */
    public function getmacro($hostName): void
    {
        if (($hostId = $this->getObjectId($hostName)) == 0) {
            throw new CentreonClapiException(self::OBJECT_NOT_FOUND . ':' . $hostName);
        }

        $aTemplates = $this->getTemplateChain($hostId, [], -1, true, 'host_name,host_id,command_command_id');
        if (! isset($cmdId)) {
            $cmdId = '';
        }
        $macroList = $this->getMacros($hostId, false, $aTemplates, $cmdId);

        echo "macro name;macro value;is_password;description;source\n";
        foreach ($macroList as $macro) {
            $source = 'direct';
            if ($macro['source'] == 'fromTpl') {
                $source = $macro['macroTpl'];
            }
            echo $this->csvEscape($macro['host_macro_name']) . $this->delim
                . $this->csvEscape($macro['host_macro_value']) . $this->delim
                . $macro['is_password'] . $this->delim
                . $this->csvEscape($macro['description']) . $this->delim
                . $source . "\n";
        }
    }

    /**
     * Inserts/updates custom macro
     *
     * @param string $parameters
     * @throws CentreonClapiException
     * @return void
     */
    public function setmacro($parameters): void
    {
        $params = explode($this->delim, $parameters);
        if (count($params) == 3) {
            $params[3] = 0;
        }

        if (($hostId = $this->getObjectId($params[self::ORDER_UNIQUENAME])) == 0) {
            throw new CentreonClapiException(self::OBJECT_NOT_FOUND . ':' . $params[self::ORDER_UNIQUENAME]);
        }
        if (count($params) < self::NB_UPDATE_PARAMS) {
            throw new CentreonClapiException(self::MISSINGPARAMETER);
        }
        $macroObj = new Centreon_Object_Host_Macro_Custom($this->dependencyInjector);
        $macroList = $macroObj->getList(
            $macroObj->getPrimaryKey(),
            -1,
            0,
            null,
            null,
            ['host_host_id' => $hostId, 'host_macro_name' => $this->wrapMacro($params[1])],
            'AND'
        );

        $maxOrder = $macroObj->getList(
            'max(macro_order)',
            -1,
            0,
            null,
            null,
            ['host_host_id' => $hostId]
        );
        $macroOrder = empty($maxOrder) ? 0 : $maxOrder[0]['max(macro_order)'] + 1;

        // disable the check if the macro added is already in host template with same value
        // if($this->hasMacroFromHostChanged($hostId,$params[1],$params[2],$cmdId = false)){
        $description = (string) $params[4];
        if (
            strlen($description) === 0
            || (
                strlen($description) === 2
                && str_starts_with($description, "'")
                && substr($description, 1, 1) === "'"
            )
        ) {
            $description = null;
        }
        if (count($macroList)) {
            $macroObj->update(
                $macroList[0][$macroObj->getPrimaryKey()],
                ['host_macro_value' => $params[2], 'is_password' => (strlen($params[3]) === 0) ? 0 : (int) $params[3], 'description' => $description]
            );
        } else {
            $macroObj->insert(
                ['host_host_id' => $hostId, 'host_macro_name' => $this->wrapMacro($params[1]), 'host_macro_value' => $params[2], 'is_password' => (strlen($params[3]) === 0) ? 0 : (int) $params[3], 'description' => $description, 'macro_order' => $macroOrder]
            );
        }

        $this->addAuditLog(
            'c',
            $hostId,
            $params[self::ORDER_UNIQUENAME],
            [$params[1] => $params[2]]
        );
        // }
    }

    /**
     * Delete custom macro
     *
     * @param string $parameters
     * @throws CentreonClapiException
     * @return void
     */
    public function delmacro($parameters): void
    {
        $params = explode($this->delim, $parameters);
        if (($hostId = $this->getObjectId($params[self::ORDER_UNIQUENAME])) == 0) {
            throw new CentreonClapiException(self::OBJECT_NOT_FOUND . ':' . $params[self::ORDER_UNIQUENAME]);
        }
        if (count($params) < 2) {
            throw new CentreonClapiException(self::MISSINGPARAMETER);
        }
        $macroObj = new Centreon_Object_Host_Macro_Custom($this->dependencyInjector);
        $macroList = $macroObj->getList(
            $macroObj->getPrimaryKey(),
            -1,
            0,
            null,
            null,
            ['host_host_id' => $hostId, 'host_macro_name' => $this->wrapMacro($params[1])],
            'AND'
        );
        if (count($macroList)) {
            $macroObj->delete($macroList[0][$macroObj->getPrimaryKey()]);
        }
        $this->addAuditLog(
            'c',
            $hostId,
            $params[self::ORDER_UNIQUENAME],
            [$params[1] => '']
        );
    }

    /**
     * Apply template in order to deploy services
     *
     * @param string $hostName
     *
     * @throws CentreonClapiException
     * @throws PDOException
     * @return void
     */
    public function applytpl($hostName): void
    {
        if (! $this->register) {
            throw new CentreonClapiException(self::UNKNOWN_METHOD);
        }
        if (($hostId = $this->getObjectId($hostName)) == 0) {
            throw new CentreonClapiException(self::OBJECT_NOT_FOUND . ':' . $hostName);
        }
        $this->deployServices($hostId);

        $aclObj = new CentreonACL($this->dependencyInjector);
        $aclObj->reload(true);
    }

    /**
     * Export
     *
     * @param null $filterName
     *
     * @throws Exception
     * @return void
     */
    public function export($filterName = null)
    {
        if (! $this->canBeExported($filterName)) {
            return 0;
        }

        $labelField = $this->object->getUniqueLabelField();
        $filters = ['host_register' => $this->register];
        if (! is_null($filterName)) {
            $filters[$labelField] = $filterName;
        }
        $elements = $this->object->getList(
            '*',
            -1,
            0,
            $labelField,
            'ASC',
            $filters,
            'AND'
        );
        $extendedObj = new Centreon_Object_Host_Extended($this->dependencyInjector);
        $macroObj = new Centreon_Object_Host_Macro_Custom($this->dependencyInjector);
        $instanceRel = new Centreon_Object_Relation_Instance_Host($this->dependencyInjector);

        if ($this->register) {
            $instElements = $instanceRel->getMergedParameters(
                ['name'],
                ['host_name'],
                -1,
                0,
                null,
                null,
                $filters,
                'AND'
            );
        }

        // Init parentship
        $parentShip = [];
        if ($this->register == 1) {
            $parentShip = $this->getHostListByParent($elements);
        }

        foreach ($elements as $element) {
            $addStr = $this->action . $this->delim . 'ADD';
            foreach ($this->insertParams as $param) {
                $addStr .= $this->delim;
                if ($param == 'instance') {
                    if ($this->register) {
                        foreach ($instElements as $instElem) {
                            if ($element['host_name'] == $instElem['host_name']) {
                                $addStr .= $instElem['name'];
                            }
                        }
                    }
                }
                if (isset($element[$param]) && $param != 'hostgroup' && $param != 'template') {
                    $addStr .= $element[$param];
                }
            }
            $addStr .= "\n";
            echo $addStr;
            foreach ($element as $parameter => $value) {
                if (! in_array($parameter, $this->exportExcludedParams) && ! is_null($value) && $value != '') {
                    $action_tmp = null;
                    if ($parameter == 'timeperiod_tp_id' || $parameter == 'timeperiod_tp_id2') {
                        $action_tmp = 'TP';
                        $tmpObj = CentreonTimePeriod::getInstance();
                    } elseif ($parameter == 'command_command_id' || $parameter == 'command_command_id2') {
                        $action_tmp = 'CMD';
                        $tmpObj = CentreonCommand::getInstance();
                    } elseif ($parameter == 'host_location') {
                        $tmpObj = CentreonTimezone::getInstance();
                    }
                    if (isset($tmpObj)) {
                        $tmpLabelField = $tmpObj->getObject()->getUniqueLabelField();
                        $tmp = $tmpObj->getObject()->getParameters($value, $tmpLabelField);
                        if (isset($tmp, $tmp[$tmpLabelField])) {
                            $value = $tmp[$tmpLabelField];
                            if (! is_null($action_tmp)) {
                                $tmpObj::getInstance()->export($value);
                            }
                        }
                        unset($tmpObj);
                    }
                    $value = CentreonUtils::convertLineBreak($value);
                    echo $this->action . $this->delim
                        . 'setparam' . $this->delim
                        . $element[$this->object->getUniqueLabelField()] . $this->delim
                        . $this->getClapiActionName($parameter) . $this->delim
                        . $value . "\n";
                }
            }

            // Set parentship
            if (isset($parentShip[$element[$this->object->getPrimaryKey()]])) {
                foreach ($parentShip[$element[$this->object->getPrimaryKey()]] as $parentId) {
                    echo $this->action . $this->delim
                    . 'addparent' . $this->delim
                    . $element[$this->object->getUniqueLabelField()] . $this->delim
                    . (
                        (
                            isset($elements[$parentId], $elements[$parentId][$this->object->getUniqueLabelField()])
                        )
                            ? $elements[$parentId][$this->object->getUniqueLabelField()]
                            : ''
                    )
                    . "\n";
                }
            }

            $params = $extendedObj->getParameters(
                $element[$this->object->getPrimaryKey()],
                ['ehi_notes', 'ehi_notes_url', 'ehi_action_url', 'ehi_icon_image', 'ehi_icon_image_alt', 'ehi_vrml_image', 'ehi_statusmap_image', 'ehi_2d_coords', 'ehi_3d_coords']
            );
            if (isset($params) && is_array($params)) {
                foreach ($params as $k => $v) {
                    if (! is_null($v) && $v != '') {
                        $v = CentreonUtils::convertLineBreak($v);
                        echo $this->action . $this->delim
                            . 'setparam' . $this->delim
                            . $element[$this->object->getUniqueLabelField()] . $this->delim
                            . $this->getClapiActionName($k) . $this->delim
                            . $v . "\n";
                    }
                }
            }
            $macros = $macroObj->getList(
                '*',
                -1,
                0,
                null,
                null,
                ['host_host_id' => $element[$this->object->getPrimaryKey()]],
                'AND'
            );
            foreach ($macros as $macro) {
                $description = $macro['description'];
                if (
                    strlen($description) > 0
                    && ! str_starts_with($description, "'")
                    && ! str_ends_with($description, "'")
                ) {
                    $description = "'" . $description . "'";
                }

                echo $this->action . $this->delim
                    . 'setmacro' . $this->delim
                    . $element[$this->object->getUniqueLabelField()] . $this->delim
                    . $this->stripMacro($macro['host_macro_name']) . $this->delim
                    . $macro['host_macro_value'] . $this->delim
                    . ((strlen($macro['is_password']) === 0) ? 0 : (int) $macro['is_password']) . $this->delim
                    . $description . "\n";
            }
        }
        $cgRel = new Centreon_Object_Relation_Contact_Group_Host($this->dependencyInjector);
        $filters_cgRel = ['host_register' => $this->register];
        if (! is_null($filterName)) {
            $filters_cgRel['host_name'] = $filterName;
        }
        $elements = $cgRel->getMergedParameters(
            ['cg_name', 'cg_id'],
            [$this->object->getUniqueLabelField()],
            -1,
            0,
            null,
            null,
            $filters_cgRel,
            'AND'
        );
        foreach ($elements as $element) {
            CentreonContactGroup::getInstance()->export($element['cg_name']);
            echo $this->action . $this->delim
                . 'addcontactgroup' . $this->delim
                . $element[$this->object->getUniqueLabelField()] . $this->delim
                . $element['cg_name'] . "\n";
        }
        $contactRel = new Centreon_Object_Relation_Contact_Host($this->dependencyInjector);
        $filters_contactRel = ['host_register' => $this->register];
        if (! is_null($filterName)) {
            $filters_contactRel['host_name'] = $filterName;
        }
        $elements = $contactRel->getMergedParameters(
            ['contact_alias', 'contact_id'],
            [$this->object->getUniqueLabelField()],
            -1,
            0,
            null,
            null,
            $filters_contactRel,
            'AND'
        );
        foreach ($elements as $element) {
            CentreonContact::getInstance()->export($element['contact_alias']);
            echo $this->action . $this->delim
                . 'addcontact' . $this->delim
                . $element[$this->object->getUniqueLabelField()] . $this->delim
                . $element['contact_alias'] . "\n";
        }
        $htplRel = new Centreon_Object_Relation_Host_Template_Host($this->dependencyInjector);
        $filters_htplRel = ['h.host_register' => $this->register];
        if (! is_null($filterName)) {
            $filters_htplRel['h.host_name'] = $filterName;
        }
        $elements = $htplRel->getMergedParameters(
            ['host_name as host'],
            ['host_name as template', 'host_id as tpl_id'],
            -1,
            0,
            'host,`order`',
            'ASC',
            $filters_htplRel,
            'AND'
        );
        foreach ($elements as $element) {
            CentreonHostTemplate::getInstance()->export($element['template']);
            echo $this->action . $this->delim
                . 'addtemplate' . $this->delim
                . $element['host'] . $this->delim
                . $element['template'] . "\n";
        }

        // Filter only
        if (! is_null($filterName)) {
            // service templates linked
            $hostRel = new Centreon_Object_Relation_Host_Service($this->dependencyInjector);
            $helements = $hostRel->getMergedParameters(
                ['host_name'],
                ['service_description', 'service_id'],
                -1,
                0,
                null,
                null,
                ['service_register' => 0, 'host_name' => $filterName],
                'AND'
            );
            foreach ($helements as $helement) {
                CentreonServiceTemplate::getInstance()->export($helement['service_description']);
            }

            // service linked
            $hostRel = new Centreon_Object_Relation_Host_Service($this->dependencyInjector);
            $helements = $hostRel->getMergedParameters(
                ['host_name'],
                ['service_description', 'service_id'],
                -1,
                0,
                null,
                null,
                ['service_register' => 1, 'host_name' => $filterName],
                'AND'
            );
            foreach ($helements as $helement) {
                CentreonService::getInstance()->export($filterName . ';' . $helement['service_description']);
            }

            // service hg linked and hostgroups
            $hostRel = new Centreon_Object_Relation_Host_Group_Host($this->dependencyInjector);
            $helements = $hostRel->getMergedParameters(
                ['hg_name', 'hg_id'],
                ['*'],
                -1,
                0,
                null,
                null,
                ['host_name' => $filterName],
                'AND'
            );
            foreach ($helements as $helement) {
                CentreonHostGroup::getInstance()->export($helement['hg_name']);
                CentreonHostGroupService::getInstance()->export($helement['hg_name']);
            }
        }
    }

    /**
     * @param $host_id
     * @param $macroInput
     * @param $macroValue
     * @param bool $cmdId
     *
     * @throws PDOException
     * @return bool
     */
    public function hasMacroFromHostChanged($host_id, &$macroInput, &$macroValue, $cmdId = false)
    {
        $aTemplates = $this->getTemplateChain($host_id, [], -1, true, 'host_name,host_id,command_command_id');
        if (! isset($cmdId)) {
            $cmdId = '';
        }
        $aMacros = $this->getMacros($host_id, false, $aTemplates, $cmdId);
        foreach ($aMacros as $macro) {
            if ($macroInput == $macro['host_macro_name'] && $macroValue == $macro['host_macro_value']) {
                return false;
            }
        }

        return true;
    }

    /**
     * Get template chain (id, text)
     *
     * @param int $hostId The host or host template Id
     * @param array $alreadyProcessed The host templates already processed
     * @param int $depth The depth to search
     * @param bool $allFields
     * @param array $fields
     *
     * @throws PDOException
     * @return array
     */
    public function getTemplateChain(
        $hostId,
        $alreadyProcessed = [],
        $depth = -1,
        $allFields = false,
        $fields = [],
    ) {
        $templates = [];

        if (($depth == -1) || ($depth > 0)) {
            if ($depth > 0) {
                $depth--;
            }
            if (in_array($hostId, $alreadyProcessed)) {
                return $templates;
            }
            $alreadyProcessed[] = $hostId;

            if (empty($fields)) {
                $fields = ! $allFields ? 'h.host_id, h.host_name' : ' * ';
            }

            $sql = 'SELECT ' . $fields . ' '
                . ' FROM host h, host_template_relation htr'
                . ' WHERE h.host_id = htr.host_tpl_id'
                . " AND htr.host_host_id = '" . $hostId . "'"
                . " AND host_activate = '1'"
                . " AND host_register = '0'"
                . ' ORDER BY `order` ASC';

            $DBRESULT = $this->db->query($sql);

            while ($row = $DBRESULT->fetch()) {
                if (! $allFields) {
                    $templates[] = ['id' => $row['host_id'], 'host_id' => $row['host_id'], 'host_name' => $row['host_name']];
                } else {
                    $templates[] = $row;
                }

                $templates = array_merge(
                    $templates,
                    $this->getTemplateChain(
                        $row['host_id'],
                        $alreadyProcessed,
                        $depth,
                        $allFields
                    )
                );
            }

            return $templates;
        }

        return $templates;
    }

    /**
     * This method get the macro attached to the host
     *
     * @param int $iHostId
     * @param int $bIsTemplate
     * @param array $aListTemplate
     * @param int $iIdCommande
     *
     * @throws PDOException
     * @return array
     */
    public function getMacros($iHostId, $bIsTemplate, $aListTemplate, $iIdCommande)
    {
        $aMacroInCommande = [];

        // Get macro attached to the host
        $macroArray = $this->getCustomMacroInDb($iHostId);

        // Get macro attached to the template
        $aMacroTemplate = [];
        foreach ($aListTemplate as $template) {
            if (! empty($template['host_id'])) {
                $aMacroTemplate[] = $this->getCustomMacroInDb($template['host_id'], $template);
            }
        }

        if (empty($iIdCommande)) {
            foreach ($aListTemplate as $template) {
                if (! empty($template['command_command_id'])) {
                    $iIdCommande = $template['command_command_id'];
                    break;
                }
            }
        }

        // Get macro attached to the command
        if (! empty($iIdCommande)) {
            $oCommand = new CentreonCommand($this->dependencyInjector);
            $aMacroInCommande[] = $oCommand->getMacroByIdAndType($iIdCommande, 'host');
        }

        // filter a macro
        $aTempMacro = [];

        if (count($macroArray) > 0) {
            foreach ($macroArray as $directMacro) {
                $directMacro['macroOldValue'] = $directMacro['host_macro_value'];
                $directMacro['macroFrom'] = 'direct';
                $directMacro['source'] = 'direct';
                $aTempMacro[] = $directMacro;
            }
        }

        if ($aMacroTemplate !== []) {
            foreach ($aMacroTemplate as $key => $macr) {
                foreach ($macr as $mm) {
                    $mm['macroOldValue'] = $mm['host_macro_value'];
                    $mm['macroFrom'] = 'fromTpl';
                    $mm['source'] = 'fromTpl';
                    $aTempMacro[] = $mm;
                }
            }
        }

        if ($aMacroInCommande !== []) {
            $macroCommande = current($aMacroInCommande);
            $counter = count($macroCommande);
            for ($i = 0; $i < $counter; $i++) {
                $macroCommande[$i]['macroOldValue'] = $macroCommande[$i]['host_macro_value'];
                $macroCommande[$i]['macroFrom'] = 'fromCommand';
                $macroCommande[$i]['source'] = 'fromCommand';
                $aTempMacro[] = $macroCommande[$i];
            }
        }

        return $this->macro_unique($aTempMacro);
    }

    /**
     * @param null $hostId
     * @param null $template
     *
     * @throws PDOException
     * @return array
     */
    public function getCustomMacroInDb($hostId = null, $template = null)
    {
        $arr = [];
        $i = 0;

        if ($hostId) {
            $sSql = 'SELECT host_macro_name, host_macro_value, is_password, description
                                FROM on_demand_macro_host
                                WHERE host_host_id = ' . intval($hostId) . ' ORDER BY macro_order ASC';

            $res = $this->db->query($sSql);

            while ($row = $res->fetch()) {
                if (preg_match('/\$_HOST(.*)\$$/', $row['host_macro_name'], $matches)) {
                    $arr[$i]['host_macro_name'] = $matches[1];
                    $arr[$i]['host_macro_value'] = $row['host_macro_value'];
                    $arr[$i]['is_password'] = $row['is_password'] ? 1 : null;
                    $arr[$i]['description'] = $row['description'];
                    $arr[$i]['description'] = $row['description'];
                    if (! is_null($template)) {
                        $arr[$i]['macroTpl'] = $template['host_name'];
                    }

                    $i++;
                }
            }
        }

        return $arr;
    }

    /**
     * @param $aTempMacro
     *
     * @return array
     */
    public function macro_unique($aTempMacro)
    {
        $storedMacros = [];
        foreach ($aTempMacro as $TempMacro) {
            $sInput = $TempMacro['host_macro_name'];
            $storedMacros[$sInput][] = $TempMacro;
        }

        $finalMacros = [];
        foreach ($storedMacros as $key => $macros) {
            $choosedMacro = [];
            foreach ($macros as $macro) {
                $choosedMacro = empty($choosedMacro) ? $macro : $this->comparaPriority($macro, $choosedMacro);
            }
            if (! empty($choosedMacro)) {
                $finalMacros[] = $choosedMacro;
            }
        }
        $this->addInfosToMacro($storedMacros, $finalMacros);

        return $finalMacros;
    }

    /**
     * Get clapi action name from db column name
     *
     * @param string $columnName
     * @return string
     */
    protected function getClapiActionName($columnName)
    {
        static $table;

        if (! isset($table)) {
            $table = ['command_command_id' => 'check_command', 'command_command_id2' => 'event_handler', 'timeperiod_tp_id' => 'check_period', 'timeperiod_tp_id2' => 'notification_period', 'command_command_id_arg1' => 'check_command_arguments', 'command_command_id_arg2' => 'event_handler_arguments', 'host_location' => self::HOST_LOCATION];
        }
        if (preg_match('/^ehi_/', $columnName)) {
            return substr($columnName, strlen('ehi_'));
        }

        return $table[$columnName] ?? $columnName;
    }

    /**
     * Wrap macro
     *
     * @param string $macroName
     * @return string
     */
    protected function wrapMacro($macroName)
    {
        return '$_HOST' . strtoupper($macroName) . '$';
    }

    /**
     * Strip macro
     *
     * @param string $macroName
     * @return string
     */
    protected function stripMacro($macroName)
    {
        $strippedMacro = $macroName;
        if (preg_match('/\$_HOST([a-zA-Z0-9_-]+)\$/', $strippedMacro, $matches)) {
            $strippedMacro = $matches[1];
        }

        return strtolower($strippedMacro);
    }

    /**
     * Deploy services
     * Recursive method
     *
     * @param int $hostId
     * @param mixed $hostTemplateId
     *
     * @throws CentreonClapiException
     * @throws PDOException
     * @return void
     */
    protected function deployServices($hostId, $hostTemplateId = null)
    {
        static $tmplRel;
        static $svcObj;
        static $hostSvcRel;
        static $svcExtended;

        if (! isset($tmplRel) && ! isset($svcObj) && ! isset($hostSvcRel)) {
            $tmplRel = new Centreon_Object_Relation_Host_Template_Host($this->dependencyInjector);
            $svcObj = new Centreon_Object_Service($this->dependencyInjector);
            $hostSvcRel = new Centreon_Object_Relation_Host_Service($this->dependencyInjector);
            $svcExtended = new Centreon_Object_Service_Extended($this->dependencyInjector);
        }

        $id = ! isset($hostTemplateId) ? $hostId : $hostTemplateId;
        $templates = $tmplRel->gethost_tpl_idFromhost_host_id($id);
        foreach ($templates as $templateId) {
            $serviceTemplates = $hostSvcRel->getservice_service_idFromhost_host_id($templateId);
            foreach ($serviceTemplates as $serviceTemplateId) {
                $params = $svcObj->getParameters($serviceTemplateId, ['service_alias']);
                $sql = 'SELECT service_id
                		FROM service s, host_service_relation hsr
                		WHERE s.service_id = hsr.service_service_id
                		AND s.service_description = :servicedescription
                		AND hsr.host_host_id = :hostid
                		UNION
                		SELECT service_id
                		FROM service s, host_service_relation hsr
                		WHERE s.service_id = hsr.service_service_id
                		AND s.service_description = :servicedescription
                		AND hsr.hostgroup_hg_id IN
                        (SELECT hostgroup_hg_id FROM hostgroup_relation WHERE host_host_id = :hostid)';
                $res = $this->db->query(
                    $sql,
                    [':servicedescription' => $params['service_alias'], ':hostid' => $hostId]
                );
                $result = $res->fetchAll();
                if (! count($result)) {
                    $serviceDesc = ['service_description' => $params['service_alias'], 'service_activate' => '1', 'service_register' => '1', 'service_template_model_stm_id' => $serviceTemplateId];
                    $svcId = $svcObj->insert($serviceDesc);
                    $hostSvcRel->insert($hostId, $svcId);
                    $serviceDesc['service_hPars'] = $hostId;
                    $this->addAuditLog(
                        'a',
                        $svcId,
                        $params['service_alias'],
                        $serviceDesc,
                        'SERVICE'
                    );
                    $svcExtended->insert([$svcExtended->getUniqueLabelField() => $svcId]);
                }
                unset($res);
            }
            $this->deployServices($hostId, $templateId);
        }
    }

    /**
     * @param $elements
     *
     * @throws Exception
     * @return array
     */
    protected function getHostListByParent(&$elements)
    {
        $hostParent = new Centreon_Object_Relation_Host_Parent_Host($this->dependencyInjector);
        $parentShip = [];
        $relations = $hostParent->getRelations();
        foreach ($relations as $relation) {
            $firstKey = $relation[$hostParent->getFirstKey()];
            $secondKey = $relation[$hostParent->getSecondKey()];
            $parentShip[$secondKey][] = $firstKey;
        }
        $hosts = [];
        foreach ($elements as $element) {
            $hosts[] = $element['host_id'];
        }
        $sortedHosts = [];
        while ($hostId = array_pop($hosts)) {
            if (! in_array((int) $hostId, $parentShip)) {
                $sortedHosts[] = $hostId;
            } else {
                $parents = $parentShip[$hostId];
                $parentExported = true;
                foreach ($parents as $parentId) {
                    if (! in_array($parentId, $sortedHosts)) {
                        $parentExported = false;
                        break;
                    }
                }
                if ($parentExported) {
                    $sortedHosts[] = $hostId;
                } else {
                    array_unshift($hosts, $hostId);
                }
            }
        }
        $elementsIndexedById = [];
        foreach ($elements as $element) {
            $elementsIndexedById[$element['host_id']] = $element;
        }
        $elements = [];
        foreach ($sortedHosts as $hostId) {
            $elements[$hostId] = $elementsIndexedById[$hostId];
        }

        return $parentShip;
    }

    /**
     * @param $macroA
     * @param $macroB
     * @param $getFirst
     *
     * @return mixed
     */
    private function comparaPriority($macroA, $macroB, $getFirst = true)
    {
        $arrayPrio = ['direct' => 3, 'fromTpl' => 2, 'fromCommand' => 1];
        if ($getFirst) {
            if ($arrayPrio[$macroA['source']] > $arrayPrio[$macroB['source']]) {
                return $macroA;
            }

            return $macroB;
        }
        if ($arrayPrio[$macroA['source']] >= $arrayPrio[$macroB['source']]) {
            return $macroA;
        }

        return $macroB;
    }

    /**
     * @param $storedMacros
     * @param $finalMacros
     *
     * @return void
     */
    private function addInfosToMacro($storedMacros, &$finalMacros): void
    {
        foreach ($finalMacros as &$finalMacro) {
            $sInput = $finalMacro['host_macro_name'];
            $this->setInheritedDescription(
                $finalMacro,
                $this->getInheritedDescription($storedMacros[$sInput], $finalMacro)
            );
            switch ($finalMacro['source']) {
                case 'direct':
                    $this->setTplValue($this->findTplValue($storedMacros[$sInput]), $finalMacro);
                    break;
                case 'fromTpl':
                    break;
                case 'fromCommand':
                    break;
                default:
                    break;
            }
        }
    }

    /**
     * @param $storedMacros
     * @param $finalMacro
     *
     * @return mixed|string
     */
    private function getInheritedDescription($storedMacros, $finalMacro)
    {
        $description = '';
        if (empty($finalMacro['description'])) {
            $choosedMacro = [];
            foreach ($storedMacros as $storedMacro) {
                if (! empty($storedMacro['description'])) {
                    $choosedMacro = empty($choosedMacro) ? $storedMacro : $this->comparaPriority($storedMacro, $choosedMacro);

                    $description = $choosedMacro['description'];
                }
            }
        } else {
            $description = $finalMacro['description'];
        }

        return $description;
    }

    /**
     * @param $finalMacro
     * @param $description
     *
     * @return void
     */
    private function setInheritedDescription(&$finalMacro, $description): void
    {
        $finalMacro['description'] = $description;
        $finalMacro['description'] = $description;
    }

    /**
     * @param $tplValue
     * @param $finalMacro
     *
     * @return void
     */
    private function setTplValue($tplValue, &$finalMacro): void
    {
        if ($tplValue) {
            $finalMacro['macroTplValue'] = $tplValue;
            $finalMacro['macroTplValToDisplay'] = 1;
        } else {
            $finalMacro['macroTplValue'] = '';
            $finalMacro['macroTplValToDisplay'] = 0;
        }
    }

    /**
     * @param $storedMacro
     * @param $getFirst
     *
     * @return false
     */
    private function findTplValue($storedMacro, $getFirst = true)
    {
        if ($getFirst) {
            foreach ($storedMacro as $macros) {
                if ($macros['source'] == 'fromTpl') {
                    return $macros['host_macro_value'];
                }
            }
        } else {
            $macroReturn = false;
            foreach ($storedMacro as $macros) {
                if ($macros['source'] == 'fromTpl') {
                    $macroReturn = $macros['host_macro_value'];
                }
            }

            return $macroReturn;
        }

        return false;
    }
}
