<?php

/**
 * ---------------------------------------------------------------------
 *
 * GLPI - Gestionnaire Libre de Parc Informatique
 *
 * http://glpi-project.org
 *
 * @copyright 2015-2026 Teclib' and contributors.
 * @copyright 2003-2014 by the INDEPNET Development Team.
 * @licence   https://www.gnu.org/licenses/gpl-3.0.html
 *
 * ---------------------------------------------------------------------
 *
 * LICENSE
 *
 * This file is part of GLPI.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 *
 * ---------------------------------------------------------------------
 */

use Glpi\Application\View\TemplateRenderer;

/**
 * Infocom class
 **/
class Infocom extends CommonDBChild
{
    // From CommonDBChild
    public static $itemtype        = 'itemtype';
    public static $items_id        = 'items_id';
    public $dohistory              = true;
    public $auto_message_on_action = false; // Link in message can't work'
    public static $logs_for_parent = false;
    public static $rightname              = 'infocom';

    //Option to automatically fill dates
    public const ON_STATUS_CHANGE   = 'STATUS';
    public const COPY_WARRANTY_DATE = 1;
    public const COPY_BUY_DATE      = 2;
    public const COPY_ORDER_DATE    = 3;
    public const COPY_DELIVERY_DATE = 4;
    public const ON_ASSET_IMPORT    = 5;


    /**
     * Check if given object can have Infocom
     *
     * @since 0.85
     *
     * @param string|object $item  an object or a string
     *
     * @return boolean true if $object is an object that can have Infocom
     *
     **/
    public static function canApplyOn($item)
    {
        /** @var array $CFG_GLPI */
        global $CFG_GLPI;

        // All devices are subjects to infocom !
        if (is_a($item, 'Item_Devices', true)) {
            return true;
        }

        // We also allow direct items to check
        if ($item instanceof CommonGLPI) {
            $item = $item->getType();
        }

        if (in_array($item, $CFG_GLPI['infocom_types'])) {
            return true;
        }

        return false;
    }


    /**
     * Get all the types that can have an infocom
     *
     * @since 0.85
     *
     * @return array of the itemtypes
     **/
    public static function getItemtypesThatCanHave()
    {
        /** @var array $CFG_GLPI */
        global $CFG_GLPI;

        $types = array_merge(
            $CFG_GLPI['infocom_types'],
            Item_Devices::getDeviceTypes()
        );
        return array_unique($types);
    }


    public static function getTypeName($nb = 0)
    {
        //TRANS: Always plural
        return __('Financial and administrative information');
    }


    public function post_getEmpty()
    {

        if (isset($_SESSION['glpiactive_entity'])) {
            $this->fields["alert"] = Entity::getUsedConfig(
                "use_infocoms_alert",
                $_SESSION['glpiactive_entity'],
                "default_infocom_alert",
                0
            );
        }
    }


    public function getLogTypeID()
    {
        return [$this->fields['itemtype'], $this->fields['items_id']];
    }


    public function getTabNameForItem(CommonGLPI $item, $withtemplate = 0)
    {

        // Can exists on template
        if (
            Session::haveRight(self::$rightname, READ)
            && ($item instanceof CommonDBTM)
        ) {
            $nb = 0;
            switch ($item->getType()) {
                case 'Supplier':
                    /** @var Supplier $item */
                    if ($_SESSION['glpishow_count_on_tabs']) {
                        $nb = self::countForSupplier($item);
                    }
                    return self::createTabEntry(_n('Item', 'Items', Session::getPluralNumber()), $nb);

                default:
                    if ($_SESSION['glpishow_count_on_tabs']) {
                        $nb = countElementsInTable(
                            'glpi_infocoms',
                            ['itemtype' => $item->getType(),
                                'items_id' => $item->getID(),
                            ]
                        );
                    }
                    return self::createTabEntry(__('Management'), $nb);
            }
        }
        return '';
    }


    /**
     * @param $item            CommonGLPI object
     * @param $tabnum          (default 1)
     * @param $withtemplate    (default 0)
     **/
    public static function displayTabContentForItem(CommonGLPI $item, $tabnum = 1, $withtemplate = 0)
    {

        switch ($item->getType()) {
            case 'Supplier':
                /** @var Supplier $item */
                $item->showInfocoms();
                break;

            default:
                self::showForItem($item, $withtemplate);
        }
        return true;
    }


    /**
     * @param $item   Supplier  object
     **/
    public static function countForSupplier(Supplier $item)
    {

        return countElementsInTable(
            'glpi_infocoms',
            [
                'suppliers_id' => $item->getField('id'),
                'NOT' => ['itemtype' => ['ConsumableItem', 'CartridgeItem', 'Software']],
            ] + getEntitiesRestrictCriteria('glpi_infocoms', '', $_SESSION['glpiactiveentities'])
        );
    }

    public static function getSpecificValueToDisplay($field, $values, array $options = [])
    {

        if (!is_array($values)) {
            $values = [$field => $values];
        }
        switch ($field) {
            case 'sink_type':
                return self::getAmortTypeName($values[$field]);

            case 'alert':
                return self::getAlertName($values[$field]);
        }
        return parent::getSpecificValueToDisplay($field, $values, $options);
    }


    /**
     * @since 0.84
     *
     * @param $field
     * @param $name               (default '')
     * @param $values             (default '')
     * @param $options      array
     **/
    public static function getSpecificValueToSelect($field, $name = '', $values = '', array $options = [])
    {

        if (!is_array($values)) {
            $values = [$field => $values];
        }
        $options['display'] = false;
        switch ($field) {
            case "sink_type":
                return self::dropdownAmortType($name, $values[$field], false);

            case "alert":
                $options['name']  = $name;
                $options['value'] = $values[$field];
                return self::dropdownAlert($options);
        }
        return parent::getSpecificValueToSelect($field, $name, $values, $options);
    }


    /**
     * Retrieve an item from the database for a device
     *
     * @param string  $itemtype  type of the device to retrieve infocom
     * @param integer $ID        ID of the device to retrieve infocom
     *
     * @return boolean true if succeed else false
     **/
    public function getFromDBforDevice($itemtype, $ID)
    {

        if (
            $this->getFromDBByCrit([
                $this->getTable() . '.items_id'  => $ID,
                $this->getTable() . '.itemtype'  => $itemtype,
            ])
        ) {
            return true;
        }
        $this->getEmpty();
        $this->fields["items_id"] = $ID;
        $this->fields["itemtype"] = $itemtype;
        return false;
    }


    public function prepareInputForAdd($input)
    {
        if (!$this->getFromDBforDevice($input['itemtype'], $input['items_id'])) {
            if ($item = static::getItemFromArray(static::$itemtype, static::$items_id, $input)) {
                $input['alert'] = Entity::getUsedConfig('default_infocom_alert', $item->getEntityID());
                return parent::prepareInputForAdd($input);
            }
        }
        return false;
    }


