<?php

/**
 * i-doit
 *
 * DAO: specific category for enclosures.
 *
 * @package     i-doit
 * @subpackage  CMDB_Categories
 * @author      Dennis Stücken <dstuecken@i-doit.org>
 * @copyright   synetics GmbH
 * @license     http://www.i-doit.com/license
 */
class isys_cmdb_dao_category_s_enclosure extends isys_cmdb_dao_category_specific
{
    /**
     * Number of maximum vertical slots.
     *
     * @var integer
     */
    const C__RACK__VERTICAL_SLOTS = 12;

    /**
     * Constant for ascending slot sorting.
     *
     * @var string
     */
    const C__SLOT_SORTING__ASC = 'asc';

    /**
     * Constant for descending slot sorting.
     *
     * @var string
     */
    const C__SLOT_SORTING__DESC = 'desc';

    /**
     * Category's name. Will be used for the identifier, constant, main table, and many more.
     *
     * @var  string
     */
    protected $m_category = 'enclosure';

    /**
     * @param   integer $p_obj_id
     *
     * @return  integer
     */
    public function get_count($p_obj_id = null)
    {
        if ($p_obj_id !== null) {
            $l_obj_id = (int)$p_obj_id;
        } else {
            $l_obj_id = (int)$this->m_object_id;
        }

        $l_sql = "SELECT COUNT(isys_catg_location_list__id) AS 'count' FROM isys_catg_location_list
			INNER JOIN isys_obj ON isys_obj__id = isys_catg_location_list__parentid
			WHERE TRUE ";

        if ($l_obj_id > 0) {
            $l_sql .= "AND (isys_catg_location_list__parentid = " . $this->convert_sql_id($l_obj_id) . ") ";
        }

        $l_sql .= "AND (isys_obj__status = " . $this->convert_sql_int(C__RECORD_STATUS__NORMAL) . ");";

        $l_data = $this->retrieve($l_sql)
            ->get_row();

        return $l_data["count"];
    }

    /**
     * Get data method, will retrieve category, object and object-type information while sorting by "position_in_room" field.
     *
     * @param   integer $p_category_data_id
     * @param   mixed   $p_obj_id
     * @param   string  $p_condition
     * @param   mixed   $p_filter
     * @param   integer $p_status
     *
     * @return  isys_component_dao_result
     * @author  Leonard Fischer <lfischer@i-doit.org>
     */
    public function get_data($p_category_data_id = null, $p_obj_id = null, $p_condition = '', $p_filter = null, $p_status = null)
    {
        $l_sql = 'SELECT *
			FROM isys_cats_enclosure_list
			LEFT JOIN isys_obj ON isys_obj__id = isys_cats_enclosure_list__isys_obj__id
			LEFT JOIN isys_obj_type ON isys_obj_type__id = isys_obj__isys_obj_type__id
			WHERE TRUE ' . $p_condition . $this->prepare_filter($p_filter);

        if ($p_obj_id !== null) {
            $l_sql .= $this->get_object_condition($p_obj_id);
        }

        if ($p_category_data_id !== null) {
            $l_sql .= ' AND isys_cats_enclosure_list__id = ' . $this->convert_sql_id($p_category_data_id);
        }

        if ($p_status !== null) {
            $l_sql .= ' AND isys_cats_enclosure_list__status = ' . $this->convert_sql_int($p_status);
        }

        $l_sql .= ' ORDER BY isys_cats_enclosure_list__position_in_room ASC;';

        return $this->retrieve($l_sql);
    }

