<?php

/**
 * i-doit
 *
 * Popup class for Dialog+ Boxes
 *
 * @package     i-doit
 * @subpackage  Popups
 * @author      Van Quyen Hoang <qhoang@i-doit.org>
 * @author      Leonard Fischer <lfischer@i-doit.org>
 * @version     0.9
 * @copyright   synetics GmbH
 * @license     http://www.i-doit.com/license
 */
class isys_popup_connection extends isys_component_popup
{
    /**
     * Handles SMARTY request for dialog plus lists and builds the list base on the specified table.
     *
     * @param   isys_component_template & $p_tplclass
     * @param   array                   $p_params
     *
     * @return  string
     */
    public function handle_smarty_include(isys_component_template &$p_tplclass, $p_params)
    {
        global $g_dirs;

        $l_button_options = [
            'id'                => $p_params['name'],
            'icon'              => $g_dirs['images'] . 'icons/silk/disconnect.png',
            'p_bInfoIconSpacer' => 0,
            'p_onClick'         => $this->process_overlay('', '80%', '90%', [C__CMDB__GET__OBJECT => $p_params[C__CMDB__GET__OBJECT]], 'popup_commentary', '770px', '350px'),
            'type'              => 'button',
            'p_strTitle'        => $this->language->get('LC__CABLE_CONNECTION__POPUP_CONNECTION_TITLE'),
            'p_strValue'        => $this->language->get('LC__CABLE_CONNECTION__POPUP_CONNECTION')
        ];

        return isys_factory::get_instance('isys_smarty_plugin_f_button')
            ->navigation_edit($this->template, $l_button_options);
    }

    /**
     * Method for handling the module request.
     *
     * @param   isys_module_request     $p_modreq
     *
     * @return void
     * @throws \idoit\Exception\JsonException
     */
    public function &handle_module_request(isys_module_request $p_modreq)
    {
        // Unpack popup request.
        $l_params = isys_format_json::decode(base64_decode($_POST['params']), true);
        $l_local_object_id = $l_params[C__CMDB__GET__OBJECT];

        $l_rules = [
            'C__CONNECTION_POPUP__LOCAL_OBJECT'       => [
                isys_popup_browser_object_ng::C__CAT_FILTER       => 'C__CATG__CABLING;C__CATG__CONNECTOR;C__CATG__UNIVERSAL_INTERFACE',
                'p_strClass'                                      => 'input-block',
                'p_bInfoIconSpacer'                               => 0,
                'disableInputGroup'                               => true,
                isys_popup_browser_object_ng::C__CALLBACK__ACCEPT => "$('C__CONNECTION_POPUP__LOCAL').fire('update:objectSelection');",
                isys_popup_browser_object_ng::C__CALLBACK__DETACH => "$('C__CONNECTION_POPUP__LOCAL').fire('reset:objectSelection');",
                'p_strSelectedID'                                 => $l_local_object_id
            ],
            'C__CONNECTION_POPUP__DESTINATION_OBJECT' => [
                isys_popup_browser_object_ng::C__CAT_FILTER       => 'C__CATG__CABLING;C__CATG__CONNECTOR;C__CATG__UNIVERSAL_INTERFACE',
                'p_strClass'                                      => 'input-block',
                'p_bInfoIconSpacer'                               => 0,
                'disableInputGroup'                               => true,
                isys_popup_browser_object_ng::C__CALLBACK__ACCEPT => "$('C__CONNECTION_POPUP__DESTINATION').fire('update:objectSelection');",
                isys_popup_browser_object_ng::C__CALLBACK__DETACH => "$('C__CONNECTION_POPUP__DESTINATION').fire('reset:objectSelection');"
            ]
        ];

        // Display the dialog template and return it.
        $this->template
            ->activate_editmode()
            ->assign('ajaxURL', isys_helper_link::create_url([C__CMDB__GET__POPUP => 'connection', C__GET__AJAX_REQUEST => 'handle_ajax_request'], true))
            ->assign('local_object_id', $l_local_object_id)
            ->assign('automatic_cabling', !!isys_usersettings::get('cmdb.connection.automatic-cabling', 0))
            ->assign('isCablingActive', isys_module_manager::instance()->is_active('cabling'))
            ->assign('cablingUrl', rtrim(isys_helper_link::get_base(), '/') . '/cabling/visualization')
            ->smarty_tom_add_rules('tom.popup.visualization', $l_rules)
            ->display(dirname(__DIR__) . '/templates/popup/popup_connection.tpl');
        die;
    }