    /**
     * Fill, if necessary, automatically some dates when status changes
     *
     * @param CommonDBTM $item          CommonDBTM object: the item whose status have changed
     * @param boolean $action_add    true if object is added, false if updated (true by default)
     *
     * @return void
     **/
    public static function manageDateOnStatusChange(CommonDBTM $item, $action_add = true)
    {
        $itemtype = get_class($item);
        $changes  = $item->fields;

        //Autofill date on item's status change ?
        $infocom = new self();
        $infocom->getFromDB($changes['id']);
        $tmp           = ['itemtype' => $itemtype,
            'items_id' => $changes['id'],
        ];
        $add_or_update = false;

        //For each date that can be automatically filled
        foreach (self::getAutoManagemendDatesFields() as $date => $date_field) {
            $resp   = [];
            $result = Entity::getUsedConfig($date, $changes['entities_id']);

            //Date must be filled if status corresponds to the one defined in the config
            if (
                preg_match('/' . self::ON_STATUS_CHANGE . '_(.*)/', $result, $values)
                && ($values[1] == $changes['states_id'])
            ) {
                $add_or_update    = true;
                $tmp[$date_field] = $_SESSION["glpi_currenttime"];
            }
        }

        //One date or more has changed
        if ($add_or_update) {
            if (!$infocom->getFromDBforDevice($itemtype, $changes['id'])) {
                $infocom->add($tmp);
            } else {
                $tmp['id'] = $infocom->fields['id'];
                $infocom->update($tmp);
            }
        }
    }


    /**
     * Automatically manage copying one date to another is necessary
     *
     * @param array $infocoms   array of item's infocom to modify
     * @param string $field            the date to modify (default '')
     * @param integer $action           the action to peform (copy from another date) (default 0)
     * @param array $params     array of additional parameters needed to perform the task
     *
     * @return void
     **/
    public static function autofillDates(&$infocoms = [], $field = '', $action = 0, $params = [])
    {

        if (isset($infocoms[$field]) || is_null($infocoms[$field])) {
            switch ($action) {
                default:
                case 0:
                    break;

                case self::COPY_WARRANTY_DATE:
                    if (isset($infocoms['warranty_date'])) {
                        $infocoms[$field] = $infocoms['warranty_date'];
                    }
                    break;

                case self::COPY_BUY_DATE:
                    if (isset($infocoms['buy_date'])) {
                        $infocoms[$field] = $infocoms['buy_date'];
                    }
                    break;

                case self::COPY_ORDER_DATE:
                    if (isset($infocoms['order_date'])) {
                        $infocoms[$field] = $infocoms['order_date'];
                    }
                    break;

                case self::COPY_DELIVERY_DATE:
                    if (isset($infocoms['delivery_date'])) {
                        $infocoms[$field] = $infocoms['delivery_date'];
                    }
                    break;
            }
        }
    }


    /**
     * Return all infocom dates that could be automaticall filled
     *
     * @return array with all dates (configuration field & real field)
     **/
    public static function getAutoManagemendDatesFields()
    {

        return ['autofill_buy_date'         => 'buy_date',
            'autofill_use_date'         => 'use_date',
            'autofill_delivery_date'    => 'delivery_date',
            'autofill_warranty_date'    => 'warranty_date',
            'autofill_order_date'       => 'order_date',
            'autofill_decommission_date' => 'decommission_date',
        ];
    }


    public function prepareInputForUpdate($input)
    {

        //Check if one or more dates needs to be updated
        foreach (self::getAutoManagemendDatesFields() as $key => $field) {
            $result = Entity::getUsedConfig($key, $this->fields['entities_id']);

            //Only update date if it's empty in DB. Otherwise do nothing
            if (
                ($result > 0)
                && !isset($this->fields[$field])
            ) {
                if (!isset($input[$field])) {
                    $input[$field] = null;
                }
                self::autofillDates($input, $field, $result);
            }
        }

        return parent::prepareInputForUpdate($input);
    }


    public function pre_updateInDB()
    {

        // Clean end alert if warranty_date is after old one
        // Or if duration is greater than old one
        if (
            (isset($this->oldvalues['warranty_date'])
            && ($this->oldvalues['warranty_date'] < $this->fields['warranty_date']))
            || (isset($this->oldvalues['warranty_duration'])
              && ($this->oldvalues['warranty_duration'] < $this->fields['warranty_duration']))
        ) {
            $alert = new Alert();
            $alert->clear($this->getType(), $this->fields['id'], Alert::END);
        }
        // Check budgets link validity
        if (
            (in_array('budgets_id', $this->updates)
            || in_array('buy_date', $this->updates))
            && $this->fields['budgets_id']
            && ($budget = getItemForItemtype('Budget'))
            && $budget->getFromDB($this->fields['budgets_id'])
        ) {
            if (
                (!is_null($budget->fields['begin_date'])
                && $this->fields['buy_date'] < $budget->fields['begin_date'])
                || (!is_null($budget->fields['end_date'])
                 && ($this->fields['buy_date'] > $budget->fields['end_date']))
            ) {
                $msg = sprintf(
                    __('Purchase date incompatible with the associated budget. %1$s not in budget period: %2$s / %3$s'),
                    Html::convDate($this->fields['buy_date']),
                    Html::convDate($budget->fields['begin_date']),
                    Html::convDate($budget->fields['end_date'])
                );
                Session::addMessageAfterRedirect($msg, false, ERROR);
            }
        }
    }

    /**
     * @since 0.84
     **/
    public function cleanDBonPurge()
    {

        $class = new Alert();
        $class->cleanDBonItemDelete($this->getType(), $this->fields['id']);
    }


    /**
     * @param $name
     **/
    public static function cronInfo($name)
    {
        return ['description' => __('Send alarms on financial and administrative information')];
    }


    /**
     * Cron action on infocom : alert on expired warranty
     *
     * @param CronTask $task to log, if NULL use display (default NULL)
     *
     * @return integer 0 : nothing to do 1 : done with success
     **/
    public static function cronInfocom($task = null)
    {
        /**
         * @var array $CFG_GLPI
         * @var \DBmysql $DB
         */
        global $CFG_GLPI, $DB;

        if (!$CFG_GLPI["use_notifications"]) {
            return 0;
        }

        $message        = [];
        $cron_status    = 0;
        $items_infos    = [];
        $items_messages = [];

        foreach (Entity::getEntitiesToNotify('use_infocoms_alert') as $entity => $value) {
            $before    = Entity::getUsedConfig('send_infocoms_alert_before_delay', $entity);
            $table = self::getTable();
            $iterator = $DB->request([
                'SELECT'    => "$table.*",
                'FROM'      => $table,
                'LEFT JOIN'  => [
                    'glpi_alerts'  => [
                        'ON' => [
                            'glpi_alerts'  => 'items_id',
                            $table         => 'id', [
                                'AND' => [
                                    'glpi_alerts.itemtype'  => self::getType(),
                                    'glpi_alerts.type'      => Alert::END,
                                ],
                            ],
                        ],
                    ],
                ],
                'WHERE'     => [
                    new \QueryExpression(
                        '(' . $DB->quoteName('glpi_infocoms.alert') . ' & ' . pow(2, Alert::END) . ') > 0'
                    ),
                    "$table.entities_id"       => $entity,
                    "$table.warranty_duration" => ['>', 0],
                    'NOT'                      => ["$table.warranty_date" => null],
                    new \QueryExpression(
                        'DATEDIFF(ADDDATE(' . $DB->quoteName('glpi_infocoms.warranty_date') . ', INTERVAL (' .
                        $DB->quoteName('glpi_infocoms.warranty_duration') . ') MONTH), CURDATE() ) <= ' .
                        $DB->quoteValue($before)
                    ),
                    'glpi_alerts.date'         => null,
                ],
            ]);

            foreach ($iterator as $data) {
                if ($item_infocom = getItemForItemtype($data["itemtype"])) {
                    if ($item_infocom->getFromDB($data["items_id"])) {
                        $entity   = $data['entities_id'];
                        $warranty = self::getWarrantyExpir($data["warranty_date"], $data["warranty_duration"]);
                        //TRANS: %1$s is a type, %2$s is a name (used in croninfocom)
                        $name    = sprintf(
                            __('%1$s - %2$s'),
                            $item_infocom->getTypeName(1),
                            $item_infocom->getName()
                        );
                        //TRANS: %1$s is the warranty end date and %2$s the name of the item
                        $message = sprintf(
                            __('Item reaching the end of warranty on %1$s: %2$s'),
                            $warranty,
                            $name
                        ) . "<br>";

                        $data['warrantyexpiration']        = $warranty;
                        $data['item_name']                 = $item_infocom->getName();
                        $items_infos[$entity][$data['id']] = $data;

                        if (!isset($items_messages[$entity])) {
                            $items_messages[$entity] = __('No item reaching the end of warranty.') . "<br>";
                        }
                        $items_messages[$entity] .= $message;
                    }
                }
            }
        }

        foreach ($items_infos as $entity => $items) {
            if (
                NotificationEvent::raiseEvent("alert", new self(), ['entities_id' => $entity,
                    'items'       => $items,
                ])
            ) {
                $message     = $items_messages[$entity];
                $cron_status = 1;
                if ($task) {
                    $task->log(sprintf(
                        __('%1$s: %2$s') . "\n",
                        Dropdown::getDropdownName("glpi_entities", $entity),
                        $message
                    ));
                    $task->addVolume(1);
                } else {
                    Session::addMessageAfterRedirect(sprintf(
                        __('%1$s: %2$s'),
                        Dropdown::getDropdownName(
                            "glpi_entities",
                            $entity
                        ),
                        $message
                    ));
                }

                $alert             = new Alert();
                $input["itemtype"] = 'Infocom';
                $input["type"]     = Alert::END;
                foreach ($items as $id => $item) {
                    $input["items_id"] = $id;
                    $alert->add($input);
                    unset($alert->fields['id']);
                }
            } else {
                $entityname = Dropdown::getDropdownName('glpi_entities', $entity);
                //TRANS: %s is entity name
                $msg = sprintf(__('%1$s: %2$s'), $entityname, __('send infocom alert failed'));
                if ($task) {
                    $task->log($msg);
                } else {
                    Session::addMessageAfterRedirect($msg, false, ERROR);
                }
            }
        }
        return $cron_status;
    }