    /**
     * Method for returning the properties.
     *
     * @return  array
     */
    protected function properties()
    {
        return [
            'vertical_slots_front' => array_replace_recursive(isys_cmdb_dao_category_pattern::int(), [
                C__PROPERTY__INFO     => [
                    C__PROPERTY__INFO__TITLE       => 'LC__CMDB__CATS__ENCLOSURE__VERTICAL_SLOTS_FRONTSIDE',
                    C__PROPERTY__INFO__DESCRIPTION => 'Vertical slots'
                ],
                C__PROPERTY__DATA     => [
                    C__PROPERTY__DATA__FIELD => 'isys_cats_enclosure_list__vertical_slots_front'
                ],
                C__PROPERTY__UI       => [
                    C__PROPERTY__UI__ID     => 'C__CATS__ENCLOSURE__VERTICAL_SLOTS_FRONT',
                    C__PROPERTY__UI__PARAMS => [
                        'p_bSort'           => false,
                        'p_bDbFieldNN'      => true,
                        'p_bInfoIconSpacer' => 0,
                        'p_strClass'        => 'input-block'
                    ]
                ],
                C__PROPERTY__PROVIDES => [
                    C__PROPERTY__PROVIDES__REPORT => false
                ]
            ]),
            'vertical_slots_rear'  => array_replace_recursive(isys_cmdb_dao_category_pattern::int(), [
                C__PROPERTY__INFO     => [
                    C__PROPERTY__INFO__TITLE       => 'LC__CMDB__CATS__ENCLOSURE__VERTICAL_SLOTS_BACKSIDE',
                    C__PROPERTY__INFO__DESCRIPTION => 'Vertical slots'
                ],
                C__PROPERTY__DATA     => [
                    C__PROPERTY__DATA__FIELD => 'isys_cats_enclosure_list__vertical_slots_rear'
                ],
                C__PROPERTY__UI       => [
                    C__PROPERTY__UI__ID     => 'C__CATS__ENCLOSURE__VERTICAL_SLOTS_REAR',
                    C__PROPERTY__UI__PARAMS => [
                        'p_bDbFieldNN'      => true,
                        'p_bInfoIconSpacer' => 0,
                        'p_strClass'        => 'input-block'
                    ]
                ],
                C__PROPERTY__PROVIDES => [
                    C__PROPERTY__PROVIDES__REPORT => false
                ]
            ]),
            'slot_sorting'         => array_replace_recursive(isys_cmdb_dao_category_pattern::dialog(), [
                C__PROPERTY__INFO     => [
                    C__PROPERTY__INFO__TITLE       => 'LC__CMDB__CATS__ENCLOSURE__SLOT_SORTING',
                    C__PROPERTY__INFO__DESCRIPTION => 'Height unit sorting'
                ],
                C__PROPERTY__DATA     => [
                    C__PROPERTY__DATA__TYPE  => C__TYPE__TEXT,
                    C__PROPERTY__DATA__FIELD => 'isys_cats_enclosure_list__slot_sorting'
                ],
                C__PROPERTY__UI       => [
                    C__PROPERTY__UI__ID      => 'C__CATS__ENCLOSURE__UNIT_SORTING',
                    C__PROPERTY__UI__DEFAULT => self::C__SLOT_SORTING__ASC,
                    C__PROPERTY__UI__PARAMS  => [
                        'p_bDbFieldNN'      => true,
                        'p_bInfoIconSpacer' => 0,
                        'p_strClass'        => 'input-block',
                        'p_arData'          => [
                            self::C__SLOT_SORTING__ASC  => 'LC__CMDB__SORTING__ASC',
                            self::C__SLOT_SORTING__DESC => 'LC__CMDB__SORTING__DESC'
                        ]
                    ]
                ],
                C__PROPERTY__PROVIDES => [
                    C__PROPERTY__PROVIDES__REPORT => false
                ]
            ]),
            'description'          => array_replace_recursive(isys_cmdb_dao_category_pattern::commentary(), [
                C__PROPERTY__INFO     => [
                    C__PROPERTY__INFO__TITLE       => 'LC__CMDB__LOGBOOK__DESCRIPTION',
                    C__PROPERTY__INFO__DESCRIPTION => 'Description'
                ],
                C__PROPERTY__DATA     => [
                    C__PROPERTY__DATA__FIELD => 'isys_cats_enclosure_list__description'
                ],
                C__PROPERTY__UI       => [
                    C__PROPERTY__UI__ID => 'C__CMDB__CAT__COMMENTARY_' . C__CMDB__CATEGORY__TYPE_SPECIFIC . defined_or_default('C__CATS__ENCLOSURE', 'C__CATS__ENCLOSURE')
                ],
            ])
        ];
    }