    public function handle_ajax_request()
    {
        $l_return = [
            'success' => true,
            'data'    => null,
            'message' => ''
        ];

        header('Content-type: application/json');

        switch ($_POST['func']) {
            case 'setAutomaticCableDefault':
                isys_usersettings::set('cmdb.connection.automatic-cabling', $_POST['value']);
                break;
            case 'loadConnectorData':
                $l_dao = isys_cmdb_dao_category_g_connector::instance($this->database);
                $l_res = $l_dao->get_data(
                    null,
                    $_POST['id'],
                    '',
                    null,
                    C__RECORD_STATUS__NORMAL,
                    'isys_catg_connector_list__title',
                    'ASC'
                );
                $l_raw_result = $l_already_processed = [];
                $l_empty = isys_tenantsettings::get('gui.empty_value', '-');

                $l_return['data'] = ['in' => [], 'out' => []];

                while ($l_row = $l_res->get_row()) {
                    $l_result = [
                        'id'           => $l_row['isys_catg_connector_list__id'],
                        'title'        => $l_row['isys_catg_connector_list__title'],
                        'inOut'        => $l_row['isys_catg_connector_list__type'],
                        'siblingId'    => $l_row['isys_catg_connector_list__isys_catg_connector_list__id'],
                        'objId'        => $l_row['isys_obj__id'],
                        'objTitle'     => $l_row['isys_obj__title'],
                        'objTypeTitle' => $this->language->get($l_row['object_type'])
                    ];

                    // Get some additional data for the connected item.
                    if ($l_row['con_connector'] > 0) {
                        $l_sql = 'SELECT isys_obj__id, isys_obj__title, isys_obj_type__title, isys_catg_connector_list__type
                            FROM isys_obj
                            INNER JOIN isys_obj_type ON isys_obj_type__id = isys_obj__isys_obj_type__id
                            INNER JOIN isys_catg_connector_list ON isys_catg_connector_list__isys_obj__id = isys_obj__id
                            WHERE isys_catg_connector_list__id = ' . $l_dao->convert_sql_id($l_row['con_connector']) . ';';

                        $l_conn_row = $l_dao->retrieve($l_sql)
                            ->get_row();

                        $l_result['cableId'] = $l_row['cable_id'];
                        $l_result['cableTitle'] = $l_row['cable_title'];
                        $l_result['connectorId'] = $l_row['con_connector'];
                        $l_result['connectorTitle'] = $l_row['connector_name'];
                        $l_result['connectorInOut'] = $l_conn_row['isys_catg_connector_list__type'];
                        $l_result['connectorObjId'] = $l_conn_row['isys_obj__id'];
                        $l_result['connectorObjTitle'] = $l_conn_row['isys_obj__title'];
                        $l_result['connectorObjTypeTitle'] = $this->language->get($l_conn_row['isys_obj_type__title']);
                    }

                    $l_raw_result[$l_row['isys_catg_connector_list__id']] = $l_result;
                }

                foreach ($l_raw_result as $id => $l_result) {
                    if (in_array($l_result['id'], $l_already_processed)) {
                        continue;
                    }

                    $l_already_processed[] = $l_result['id'];

                    if ($l_result['inOut'] == C__CONNECTOR__INPUT) {
                        $l_return['data']['in'][] = $l_result;

                        if (isset($l_raw_result[$l_result['siblingId']])) {
                            $l_return['data']['out'][] = $l_raw_result[$l_result['siblingId']];

                            $l_already_processed[] = $l_result['siblingId'];
                            continue;
                        } else {
                            $l_return['data']['out'][] = [
                                'id'        => null,
                                'title'     => $l_empty,
                                'inOut'     => C__CONNECTOR__OUTPUT,
                                'siblingId' => $id,
                            ];
                            continue;
                        }
                    }

                    if ($l_result['inOut'] == C__CONNECTOR__OUTPUT) {
                        $l_return['data']['out'][] = $l_result;

                        if (isset($l_raw_result[$l_result['siblingId']])) {
                            $l_return['data']['in'][] = $l_raw_result[$l_result['siblingId']];

                            $l_already_processed[] = $l_result['siblingId'];
                            continue;
                        } else {
                            $l_return['data']['in'][] = [
                                'id'        => null,
                                'title'     => $l_empty,
                                'inOut'     => C__CONNECTOR__INPUT,
                                'siblingId' => $id,
                            ];
                            continue;
                        }
                    }
                }
                break;

            case 'connectConnectors':
                $l_return_connector = !!($_POST['returnConnector'] ?: 0);
                $l_local_connector = array_filter(explode(',', $_POST['a']));
                $l_destination_connector = array_filter(explode(',', $_POST['b']));
                $l_cables = array_filter(explode(',', $_POST['cables']));
                $l_counter = count($l_local_connector);

                if (empty($l_local_connector) || empty($l_destination_connector)) {
                    throw new Exception('At least two connectors needed to connect!');
                }

                $l_dao = isys_cmdb_dao_cable_connection::instance($this->database);
                $l_dao_connector = isys_cmdb_dao_category_g_connector::instance($this->database);

                for ($i = 0;$i < $l_counter;$i++) {
                    $l_old_cable_object_id = $l_dao->get_cable_object_id_by_connection_id($l_local_connector[$i]) ?: 0;

                    // Remove the current cable connection of the local and destination controller.
                    $this->detach_cable($l_local_connector[$i]);
                    $this->detach_cable($l_destination_connector[$i]);

                    $l_cable_object_id = $l_cables[$i];

                    if (empty($l_cable_object_id)) {
                        $l_cable_object_id = isys_cmdb_dao_cable_connection::add_cable();
                    }

                    $l_cable_con = $this->attach_cable($l_cable_object_id);

                    if (!$l_dao->save_connection($l_local_connector[$i], $l_destination_connector[$i], $l_cable_con, $l_local_connector[$i])) {
                        $l_return['data'][] = false;
                    } else {
                        $this->write_logbook(
                            $l_dao->retrieve('SELECT isys_catg_connector_list__isys_obj__id FROM isys_catg_connector_list WHERE isys_catg_connector_list__id = ' .
                            $l_dao->convert_sql_id($l_local_connector[$i]))
                            ->get_row_value('isys_catg_connector_list__isys_obj__id'),
                            ['isys_cmdb_dao_category_g_connector::cable_connection' => ['from' => $l_local_connector[$i], 'to' => $l_cable_con]],
                            $this->language->get('LC__CABLE_CONNECTION__POPUP_CONNECTION_SWITCHED_CABLE', [$l_old_cable_object_id, $l_cable_object_id])
                        );

                        if ($l_return_connector) {
                            $l_destination_data = $l_dao_connector->get_data($l_destination_connector[$i])
                                ->get_row();

                            $l_return['data'][] = [
                                'objId'      => $l_destination_data['isys_obj__id'],
                                'objTitle'   => $l_destination_data['isys_obj__title'],
                                'connId'     => $l_destination_connector[$i],
                                'connTitle'  => $l_destination_data['isys_catg_connector_list__title'],
                                'cableId'    => $l_destination_data['cable_object'],
                                'cableTitle' => $l_destination_data['cable_title']
                            ];
                        } else {
                            $l_return['data'][] = true;
                        }
                    }
                }
                break;

            case 'detachConnector':
                $l_dao = isys_cmdb_dao_cable_connection::instance($this->database);

                $l_connectors = array_filter(explode(',', $_POST['connector']));

                foreach ($l_connectors as $l_connector) {
                    $l_connection_id = $l_dao->get_cable_connection_id_by_connector_id((int)$l_connector);
                    $l_old_cable = $l_dao->get_cable_object_id_by_connection_id((int)$l_connector);

                    $this->write_logbook(
                        $l_dao->retrieve('SELECT isys_catg_connector_list__isys_obj__id FROM isys_catg_connector_list WHERE isys_catg_connector_list__id = ' .
                        $l_dao->convert_sql_id($l_connector))
                        ->get_row_value('isys_catg_connector_list__isys_obj__id'),
                        ['isys_cmdb_dao_category_g_connector::cable_connection' => ['from' => $l_connection_id, 'to' => null]],
                        $this->language->get('LC__CABLE_CONNECTION__POPUP_CONNECTION_REMOVED_CABLE', [$l_old_cable])
                    );

                    $l_return['data'][] = $this->detach_cable($l_connector);
                }
                break;

            case 'loadCablePopupTemplate':
                $l_rules = $l_cables = [];
                $l_cableIDs = array_filter(explode(',', $_POST['cables']));
                $l_connectorLocalIDs = array_filter(explode(',', $_POST['connectorsA']));
                $l_connectorDestinationIDs = array_filter(explode(',', $_POST['connectorsB']));
                $l_cable_dao = isys_cmdb_dao_category_g_cable::instance($this->database);

                if (is_array($l_cableIDs) && count($l_cableIDs)) {
                    foreach ($l_cableIDs as $l_cable) {
                        $l_rules['C__CONNECTION_POPUP__CABLE_' . $l_cable] = [
                            isys_popup_browser_object_ng::C__CAT_FILTER => 'C__CATG__CABLE;C__CATG__CABLE_CONNECTION',
                            'p_strClass'                                => 'input-block',
                            'p_bInfoIconSpacer'                         => 0,
                            'p_strValue'                                => $l_cable
                        ];

                        $l_sql = 'SELECT isys_catg_connector_list__id AS id, isys_obj_type__title AS objTypeTitle, isys_obj__title AS objTitle, isys_obj__id AS objId, isys_catg_connector_list__title AS connTitle
                            FROM isys_cable_connection
                            LEFT JOIN isys_catg_connector_list ON isys_catg_connector_list__isys_cable_connection__id = isys_cable_connection__id
                            INNER JOIN isys_obj ON isys_obj__id = isys_catg_connector_list__isys_obj__id
                            INNER JOIN isys_obj_type ON isys_obj_type__id = isys_obj__isys_obj_type__id
                            WHERE isys_cable_connection__isys_obj__id = ' . $l_cable_dao->convert_sql_id($l_cable) . ';';

                        $l_res = $l_cable_dao->retrieve($l_sql);

                        $l_cables[] = [
                            'id'    => $l_cable,
                            'connA' => $l_res->get_row(),
                            'connB' => $l_res->get_row()
                        ];
                    }
                } elseif (is_array($l_connectorLocalIDs) && count($l_connectorLocalIDs) && is_array($l_connectorDestinationIDs) && count($l_connectorDestinationIDs)) {
                    foreach ($l_connectorLocalIDs as $i => $l_local) {
                        $l_sql = 'SELECT isys_catg_connector_list__id AS id, isys_obj_type__title AS objTypeTitle, isys_obj__title AS objTitle, isys_obj__id AS objId, isys_catg_connector_list__title AS connTitle, isys_cable_connection__isys_obj__id AS cableObjId
                            FROM isys_catg_connector_list
                            INNER JOIN isys_obj ON isys_obj__id = isys_catg_connector_list__isys_obj__id
                            INNER JOIN isys_obj_type ON isys_obj_type__id = isys_obj__isys_obj_type__id
                            LEFT JOIN isys_cable_connection ON isys_cable_connection__id = isys_catg_connector_list__isys_cable_connection__id
                            WHERE isys_catg_connector_list__id = ' . $l_cable_dao->convert_sql_id($l_connectorLocalIDs[$i]) . ';';

                        $l_sql2 = 'SELECT isys_catg_connector_list__id AS id, isys_obj_type__title AS objTypeTitle, isys_obj__title AS objTitle, isys_obj__id AS objId, isys_catg_connector_list__title AS connTitle, isys_cable_connection__isys_obj__id AS cableObjId
                            FROM isys_catg_connector_list
                            INNER JOIN isys_obj ON isys_obj__id = isys_catg_connector_list__isys_obj__id
                            INNER JOIN isys_obj_type ON isys_obj_type__id = isys_obj__isys_obj_type__id
                            LEFT JOIN isys_cable_connection ON isys_cable_connection__id = isys_catg_connector_list__isys_cable_connection__id
                            WHERE isys_catg_connector_list__id = ' . $l_cable_dao->convert_sql_id($l_connectorDestinationIDs[$i]) . ';';

                        $l_conn_a = $l_cable_dao->retrieve($l_sql)
                            ->get_row();
                        $l_conn_b = $l_cable_dao->retrieve($l_sql2)
                            ->get_row();

                        $l_cable_id = ($l_conn_a['cableObjId'] > 0 ? $l_conn_a['cableObjId'] : ($l_conn_b['cableObjId'] > 0 ? $l_conn_b['cableObjId'] : uniqid()));

                        $l_cables[] = [
                            'id'    => $l_cable_id,
                            'connA' => $l_conn_a,
                            'connB' => $l_conn_b
                        ];

                        $l_rules['C__CONNECTION_POPUP__CABLE_' . $l_cable_id] = [
                            isys_popup_browser_object_ng::C__CAT_FILTER       => 'C__CATG__CABLE;C__CATG__CABLE_CONNECTION',
                            'p_strClass'                                      => 'input-block',
                            'p_bInfoIconSpacer'                               => 0,
                            'p_strPlaceholder'                                => $this->language->get('LC__CABLE_CONNECTION__CREATE_AUTOMATICALLY'),
                            'p_strValue'                                      => (is_numeric($l_cable_id) ? $l_cable_id : 0),
                            isys_popup_browser_object_ng::C__CALLBACK__DETACH => "$('C__CONNECTION_POPUP__CABLE_" . $l_cable_id .
                                "__VIEW').writeAttribute('data-last-value', '" . $this->language->get('LC__CABLE_CONNECTION__CREATE_AUTOMATICALLY') . "').setValue('" . $this->language->get('LC__CABLE_CONNECTION__CREATE_AUTOMATICALLY') . "');"
                        ];
                    }
                }

                $l_return['data'] = $this->template->activate_editmode()
                    ->assign('cables', $l_cables)
                    ->smarty_tom_add_rules('tom.popup.visualization', $l_rules)
                    ->fetch(dirname(__DIR__) . '/templates/popup/popup_connection_cables.tpl');
                break;

            case 'saveCablePopupData':
                $l_changes = isys_format_json::decode($_POST['changes']);
                $l_dao = isys_cmdb_dao_cable_connection::instance($this->database);

                foreach ($l_changes as $l_change) {
                    $l_conn_a = (int)$l_change['connA'];
                    $l_conn_a_object_id = (int)$l_change['connAObjId'];
                    $l_conn_b = (int)$l_change['connB'];
                    $l_conn_b_object_id = (int)$l_change['connBObjId'];
                    $l_old_cable = (int)$l_change['from'];
                    $l_new_cable = (int)$l_change['to'];
                    $l_old_cable_connection = null;

                    // Remove the current cable connection of the local and destination controller.
                    if ($l_conn_a > 0) {
                        $this->detach_cable($l_conn_a);
                    }

                    if ($l_conn_b > 0) {
                        $this->detach_cable($l_conn_b);
                    }

                    if ($l_new_cable > 0 && $l_conn_a > 0 && $l_conn_b > 0) {
                        $l_cable_con = $this->attach_cable($l_new_cable);

                        if (!$l_dao->save_connection($l_conn_a, $l_conn_b, $l_cable_con)) {
                            $this->write_logbook(
                                $l_conn_a_object_id,
                                ['isys_cmdb_dao_category_g_connector::cable_connection' => ['from' => $l_old_cable_connection, 'to' => $l_cable_con]],
                                $this->language->get('LC__CABLE_CONNECTION__POPUP_CONNECTION_SWITCHED_CABLE', [$l_old_cable, $l_new_cable])
                            )
                                ->write_logbook(
                                    $l_conn_b_object_id,
                                    serialize(['isys_cmdb_dao_category_g_connector::cable_connection' => ['from' => $l_old_cable_connection, 'to' => $l_cable_con]]),
                                    $this->language->get('LC__CABLE_CONNECTION__POPUP_CONNECTION_SWITCHED_CABLE', [$l_old_cable, $l_new_cable])
                                );

                            $l_return['data'][] = false;
                        } else {
                            $l_return['data'][] = true;
                        }
                    } else {
                        $this->write_logbook(
                            $l_conn_a_object_id,
                            ['isys_cmdb_dao_category_g_connector::cable_connection' => ['from' => $l_old_cable_connection, 'to' => null]],
                            $this->language->get('LC__CABLE_CONNECTION__POPUP_CONNECTION_REMOVED_CABLE', [$l_old_cable])
                        )
                            ->write_logbook(
                                $l_conn_b_object_id,
                                ['isys_cmdb_dao_category_g_connector::cable_connection' => ['from' => $l_old_cable_connection, 'to' => null]],
                                $this->language->get('LC__CABLE_CONNECTION__POPUP_CONNECTION_REMOVED_CABLE', [$l_old_cable])
                            );

                        $l_return['data'][] = true;
                    }
                }
                break;
        }

        echo isys_format_json::encode($l_return);
        die;
    }