    /**
     * Get the possible value for infocom alert
     *
     * @since 0.84 (before in alert.class)
     *
     * @param integer|string|null $val if not set, ask for all values, else for 1 value (default NULL)
     *
     * @return array|string
     **/
    public static function getAlertName($val = null)
    {

        $tmp[0]                  = Dropdown::EMPTY_VALUE;
        $tmp[pow(2, Alert::END)] = __('Warranty expiration date');

        if (is_null($val)) {
            return $tmp;
        }
        // Default value for display
        $tmp[0] = ' ';

        if (isset($tmp[$val])) {
            return $tmp[$val];
        }
        // If not set and is a string return value
        if (is_string($val)) {
            return $val;
        }
        return NOT_AVAILABLE;
    }


    /**
     * @param $options array
     **/
    public static function dropdownAlert($options)
    {

        $p['name']           = 'alert';
        $p['value']          = 0;
        $p['display']        = true;
        $p['inherit_parent'] = false;

        if (count($options)) {
            foreach ($options as $key => $val) {
                $p[$key] = $val;
            }
        }

        $tab = [];
        if ($p['inherit_parent']) {
            $tab[Entity::CONFIG_PARENT] = __('Inheritance of the parent entity');
        }

        $tab += self::getAlertName();

        return Dropdown::showFromArray($p['name'], $tab, $p);
    }


    /**
     * Dropdown of amortissement type for infocoms
     *
     * @param string  $name      select name
     * @param integer $value     default value (default 0)
     * @param boolean $display   display or get string (true by default)
     **/
    public static function dropdownAmortType($name, $value = 0, $display = true)
    {

        $values = [
            2 => __('Linear'),
            1 => __('Decreasing'),
        ];

        return Dropdown::showFromArray($name, $values, [
            'value'               => $value,
            'display'             => $display,
            'display_emptychoice' => true,
            'width'               => '100%',
        ]);
    }


    /**
     * Get amortissement type name for infocoms
     *
     * @param integer $value status ID
     **/
    public static function getAmortTypeName($value)
    {

        switch ($value) {
            case 2:
                return __('Linear');

            case 1:
                return __('Decreasing');

            case 0:
                return " ";
        }
    }


    /**
     * Calculate TCO and TCO by month for an item
     *
     * @param string|number $ticket_tco    Tco part of tickets
     * @param number        $value
     * @param string        $date_achat    (default '')
     *
     * @return float
     **/
    public static function showTco($ticket_tco, $value, $date_achat = "")
    {
        if ($ticket_tco == NOT_AVAILABLE) {
            return '-';
        }

        // Affiche le TCO ou le TCO mensuel pour un mat??riel
        $totalcost = $ticket_tco;

        if ($date_achat) { // on veut donc le TCO mensuel
            // just to avoid IDE warning
            $date_Y = $date_m = $date_d = 0;

            sscanf($date_achat, "%4s-%2s-%2s", $date_Y, $date_m, $date_d);

            $timestamp2 = mktime(0, 0, 0, $date_m, $date_d, $date_Y);
            $timestamp  = mktime(0, 0, 0, date("m"), date("d"), date("Y"));

            $diff = floor(($timestamp - $timestamp2) / (MONTH_TIMESTAMP)); // Mois d'utilisation

            if ($diff) {
                return Html::formatNumber((($totalcost + $value) / $diff)); // TCO mensuel
            }
            return "";
        }
        return Html::formatNumber(($totalcost + $value)); // TCO
    }


    /**
     * Show infocom link to display modal
     *
     * @param $itemtype   integer  item type
     * @param $device_id  integer  item ID
     *
     * @return void
     **/
    public static function showDisplayLink($itemtype, $device_id)
    {
        /**
         * @var array $CFG_GLPI
         * @var \DBmysql $DB
         */
        global $CFG_GLPI, $DB;

        if (
            !Session::haveRight(self::$rightname, READ)
            || !($item = getItemForItemtype($itemtype))
        ) {
            return;
        }

        $result = $DB->request([
            'COUNT'  => 'cpt',
            'FROM'   => 'glpi_infocoms',
            'WHERE'  => [
                'itemtype'  => $itemtype,
                'items_id'  => $device_id,
            ],
        ])->current();

        $add    = "add";
        $text   = __('Add');
        if ($result['cpt'] > 0) {
            $add  = "";
            $text = _x('button', 'Show');
        } elseif (!Infocom::canUpdate()) {
            return;
        }

        if ($item->canView()) {
            echo "<span data-bs-toggle='modal' data-bs-target='#infocom$itemtype$device_id' style='cursor:pointer'>
               <img src=\"" . $CFG_GLPI["root_doc"] . "/pics/dollar$add.png\" alt=\"$text\" title=\"$text\">
               </span>";
            Ajax::createIframeModalWindow(
                'infocom' . $itemtype . $device_id,
                Infocom::getFormURL() .
                                          "?itemtype=$itemtype&items_id=$device_id",
                ['height' => 600]
            );
        }
    }