    /**
     * This save method is used, because we may have to detach objects before saving.
     *
     * @param   integer $p_cat_id
     * @param   array   $p_data
     *
     * @return  boolean
     * @author  Leonard Fischer <lfischer@i-doit.com>
     */
    public function save_data($p_cat_id, $p_data)
    {
        /*
         * The following lines are used for the case that we save a rack with for example 8 (front/rear) v-slots,
         * although there are still objects assigned between slot 9 and 12.
         */
        $l_positions = isys_cmdb_dao_category_g_location::instance($this->get_database_component())
            ->get_positions_in_rack($p_data['isys_obj__id']);

        if (is_countable($l_positions['assigned_units']) && count($l_positions['assigned_units'])) {
            foreach ($l_positions['assigned_units'] as $l_object) {
                if ($l_object['option'] == C__RACK_INSERTION__VERTICAL) {
                    if ($l_object['insertion'] == C__INSERTION__FRONT) {
                        if ($l_object['pos'] > $p_data['vertical_slots_front']) {
                            // This object is assigned to a not existing slot - Remove it.
                            isys_cmdb_dao_location::instance($this->get_database_component())
                                ->update_position($l_object['obj_id']);
                        }
                    } else {
                        if ($l_object['insertion'] == C__INSERTION__REAR) {
                            if ($l_object['pos'] > $p_data['vertical_slots_rear']) {
                                // This object is assigned to a not existing slot - Remove it.
                                isys_cmdb_dao_location::instance($this->get_database_component())
                                    ->update_position($l_object['obj_id']);
                            }
                        }
                    }
                }
            }
        }

        return parent::save_data($p_cat_id, $p_data);
    }

    /**
     * Method for preparing the necessary information of a rack, by receiving it's object ID and title.
     *
     * @param   integer $p_rack_obj_id
     * @param   string  $p_rack_obj_title
     *
     * @return  array
     * @author  Leonard Fischer <lfischer@i-doit.com>
     */
    public function prepare_rack_data($p_rack_obj_id, $p_rack_obj_title)
    {
        global $g_dirs;

        $l_cmdb_status = [];

        $language = isys_application::instance()->container->get('language');

        $l_rackobj_res = isys_cmdb_dao_location::instance($this->m_db)
            ->get_location($p_rack_obj_id, null);

        $l_rack_formfactor = isys_cmdb_dao_category_g_formfactor::instance($this->m_db)
            ->get_data(null, $p_rack_obj_id)
            ->get_row();

        $l_rack_enclosure = $this->get_data(null, $p_rack_obj_id)->get_row();

        $l_cmdb_res = isys_cmdb_dao_status::instance($this->m_db)->get_cmdb_status();

        while ($l_row = $l_cmdb_res->get_row()) {
            $l_cmdb_status[$l_row['isys_cmdb_status__id']] = [
                'color' => '#' . $l_row['isys_cmdb_status__color'],
                'title' => $language->get($l_row['isys_cmdb_status__title'])
            ];
        }

        $l_return = [
            'id'               => (int)$p_rack_obj_id,
            'title'            => $p_rack_obj_title,
            'slots'            => (int)$l_rack_formfactor['isys_catg_formfactor_list__rackunits'],
            'sorting'          => $l_rack_enclosure['isys_cats_enclosure_list__slot_sorting'] ?: self::C__SLOT_SORTING__ASC,
            'vslots_front'     => (int)$l_rack_enclosure['isys_cats_enclosure_list__vertical_slots_front'],
            'vslots_rear'      => (int)$l_rack_enclosure['isys_cats_enclosure_list__vertical_slots_rear'],
            'position_in_room' => (int)$l_rack_enclosure['isys_cats_enclosure_list__position_in_room'],
            'objects'          => []
        ];

        while ($l_rackobj_row = $l_rackobj_res->get_row()) {
            if (!$l_rackobj_row['isys_obj_type__show_in_rack']) {
                continue;
            }

            $l_icon = $l_rackobj_row['isys_obj_type__icon'];

            if (empty($l_icon)) {
                $l_icon = $g_dirs['images'] . 'dtree/page.gif';
            }

            if (strpos($l_icon, '/') === false) {
                $l_icon = $g_dirs['images'] . 'tree/' . $l_icon;
            }

            $l_chassis = $this->objtype_is_cats_assigned($l_rackobj_row['isys_obj_type__id'], defined_or_default('C__CATS__CHASSIS'));

            $objectFormFactor = isys_cmdb_dao_category_g_formfactor::instance($this->m_db)
                ->get_data(null, $l_rackobj_row['isys_obj__id'])
                ->get_row();

            $position = (int)$l_rackobj_row['isys_catg_location_list__pos'];
            $insertion = (int)$l_rackobj_row['isys_catg_location_list__insertion'];

            $l_return['objects'][$l_rackobj_row['isys_obj__id']] = [
                'id'          => $l_rackobj_row['isys_obj__id'],
                'title'       => isys_glob_htmlentities($l_rackobj_row['isys_obj__title']),
                'type'        => $language->get($l_rackobj_row['isys_obj_type__title']),
                'icon'        => $l_icon,
                'color'       => '#' . $l_rackobj_row['isys_obj_type__color'],
                'cmdb_color'  => $l_cmdb_status[$l_rackobj_row['isys_obj__isys_cmdb_status__id']]['color'],
                'cmdb_status' => $l_cmdb_status[$l_rackobj_row['isys_obj__isys_cmdb_status__id']]['title'],
                'height'      => $l_rackobj_row['isys_catg_formfactor_list__rackunits'] ?: 1,
                'rawHeight'   => (int) $l_rackobj_row['isys_catg_formfactor_list__rackunits'],
                'option'      => $l_rackobj_row['isys_catg_location_list__option'],
                'insertion'   => $position > 0 ? $insertion : null,
                'pos'         => $position > 0 ? $position : null,
                'isChassis'   => $l_chassis,
                'formfactor'  => $objectFormFactor ? $objectFormFactor['isys_catg_formfactor_type__title'] : $language->get('LC__UNIVERSAL__NOT_ASSIGNED'),
                'chassis'     => null
            ];

            if ($l_chassis) {
                $l_return['objects'][$l_rackobj_row['isys_obj__id']]['chassis'] = $this->prepare_chassis_data($l_rackobj_row['isys_obj__id']);
                // @see  ID-5201  If we have no filled array, we don't treat this object like an chassis.
                $l_return['objects'][$l_rackobj_row['isys_obj__id']]['isChassis'] = !empty($l_return['objects'][$l_rackobj_row['isys_obj__id']]['chassis']);
            }
        }

        return $l_return;
    }