    /**
     * Method for detaching a cable connection.
     *
     * @param integer $l_connection_id
     *
     * @return boolean
     * @throws isys_exception_database
     */
    private function detach_cable($l_connection_id)
    {
        $l_dao = isys_cmdb_dao_cable_connection::instance($this->database);

        $l_old_cable_connection = $l_dao->get_cable_connection_id_by_connector_id($l_connection_id);

        if (isys_tenantsettings::get('cmdb.cable.change-cmdb-status-on-detach', 1)) {
            $l_cable_object_id = $l_dao->get_cable_object_id_by_connection_id($l_old_cable_connection);
            $l_dao->set_object_cmdb_status($l_cable_object_id, defined_or_default('C__CMDB_STATUS__INOPERATIVE'));
        }

        return $l_dao->delete_cable_connection($l_old_cable_connection);
    }

    /**
     * Method for creating a cable connection.
     *
     * @param integer $p_cable_obj_id
     *
     * @return mixed
     */
    private function attach_cable($p_cable_obj_id)
    {
        $l_dao = isys_cmdb_dao_cable_connection::instance($this->database);

        if (isys_tenantsettings::get('cmdb.cable.change-cmdb-status-on-attach', 1)) {
            $l_dao->set_object_cmdb_status($p_cable_obj_id, defined_or_default('C__CMDB_STATUS__IN_OPERATION'));
        }

        return $l_dao->add_cable_connection($p_cable_obj_id);
    }

    /**
     * Method for writing a logbook entry.
     *
     * @param integer $p_object_id
     * @param array   $p_changes
     * @param string  $p_comment
     *
     * @return $this
     * @throws isys_exception_dao
     * @throws isys_exception_database
     */
    private function write_logbook($p_object_id, $p_changes, $p_comment)
    {
        $l_dao = isys_cmdb_dao::instance($this->database);

        isys_component_dao_logbook::instance($this->database)
            ->set_entry(
                'C__LOGBOOK_EVENT__CATEGORY_PURGED',
                '',
                null,
                defined_or_default('C__LOGBOOK__ALERT_LEVEL__0', 1),
                $p_object_id,
                $l_dao->get_obj_name_by_id_as_string($p_object_id),
                $l_dao->get_obj_type_name_by_obj_id($p_object_id),
                'LC__CMDB__CATG__CONNECTORS',
                defined_or_default('C__LOGBOOK_SOURCE__USER'),
                serialize($p_changes),
                $p_comment
            );

        return $this;
    }
}