    /**
     * Calculate amortization values
     *
     * @param number        $value       Purchase value
     * @param number        $duration    Amortise duration
     * @param string        $fiscaldate  Begin of fiscal excercise
     * @param string $buydate     Buy date
     * @param string $usedate     Date of use
     *
     * @return array|boolean
     */
    public static function linearAmortise($value, $duration, $fiscaldate, $buydate = '', $usedate = '')
    {
        //Set timezone to UTC; see https://stackoverflow.com/a/40358744
        $TZ = 'UTC';

        try {
            if ($fiscaldate == '') {
                throw new \RuntimeException('Empty date');
            }
            $fiscaldate = new \DateTime($fiscaldate, new DateTimeZone($TZ));
        } catch (\Throwable $e) {
            Session::addMessageAfterRedirect(
                __('Please fill you fiscal year date in preferences.'),
                false,
                ERROR
            );
            return false;
        }

        //get begin date. Work on use date if provided.
        try {
            if ($buydate == '' && $usedate == '') {
                throw new \RuntimeException('Empty date');
            }
            if ($usedate != '') {
                $usedate = new \DateTime($usedate, new DateTimeZone($TZ));
            } else {
                $usedate = new \DateTime($buydate, new DateTimeZone($TZ));
            }
        } catch (\Throwable $e) {
            Session::addMessageAfterRedirect(
                __('Please fill either buy or use date in preferences.'),
                false,
                ERROR
            );
            return false;
        }

        $now = new \DateTime('now', new DateTimeZone($TZ));

        $elapsed_years = $now->format('Y') - $usedate->format('Y');

        $annuity = 0;
        if ($duration) {
            $annuity = $value * (1 / $duration);
        }

        $years = [];
        for ($i = 0; $i <= $elapsed_years; ++$i) {
            $begin_value      = $value;
            $current_annuity  = $annuity;
            $fiscal_end       = new \DateTime(
                $fiscaldate->format('d-m-') . ($usedate->format('Y') + $i),
                new DateTimeZone($TZ)
            );

            if ($i == 0) {
                //first year, calculate prorata
                if ($fiscal_end < $usedate) {
                    $fiscal_end->modify('+1 year');
                }
                $days = $fiscal_end->diff($usedate);
                $days = (int) $days->format('%m') * 30 + (int) $days->format('%d');
                $current_annuity = $annuity * $days / 360;
            } elseif ($i == $duration) {
                $current_annuity = $value;
            }
            if ($i > $duration) {
                $value = 0;
                $current_annuity = 0;
            } else {
                //calculate annuity
                //full year case
                $value -= $current_annuity;
            }

            $years[$usedate->format('Y') + $i] = [
                'start_value'  => (float) $begin_value,
                'value'        => $value,
                'annuity'      => $current_annuity,
            ];
        }

        return $years;
    }

    /**
     * Maps new amortise format to old one...
     * To not rewrite all the old method.
     *
     * @param array $values New format amortise values
     * @param boolean $current True to get only current year, false to get the whole array
     *
     * @return array|double
     */
    public static function mapOldAmortiseFormat($values, $current = true)
    {

        if ($current === true) {
            return $values[date('Y')]['value'];
        }

        $old = [
            'annee'     => [],
            'annuite'   => [],
            'vcnetdeb'  => [],
            'vcnetfin'  => [],
        ];
        foreach ($values as $year => $value) {
            $old['annee'][]      = $year;
            $old['annuite'][]    = $value['annuity'];
            $old['vcnetdeb'][]   = $value['start_value'];
            $old['vcnetfin'][]   = $value['value'];
        }

        return $old;
    }

    /**
     * Calculate amortissement for an item
     *
     * @param integer $type_amort    type d'amortisssment "lineaire=2" ou "degressif=1"
     * @param number  $va            valeur d'acquisition
     * @param number  $duree         duree d'amortissement
     * @param number  $coef          coefficient d'amortissement
     * @param string|null  $date_achat    Date d'achat
     * @param string|null  $date_use      Date d'utilisation
     * @param string|null  $date_tax      date du debut de l'annee fiscale
     * @param string  $view          "n" pour l'annee en cours ou "all" pour le tableau complet (default 'n')
     *
     * @return float|array
     **/
    public static function Amort(
        $type_amort,
        $va,
        $duree,
        $coef,
        $date_achat,
        $date_use,
        $date_tax,
        $view = "n"
    ) {
        // By Jean-Mathieu Doleans qui s'est un peu pris le chou :p

        // Attention date mise en service/dateachat ->amort lineaire  et $prorata en jour !!
        // amort degressif au prorata du nombre de mois.
        // Son point de depart est le 1er jour du mois d'acquisition et non date de mise en service

        if ($type_amort == "2") {
            $values = self::linearAmortise($va, $duree, $date_tax, $date_achat, $date_use);
            if ($values == false) {
                return '-';
            }
            return self::mapOldAmortiseFormat($values, $view != 'all');
        }

        $prorata             = 0;
        $ecartfinmoiscourant = 0;
        $ecartmoisexercice   = 0;
        $date_Y  =  $date_m  =  $date_d  =  $date_H  =  $date_i  =  $date_s  =  0;
        sscanf(
            ($date_achat ?? ""),
            "%4s-%2s-%2s %2s:%2s:%2s",
            $date_Y,
            $date_m,
            $date_d,
            $date_H,
            $date_i,
            $date_s
        ); // un traitement sur la date mysql pour recuperer l'annee

        // un traitement sur la date mysql pour les infos necessaires
        $date_Y2 = $date_m2 = $date_d2 = $date_H2 = $date_i2 = $date_s2 = 0;
        sscanf(
            ($date_tax ?? ""),
            "%4s-%2s-%2s %2s:%2s:%2s",
            $date_Y2,
            $date_m2,
            $date_d2,
            $date_H2,
            $date_i2,
            $date_s2
        );
        $date_Y2 = date("Y");

        switch ($type_amort) {
            case "1":
                //########################### Calcul amortissement degressif ###########################
                if (
                    ($va > 0)
                    && ($duree > 0)
                    && ($coef > 1)
                    && !empty($date_achat)
                ) {
                    //## calcul du prorata temporis en mois ##
                    // si l'annee fiscale debute au dela de l'annee courante
                    if ($date_m > $date_m2) {
                        $date_m2 = $date_m2 + 12;
                    }
                    $ecartmois      = ($date_m2 - $date_m) + 1; // calcul ecart entre mois d'acquisition
                    // et debut annee fiscale
                    $prorata        = $ecartfinmoiscourant + $ecartmois - $ecartmoisexercice;
                    // calcul tableau d'amortissement ##
                    $txlineaire     = (100 / $duree); // calcul du taux lineaire virtuel
                    $txdegressif    = $txlineaire * $coef; // calcul du taux degressif
                    $dureelineaire  = (int) (100 / $txdegressif); // calcul de la duree de l'amortissement
                    // en mode lineaire
                    $dureedegressif = $duree - $dureelineaire; // calcul de la duree de l'amortissement
                    // en mode degressif
                    $mrt            = $va;

                    $tab = [
                        'annee'    => [],
                        'vcnetdeb' => [],
                        'vcnetfin' => [],
                        'annuite'  => [],
                    ];

                    // amortissement degressif pour les premieres annees
                    for ($i = 1; $i <= $dureedegressif; $i++) {
                        $tab['annee'][$i]    = $date_Y + $i - 1;
                        $tab['vcnetdeb'][$i] = $mrt; // Pour chaque annee on calcule la valeur comptable nette
                        // de debut d'exercice
                        $tab['annuite'][$i]  = $tab['vcnetdeb'][$i] * $txdegressif / 100;
                        $tab['vcnetfin'][$i] = $mrt - $tab['annuite'][$i]; //Pour chaque annee on calcule la valeur
                        //comptable nette de fin d'exercice
                        // calcul de la premiere annuite si prorata temporis
                        if ($prorata > 0) {
                            $tab['annuite'][1]  = ($va * $txdegressif / 100) * ($prorata / 12);
                            $tab['vcnetfin'][1] = $va - $tab['annuite'][1];
                        }
                        $mrt = $tab['vcnetfin'][$i];
                    }
                    // amortissement en lineaire pour les derneres annees
                    if ($dureelineaire != 0) {
                        $txlineaire = (100 / $dureelineaire); // calcul du taux lineaire
                    } else {
                        $txlineaire = 100;
                    }
                    $annuite = ($tab['vcnetfin'][$dureedegressif] * $txlineaire) / 100; // calcul de l'annuite
                    $mrt     = $tab['vcnetfin'][$dureedegressif];
                    for ($i = $dureedegressif + 1; $i <= $dureedegressif + $dureelineaire; $i++) {
                        $tab['annee'][$i]    = $date_Y + $i - 1;
                        $tab['annuite'][$i]  = $annuite;
                        $tab['vcnetdeb'][$i] = $mrt; // Pour chaque annee on calcule la valeur comptable nette
                        // de debut d'exercice
                        $tab['vcnetfin'][$i] = abs(($mrt - $annuite)); // Pour chaque annee on calcule la valeur
                        // comptable nette de fin d'exercice
                        $mrt                 = $tab['vcnetfin'][$i];
                    }
                    // calcul de la derniere annuite si prorata temporis
                    if ($prorata > 0) {
                        $tab['annuite'][$duree] = $tab['vcnetdeb'][$duree];
                        if (isset($tab['vcnetfin'][$duree - 1])) {
                            $tab['vcnetfin'][$duree] = ($tab['vcnetfin'][$duree - 1] - $tab['annuite'][$duree]);
                        } else {
                            $tab['vcnetfin'][$duree] = 0;
                        }
                    }
                } else {
                    return "-";
                }
                break;

            default:
                return "-";
        }

        // le return
        if ($view == "all") {
            // on retourne le tableau complet
            return $tab;
        }
        // on retourne juste la valeur residuelle
        // si on ne trouve pas l'annee en cours dans le tableau d'amortissement dans le tableau,
        // le materiel est amorti
        if (!array_search(date("Y"), $tab["annee"])) {
            $vnc = 0;
        } elseif (
            mktime(0, 0, 0, $date_m2, $date_d2, date("Y"))
                 - mktime(0, 0, 0, date("m"), date("d"), date("Y")) < 0
        ) {
            // on a depasse la fin d'exercice de l'annee en cours
            //on prend la valeur residuelle de l'annee en cours
            $vnc = $tab["vcnetfin"][array_search(date("Y"), $tab["annee"])];
        } else {
            // on se situe avant la fin d'exercice
            // on prend la valeur residuelle de l'annee n-1
            $vnc = $tab["vcnetdeb"][array_search(date("Y"), $tab["annee"])];
        }
        return $vnc;
    }