    /**
     * Method for preparing chassis data.
     *
     * @param integer $p_chassis_object_id
     *
     * @return  array
     */
    public function prepare_chassis_data($p_chassis_object_id)
    {
        $l_chassis_doa = isys_cmdb_dao_category_s_chassis_slot::instance($this->m_db);
        $l_chassis_view_dao = isys_cmdb_dao_category_s_chassis_view::instance($this->m_db);

        $l_chassis_row = $l_chassis_view_dao->get_data(null, $p_chassis_object_id)
            ->get_row();
        $l_slots_res = $l_chassis_doa->get_data(null, $p_chassis_object_id);

        if ($l_slots_res->num_rows() > 0) {
            $frontSlotPositions = [];
            $rearSlotPositions = [];

            while ($l_slot_row = $l_slots_res->get_row()) {
                // @see  ID-5201  Only cast the x and y values to int, if they are not null.
                $slotDefinition = [
                    'id'     => $l_slot_row['isys_cats_chassis_slot_list__id'],
                    'title'  => isys_glob_htmlentities($l_slot_row['isys_cats_chassis_slot_list__title']),
                    'x_from' => $l_slot_row['isys_cats_chassis_slot_list__x_from'] !== null ? (int)$l_slot_row['isys_cats_chassis_slot_list__x_from'] : null,
                    'x_to'   => $l_slot_row['isys_cats_chassis_slot_list__x_to'] !== null ? (int)$l_slot_row['isys_cats_chassis_slot_list__x_to'] : null,
                    'y_from' => $l_slot_row['isys_cats_chassis_slot_list__y_from'] !== null ? (int)$l_slot_row['isys_cats_chassis_slot_list__y_from'] : null,
                    'y_to'   => $l_slot_row['isys_cats_chassis_slot_list__y_to'] !== null ? (int)$l_slot_row['isys_cats_chassis_slot_list__y_to'] : null,
                ];

                // @see  ID-5201  Add the slots front/rear specific.
                if ($l_slot_row['isys_cats_chassis_slot_list__insertion'] == C__INSERTION__FRONT) {
                    $frontSlotPositions[] = $slotDefinition;
                } else {
                    $rearSlotPositions[] = $slotDefinition;
                }
            }

            // @see  ID-5201  Add rear specific data (including devices).
            return [
                'x'       => (int)$l_chassis_row['isys_cats_chassis_view_list__front_width'],
                'y'       => (int)$l_chassis_row['isys_cats_chassis_view_list__front_height'],
                'devices' => $l_chassis_view_dao->process_matrix_devices($p_chassis_object_id, C__INSERTION__FRONT),
                'matrix'  => $l_chassis_view_dao::process_matrix(
                    $l_chassis_row['isys_cats_chassis_view_list__front_width'],
                    $l_chassis_row['isys_cats_chassis_view_list__front_height'],
                    $frontSlotPositions
                ),
                'xRear'       => (int)$l_chassis_row['isys_cats_chassis_view_list__rear_width'],
                'yRear'       => (int)$l_chassis_row['isys_cats_chassis_view_list__rear_height'],
                'devicesRear' => $l_chassis_view_dao->process_matrix_devices($p_chassis_object_id, C__INSERTION__REAR),
                'matrixRear' => $l_chassis_view_dao::process_matrix(
                    $l_chassis_row['isys_cats_chassis_view_list__rear_width'],
                    $l_chassis_row['isys_cats_chassis_view_list__rear_height'],
                    $rearSlotPositions
                )
            ];
        }

        return [];
    }