    /**
     * Show Infocom form for an item (not a standard showForm)
     *
     * @param $item                  CommonDBTM object
     * @param $withtemplate integer  template or basic item (default 0)
     **/
    public static function showForItem(CommonDBTM $item, $withtemplate = 0)
    {
        // Show Infocom or blank form
        if (!self::canView()) {
            return false;
        }

        if (!$item) {
            echo "<div class='spaced'>" . __('Requested item not found') . "</div>";
        } else {
            $dev_ID   = $item->getField('id');
            $ic       = new self();

            if (
                !strpos($_SERVER['PHP_SELF'], "infocoms-show")
                && in_array($item->getType(), self::getExcludedTypes())
            ) {
                echo "<div class='firstbloc center'>" .
                  __('For this type of item, the financial and administrative information are only a model for the items which you should add.') .
                 "</div>";
            }

            $ic->getFromDBforDevice($item->getType(), $dev_ID);
            $can_input = [
                'itemtype'    => $item->getType(),
                'items_id'    => $dev_ID,
                'entities_id' => $item->getEntityID(),
            ];
            TemplateRenderer::getInstance()->display('components/infocom.html.twig', [
                'item'              => $item,
                'infocom'           => $ic,
                'withtemplate'      => $withtemplate,
                'can_create'        => $ic->can(-1, CREATE, $can_input),
                'can_edit'          => ($ic->canEdit($ic->fields['id']) && ($withtemplate != 2)),
                'can_global_update' => Session::haveRight(self::$rightname, UPDATE),
                'can_global_purge'  => Session::haveRight(self::$rightname, PURGE),
            ]);
        }
    }

    public static function addPluginInfos(CommonDBTM $item)
    {
        Plugin::doHookFunction("infocom", $item);
    }

    /**
     * @param $itemtype
     **/
    public static function rawSearchOptionsToAdd($itemtype = null)
    {
        $specific_itemtype = '';
        $beforejoin        = [];

        switch ($itemtype) {
            case 'CartridgeItem':
                // Return the infocom linked to the license, not the template linked to the software
                $beforejoin        = ['table'      => 'glpi_cartridges',
                    'joinparams' => ['jointype' => 'child'],
                ];
                $specific_itemtype = 'Cartridge';
                break;

            case 'ConsumableItem':
                // Return the infocom linked to the license, not the template linked to the software
                $beforejoin        = ['table'      => 'glpi_consumables',
                    'joinparams' => ['jointype' => 'child'],
                ];
                $specific_itemtype = 'Consumable';
                break;
        }

        $joinparams        = ['jointype'          => 'itemtype_item',
            'specific_itemtype' => $specific_itemtype,
        ];
        $complexjoinparams = [];
        if (count($beforejoin)) {
            $complexjoinparams['beforejoin'][] = $beforejoin;
            $joinparams['beforejoin']          = $beforejoin;
        }
        $complexjoinparams['beforejoin'][] = ['table'      => 'glpi_infocoms',
            'joinparams' => $joinparams,
        ];

        $tab = [];

        $tab[] = [
            'id'                 => 'financial',
            'name'               => __('Financial and administrative information'),
        ];

        $tab[] = [
            'id'                 => '25',
            'table'              => 'glpi_infocoms',
            'field'              => 'immo_number',
            'name'               => __('Immobilization number'),
            'forcegroupby'       => true,
            'joinparams'         => $joinparams,
            'datatype'           => 'string',
        ];

        $tab[] = [
            'id'                 => '26',
            'table'              => 'glpi_infocoms',
            'field'              => 'order_number',
            'name'               => __('Order number'),
            'forcegroupby'       => true,
            'joinparams'         => $joinparams,
            'datatype'           => 'string',
        ];

        $tab[] = [
            'id'                 => '27',
            'table'              => 'glpi_infocoms',
            'field'              => 'delivery_number',
            'name'               => __('Delivery form'),
            'forcegroupby'       => true,
            'joinparams'         => $joinparams,
            'datatype'           => 'string',
        ];

        $tab[] = [
            'id'                 => '28',
            'table'              => 'glpi_infocoms',
            'field'              => 'bill',
            'name'               => __('Invoice number'),
            'forcegroupby'       => true,
            'joinparams'         => $joinparams,
            'datatype'           => 'string',
        ];

        $tab[] = [
            'id'                 => '37',
            'table'              => 'glpi_infocoms',
            'field'              => 'buy_date',
            'name'               => __('Date of purchase'),
            'datatype'           => 'date',
            'forcegroupby'       => true,
            'joinparams'         => $joinparams,
        ];

        $tab[] = [
            'id'                 => '38',
            'table'              => 'glpi_infocoms',
            'field'              => 'use_date',
            'name'               => __('Startup date'),
            'datatype'           => 'date',
            'forcegroupby'       => true,
            'joinparams'         => $joinparams,
        ];

        $tab[] = [
            'id'                 => '142',
            'table'              => 'glpi_infocoms',
            'field'              => 'delivery_date',
            'name'               => __('Delivery date'),
            'datatype'           => 'date',
            'forcegroupby'       => true,
            'joinparams'         => $joinparams,
        ];

        $tab[] = [
            'id'                 => '124',
            'table'              => 'glpi_infocoms',
            'field'              => 'order_date',
            'name'               => __('Order date'),
            'datatype'           => 'date',
            'forcegroupby'       => true,
            'joinparams'         => $joinparams,
        ];

        $tab[] = [
            'id'                 => '123',
            'table'              => 'glpi_infocoms',
            'field'              => 'warranty_date',
            'name'               => __('Start date of warranty'),
            'datatype'           => 'date',
            'forcegroupby'       => true,
            'joinparams'         => $joinparams,
        ];

        $tab[] = [
            'id'                 => '125',
            'table'              => 'glpi_infocoms',
            'field'              => 'inventory_date',
            'name'               => __('Date of last physical inventory'),
            'datatype'           => 'date',
            'forcegroupby'       => true,
            'joinparams'         => $joinparams,
        ];

        $tab[] = [
            'id'                 => '50',
            'table'              => 'glpi_budgets',
            'field'              => 'name',
            'datatype'           => 'dropdown',
            'name'               => Budget::getTypeName(1),
            'forcegroupby'       => true,
            'joinparams'         => $complexjoinparams,
        ];

        $tab[] = [
            'id'                 => '51',
            'table'              => 'glpi_infocoms',
            'field'              => 'warranty_duration',
            'name'               => __('Warranty duration'),
            'forcegroupby'       => true,
            'joinparams'         => $joinparams,
            'datatype'           => 'number',
            'unit'               => 'month',
            'max'                => '120',
            'toadd'              => [
                '-1'                 => __('Lifelong'),
            ],
        ];

        $tab[] = [
            'id'                 => '52',
            'table'              => 'glpi_infocoms',
            'field'              => 'warranty_info',
            'name'               => __('Warranty information'),
            'forcegroupby'       => true,
            'joinparams'         => $joinparams,
            'datatype'           => 'string',
        ];

        $tab[] = [
            'id'                 => '120',
            'table'              => 'glpi_infocoms',
            'field'              => 'end_warranty',
            'name'               => __('Warranty expiration date'),
            'datatype'           => 'date_delay',
            'datafields'         => [
                '1'                  => 'warranty_date',
                '2'                  => 'warranty_duration',
            ],
            'searchunit'         => 'MONTH',
            'delayunit'          => 'MONTH',
            'maybefuture'        => true,
            'forcegroupby'       => true,
            'massiveaction'      => false,
            'joinparams'         => $joinparams,
        ];

        $tab[] = [
            'id'                 => '53',
            'table'              => 'glpi_suppliers',
            'field'              => 'name',
            'datatype'           => 'dropdown',
            'name'               => Supplier::getTypeName(1),
            'forcegroupby'       => true,
            'joinparams'         => $complexjoinparams,
        ];

        $tab[] = [
            'id'                 => '54',
            'table'              => 'glpi_infocoms',
            'field'              => 'value',
            'name'               => _x('price', 'Value'),
            'datatype'           => 'decimal',
            'forcegroupby'       => true,
            'joinparams'         => $joinparams,
        ];

        $tab[] = [
            'id'                 => '55',
            'table'              => 'glpi_infocoms',
            'field'              => 'warranty_value',
            'name'               => __('Warranty extension value'),
            'datatype'           => 'decimal',
            'forcegroupby'       => true,
            'joinparams'         => $joinparams,
        ];

        $tab[] = [
            'id'                 => '56',
            'table'              => 'glpi_infocoms',
            'field'              => 'sink_time',
            'name'               => __('Amortization duration'),
            'forcegroupby'       => true,
            'joinparams'         => $joinparams,
            'datatype'           => 'number',
            'max'                => '15',
            'unit'               => 'year',
        ];

        $tab[] = [
            'id'                 => '57',
            'table'              => 'glpi_infocoms',
            'field'              => 'sink_type',
            'name'               => __('Amortization type'),
            'forcegroupby'       => true,
            'joinparams'         => $joinparams,
            'datatype'           => 'specific',
            'searchequalsonfield' => 'specific',
            'searchtype'         => ['equals', 'notequals'],
        ];

        $tab[] = [
            'id'                 => '58',
            'table'              => 'glpi_infocoms',
            'field'              => 'sink_coeff',
            'name'               => __('Amortization coefficient'),
            'forcegroupby'       => true,
            'joinparams'         => $joinparams,
            'datatype'           => 'decimal',
        ];

        $tab[] = [
            'id'                 => '59',
            'table'              => 'glpi_infocoms',
            'field'              => 'alert',
            'name'               => __('Email alarms'),
            'forcegroupby'       => true,
            'joinparams'         => $joinparams,
            'datatype'           => 'specific',
        ];

        $tab[] = [
            'id'                 => '122',
            'table'              => 'glpi_infocoms',
            'field'              => 'comment',
            'name'               => __('Comments on financial and administrative information'),
            'datatype'           => 'text',
            'forcegroupby'       => true,
            'joinparams'         => $joinparams,
        ];

        $tab[] = [
            'id'                 => '173',
            'table'              => 'glpi_businesscriticities',
            'field'              => 'completename',
            'name'               => _n('Business criticity', 'Business criticities', 1),
            'datatype'           => 'dropdown',
            'forcegroupby'       => true,
            'joinparams'         => $complexjoinparams,
        ];

        $tab[] = [
            'id'                 => '159',
            'table'              => 'glpi_infocoms',
            'field'              => 'decommission_date',
            'name'               => __('Decommission date'),
            'datatype'           => 'date',
            'maybefuture'        => true,
            'forcegroupby'       => true,
            'joinparams'         => $joinparams,
        ];

        return $tab;
    }