    /**
     * Method for saving the position of one or more racks inside a locataion.
     * We only need the key (position) and value (rack obj-id).
     *
     * @param   array $p_position
     *
     * @return  boolean
     * @author  Leonard Fischer <lfischer@i-doit.com>
     */
    public function save_position_in_location(array $p_position = [])
    {
        if (count($p_position) > 0) {
            foreach ($p_position as $l_pos => $l_rack_obj_id) {
                $l_sql = 'UPDATE isys_cats_enclosure_list
					SET isys_cats_enclosure_list__position_in_room = ' . $this->convert_sql_int($l_pos) . '
					WHERE  isys_cats_enclosure_list__isys_obj__id = ' . $this->convert_sql_id($l_rack_obj_id) . ';';

                if (!($this->update($l_sql) && $this->apply_update())) {
                    return false;
                }
            }
        }

        return true;
    }

    /**
     * Method for retrieving the RU value of a rack and a certain position (the value changes according to the slot sorting).
     *
     * @param integer $p_rack_object_id
     * @param string  $p_segment_title
     * @param integer $p_position
     *
     * @return string
     */
    public function get_segment_object_name($p_rack_object_id, $p_segment_title, $p_position = null)
    {
        $l_pos = '';
        $l_sql = 'SELECT isys_catg_formfactor_list__rackunits, isys_cats_enclosure_list__slot_sorting FROM isys_obj
            LEFT JOIN isys_catg_formfactor_list ON isys_catg_formfactor_list__isys_obj__id = isys_obj__id
            LEFT JOIN isys_cats_enclosure_list ON isys_cats_enclosure_list__isys_obj__id = isys_obj__id
            WHERE isys_obj__id = ' . $this->convert_sql_id($p_rack_object_id) . '
            LIMIT 1;';

        $l_data = $this->retrieve($l_sql)
            ->get_row();

        if ($p_position !== null) {
            if ($l_data['isys_cats_enclosure_list__slot_sorting'] === self::C__SLOT_SORTING__DESC) {
                $l_position = $l_data['isys_catg_formfactor_list__rackunits'] - ($p_position - 1);
            } else {
                $l_position = $p_position;
            }

            $l_pos = isys_application::instance()->container->get('language')
                    ->get_in_text(' LC_UNIVERSAL__IN LC__CMDB__CATG__RACKUNITS_ABBR') . substr('0' . $l_position, -2);
        }

        return $p_segment_title . isys_application::instance()->container->get('language')
                ->get_in_text(' LC_UNIVERSAL__IN ') . $this->obj_get_title_by_id_as_string($p_rack_object_id) . $l_pos;
    }
}