    public function rawSearchOptions()
    {
        $tab = [];

        $tab[] = [
            'id'                 => 'common',
            'name'               => __('Characteristics'),
        ];

        $tab[] = [
            'id'                 => '2',
            'table'              => $this->getTable(),
            'field'              => 'id',
            'name'               => __('ID'),
            'massiveaction'      => false,
            'datatype'           => 'number',
        ];

        $tab[] = [
            'id'                 => '4',
            'table'              => $this->getTable(),
            'field'              => 'buy_date',
            'name'               => __('Date of purchase'),
            'datatype'           => 'date',
        ];

        $tab[] = [
            'id'                 => '5',
            'table'              => $this->getTable(),
            'field'              => 'use_date',
            'name'               => __('Startup date'),
            'datatype'           => 'date',
        ];

        $tab[] = [
            'id'                 => '24',
            'table'              => $this->getTable(),
            'field'              => 'delivery_date',
            'name'               => __('Delivery date'),
            'datatype'           => 'date',
            'forcegroupby'       => true,
        ];

        $tab[] = [
            'id'                 => '23',
            'table'              => $this->getTable(),
            'field'              => 'order_date',
            'name'               => __('Order date'),
            'datatype'           => 'date',
            'forcegroupby'       => true,
        ];

        $tab[] = [
            'id'                 => '25',
            'table'              => $this->getTable(),
            'field'              => 'warranty_date',
            'name'               => __('Start date of warranty'),
            'datatype'           => 'date',
            'forcegroupby'       => true,
        ];

        $tab[] = [
            'id'                 => '27',
            'table'              => $this->getTable(),
            'field'              => 'inventory_date',
            'name'               => __('Date of last physical inventory'),
            'datatype'           => 'date',
            'forcegroupby'       => true,
        ];

        $tab[] = [
            'id'                 => '28',
            'table'              => $this->getTable(),
            'field'              => 'decommission_date',
            'name'               => __('Decommission date'),
            'maybefuture'        => true,
            'datatype'           => 'date',
            'forcegroupby'       => true,
        ];

        $tab[] = [
            'id'                 => '6',
            'table'              => $this->getTable(),
            'field'              => 'warranty_duration',
            'name'               => __('Warranty duration'),
            'datatype'           => 'number',
            'unit'               => 'month',
            'max'                => '120',
            'toadd'              => [
                '-1'                 => __('Lifelong'),
            ],
        ];

        $tab[] = [
            'id'                 => '7',
            'table'              => $this->getTable(),
            'field'              => 'warranty_info',
            'name'               => __('Warranty information'),
            'datatype'           => 'string',
        ];

        $tab[] = [
            'id'                 => '8',
            'table'              => $this->getTable(),
            'field'              => 'warranty_value',
            'name'               => __('Warranty extension value'),
            'datatype'           => 'decimal',
        ];

        $tab[] = [
            'id'                 => '9',
            'table'              => 'glpi_suppliers',
            'field'              => 'name',
            'name'               => Supplier::getTypeName(1),
            'datatype'           => 'dropdown',
        ];

        $tab[] = [
            'id'                 => '10',
            'table'              => $this->getTable(),
            'field'              => 'order_number',
            'name'               => __('Order number'),
            'datatype'           => 'string',
        ];

        $tab[] = [
            'id'                 => '11',
            'table'              => $this->getTable(),
            'field'              => 'delivery_number',
            'name'               => __('Delivery form'),
            'datatype'           => 'string',
        ];

        $tab[] = [
            'id'                 => '12',
            'table'              => $this->getTable(),
            'field'              => 'immo_number',
            'name'               => __('Immobilization number'),
            'datatype'           => 'string',
        ];

        $tab[] = [
            'id'                 => '13',
            'table'              => $this->getTable(),
            'field'              => 'value',
            'name'               => _x('price', 'Value'),
            'datatype'           => 'decimal',
        ];

        $tab[] = [
            'id'                 => '14',
            'table'              => $this->getTable(),
            'field'              => 'sink_time',
            'name'               => __('Amortization duration'),
            'datatype'           => 'number',
            'max'                => '15',
            'unit'               => 'year',
        ];

        $tab[] = [
            'id'                 => '15',
            'table'              => $this->getTable(),
            'field'              => 'sink_type',
            'name'               => __('Amortization type'),
            'datatype'           => 'specific',
            'searchtype'         => ['equals', 'notequals'],
        ];

        $tab[] = [
            'id'                 => '16',
            'table'              => $this->getTable(),
            'field'              => 'comment',
            'name'               => __('Comments'),
            'datatype'           => 'text',
        ];

        $tab[] = [
            'id'                 => '17',
            'table'              => $this->getTable(),
            'field'              => 'sink_coeff',
            'name'               => __('Amortization coefficient'),
            'datatype'           => 'decimal',
        ];

        $tab[] = [
            'id'                 => '18',
            'table'              => $this->getTable(),
            'field'              => 'bill',
            'name'               => __('Invoice number'),
            'datatype'           => 'string',
        ];

        $tab[] = [
            'id'                 => '19',
            'table'              => 'glpi_budgets',
            'field'              => 'name',
            'name'               => Budget::getTypeName(1),
            'datatype'           => 'itemlink',
        ];

        $tab[] = [
            'id'                 => '20',
            'table'              => $this->getTable(),
            'field'              => 'itemtype',
            'name'               => _n('Type', 'Types', 1),
            'datatype'           => 'itemtypename',
            'itemtype_list'      => 'infocom_types',
            'massiveaction'      => false,
        ];

        $tab[] = [
            'id'                 => '21',
            'table'              => $this->getTable(),
            'field'              => 'items_id',
            'name'               => __('ID'),
            'datatype'           => 'integer',
            'massiveaction'      => false,
        ];

        $tab[] = [
            'id'                 => '22',
            'table'              => $this->getTable(),
            'field'              => 'alert',
            'name'               => __('Alarms on financial and administrative information'),
            'datatype'           => 'integer',
        ];

        $tab[] = [
            'id'                 => '80',
            'table'              => 'glpi_entities',
            'field'              => 'completename',
            'name'               => Entity::getTypeName(1),
            'massiveaction'      => false,
            'datatype'           => 'dropdown',
        ];

        $tab[] = [
            'id'                 => '86',
            'table'              => $this->getTable(),
            'field'              => 'is_recursive',
            'name'               => __('Child entities'),
            'datatype'           => 'bool',
        ];

        $tab[] = [
            'id'                 => '173',
            'table'              => 'glpi_businesscriticities',
            'field'              => 'completename',
            'name'               => _n('Business criticity', 'Business criticities', 1),
            'datatype'           => 'dropdown',
        ];

        return $tab;
    }


    /**
     * Display debug information for infocom of current object
     **/
    public function showDebug()
    {

        $item = ['item_name'          => '',
            'warrantyexpiration' => '',
            'itemtype'           => $this->fields['itemtype'],
            'items_id'           => $this->fields['items_id'],
        ];

        $options['entities_id'] = $this->getEntityID();
        $options['items']       = [$item];
        NotificationEvent::debugEvent($this, $options);
    }


    /**
     * Get date using a begin date and a period in month
     *
     * @param string  $from          begin date
     * @param integer $addwarranty   period in months
     * @param integer $deletenotice  period in months of notice (default 0)
     * @param boolean $color         if show expire date in red color (false by default)
     * @param boolean $auto_renew
     * @param integer $periodicity   renewal periodicity in month if different from addwarranty
     *
     * @return string expiration date
     **/
    public static function getWarrantyExpir($from, $addwarranty, $deletenotice = 0, $color = false, $auto_renew = false, $periodicity = 0)
    {

        // Life warranty
        if (
            ($addwarranty == -1)
            && ($deletenotice == 0)
        ) {
            return __('Never');
        }

        if (empty($from)) {
            return "";
        }

        $periodicity = ($periodicity > 0) ? $periodicity : $addwarranty;

        // For notice calculation with periodicity, we need to find the current period
        if ($deletenotice > 0 && $periodicity > 0 && $periodicity != $addwarranty) {
            $current_time = strtotime($_SESSION['glpi_currenttime']);

            // Calculate the number of months elapsed since the beginning
            $current = new DateTime($_SESSION['glpi_currenttime']);
            $start = new DateTime($from);
            $interval = $start->diff($current);
            $months_elapsed = ($interval->y * 12) + $interval->m;

            // For TACIT contracts: renewal is based on addwarranty, not periodicity
            // For EXPRESS contracts: renewal is based on periodicity
            $renewal_period = $auto_renew ? $addwarranty : $periodicity;

            // Find which period we are in
            $current_period = floor($months_elapsed / $renewal_period);

            // Calculate the end of the current period
            $period_end_months = ($current_period + 1) * $renewal_period;

            // Do not exceed the total duration of the contract
            if ($period_end_months > $addwarranty && !$auto_renew) {
                $period_end_months = $addwarranty;
            }

            // The notice is X months before the end of the period (without subtracting 1 day)
            $notice_months = $period_end_months - $deletenotice;
            $notice_date = new DateTime($from);
            $notice_date->add(new DateInterval("P{$notice_months}M"));
            $timestamp = $notice_date->getTimestamp();

            // For TACIT contracts with notice: find the current or next notice
            if ($auto_renew) {
                // Calculate the end of the notice period (start + deletenotice)
                $notice_end_timestamp = strtotime(date("Y-m-d", $timestamp) . " +{$deletenotice} month");

                // If the notice period is not yet finished, we keep this date
                // Otherwise, we advance to the next future notice
                while ($notice_end_timestamp < $current_time) {
                    $notice_months += $renewal_period;
                    $notice_date = new DateTime($from);
                    $notice_date->add(new DateInterval("P{$notice_months}M"));
                    $timestamp = $notice_date->getTimestamp();
                    $notice_end_date = clone $notice_date;
                    $notice_end_date->add(new DateInterval("P{$deletenotice}M"));
                    $notice_end_timestamp = $notice_end_date->getTimestamp();
                }
            }
        } else {
            // Standard calculation
            if ($deletenotice > 0) {
                // For NOTICE: duration - notice (WITHOUT subtracting 1 day)
                $initial_notice_timestamp = strtotime("$from+$addwarranty month -$deletenotice month");

                // For TACIT contracts: calculate the next FUTURE notice
                if ($auto_renew && $periodicity > 0) {
                    $current_time = strtotime($_SESSION['glpi_currenttime']);

                    $start_date = new DateTime($from);
                    $first_notice_date = clone $start_date;
                    $first_notice_date->modify("+{$addwarranty} month");
                    $first_notice_date->modify("-{$deletenotice} month");

                    $timestamp = $first_notice_date->getTimestamp();

                    // Advance by periods until finding the current or next notice
                    // If we are in the notice period, we keep this date
                    while ($timestamp < $current_time) {
                        // Calculate the end of the notice period
                        $notice_end_date = clone $first_notice_date;
                        $notice_end_date->modify("+{$deletenotice} month");

                        // If the notice period is not yet finished, we stop
                        if ($notice_end_date->getTimestamp() >= $current_time) {
                            break;
                        }
                        // Otherwise we advance to the next period
                        $first_notice_date->modify("+{$periodicity} month");
                        $timestamp = $first_notice_date->getTimestamp();
                    }
                } else {
                    $timestamp = $initial_notice_timestamp;
                }
            } else {
                // For EXPIRATION (without notice)
                $expiration_date = new DateTime($from);
                $expiration_date->add(new DateInterval("P{$addwarranty}M"));
                $timestamp = $expiration_date->getTimestamp();
            }
        }

        if ($auto_renew && $periodicity > 0 && $deletenotice == 0) {
            // Renewal occurs every addwarranty months (initial duration)
            $renewal_period = ($periodicity != $addwarranty) ? $addwarranty : $periodicity;

            while ($timestamp < strtotime($_SESSION['glpi_currenttime'])) {
                $datetime = new DateTime();
                $datetime->setTimestamp($timestamp);
                $datetime->add(new DateInterval("P{$renewal_period}M"));
                $timestamp = $datetime->getTimestamp();
            }
        } elseif ($deletenotice == 0) {
            $datetime = new DateTime();
            $datetime->setTimestamp($timestamp);
            $datetime->sub(new DateInterval("P1D"));
            $timestamp = $datetime->getTimestamp();
        }

        $date = Html::convDate(date("Y-m-d", $timestamp));

        // Ensure we never return null, fallback to empty string if convDate returns null
        if ($date === null) {
            $date = '';
        }

        if ($color && ($timestamp < strtotime($_SESSION['glpi_currenttime']))) {
            return "<span class='red'>" . Html::convDate(date("Y-m-d", $timestamp)) . "</span>";
        }
        return Html::convDate(date("Y-m-d", $timestamp));
    }


    public static function getMassiveActionsForItemtype(
        array &$actions,
        $itemtype,
        $is_deleted = false,
        ?CommonDBTM $checkitem = null
    ) {

        $action_name = __CLASS__ . MassiveAction::CLASS_ACTION_SEPARATOR . 'activate';

        if (
            Infocom::canApplyOn($itemtype)
            && static::canCreate()
        ) {
            $actions[$action_name] = "<i class='fa-fw " . self::getIcon() . "'></i>" .
                                  __('Enable the financial and administrative information');
        }
    }


    public static function processMassiveActionsForOneItemtype(
        MassiveAction $ma,
        CommonDBTM $item,
        array $ids
    ) {

        switch ($ma->getAction()) {
            case 'activate':
                $ic = new self();
                if ($ic->canCreate()) {
                    $itemtype = $item->getType();
                    foreach ($ids as $key) {
                        if (!$ic->getFromDBforDevice($itemtype, $key)) {
                            $input = ['itemtype' => $itemtype,
                                'items_id' => $key,
                            ];
                            if ($ic->can(-1, CREATE, $input)) {
                                if ($ic->add($input)) {
                                    $ma->itemDone($item->getType(), $key, MassiveAction::ACTION_OK);
                                } else {
                                    $ma->itemDone($item->getType(), $key, MassiveAction::ACTION_KO);
                                    $ma->addMessage($ic->getErrorMessage(ERROR_ON_ACTION));
                                }
                            } else {
                                $ma->itemDone($item->getType(), $key, MassiveAction::ACTION_NORIGHT);
                                $ma->addMessage($ic->getErrorMessage(ERROR_RIGHT));
                            }
                        } else {
                            // Infocom already exists for this item, nothing to do.
                            $ma->itemDone($item->getType(), $key, MassiveAction::ACTION_OK);
                        }
                    }
                }
                return;
        }
        parent::processMassiveActionsForOneItemtype($ma, $item, $ids);
    }


    /**
     * @since 9.1.7
     * @see CommonDBChild::canUpdateItem()
     **/
    public function canUpdateItem()
    {
        return Session::haveRight(static::$rightname, UPDATE);
    }


    /**
     * @since 9.1.7
     * @see CommonDBChild::canPurgeItem()
     **/
    public function canPurgeItem()
    {
        return Session::haveRight(static::$rightname, PURGE);
    }


    /**
     * @since 9.1.7
     * @see CommonDBChild::canCreateItem()
     **/
    public function canCreateItem()
    {
        return Session::haveRight(static::$rightname, CREATE);
    }

    /**
     * Get item types
     *
     * @since 9.3.1
     *
     * @param array $where Where clause
     *
     * @return DBmysqlIterator
     */
    public static function getTypes($where)
    {
        /** @var \DBmysql $DB */
        global $DB;

        $types_iterator = $DB->request([
            'SELECT'          => 'itemtype',
            'DISTINCT'        => true,
            'FROM'            => 'glpi_infocoms',
            'WHERE'           => [
                'NOT'          => ['itemtype' => self::getExcludedTypes()],
            ] + $where,
            'ORDER'           => 'itemtype',
        ]);
        return $types_iterator;
    }


    /**
     * Get excluded itemtypes
     *
     * @since 9.3.1
     *
     * @return array
     */
    public static function getExcludedTypes()
    {
        return ['ConsumableItem', 'CartridgeItem'];
    }


    public static function getIcon()
    {
        return "fas fa-wallet";
    }
}
