/*
 * Copyright (C) 2019 by Sukchan Lee <acetcom@gmail.com>
 *
 * This file is part of Open5GS.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero 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/>.
 */

#include "test-common.h"

ogs_pkbuf_t *test_s2b_build_create_session_request(
        uint8_t type, test_sess_t *sess, bool handover_ind)
{
    int rv;
    ogs_session_t *session = NULL;
    test_ue_t *test_ue = NULL;
    test_bearer_t *bearer = NULL;
    ogs_gtp2_message_t gtp_message;
    ogs_gtp2_create_session_request_t *req =
        &gtp_message.create_session_request;

    uint8_t msisdn_buf[OGS_MAX_MSISDN_LEN];
    int msisdn_len;

    ogs_gtp2_uli_t uli;
    char uli_buf[OGS_GTP2_MAX_ULI_LEN];
    ogs_gtp2_f_teid_t test_s2b_c_teid, test_s2b_u_teid[OGS_BEARER_PER_UE];
    int len, test_s2b_u_len;
    ogs_paa_t paa;

    ogs_gtp2_ambr_t ambr;
    ogs_gtp2_bearer_qos_t bearer_qos;
    char bearer_qos_buf[OGS_BEARER_PER_UE][GTP2_BEARER_QOS_LEN];
    char apn[OGS_MAX_APN_LEN+1];

    ogs_gtp2_indication_t indication;

    char node_identifier_buf[OGS_GTP2_MAX_NODE_IDENTIFIER_LEN];
    ogs_gtp2_node_identifier_t node_identifier;

    ogs_assert(sess);
    test_ue = sess->test_ue;
    ogs_assert(test_ue);

    ogs_assert(0 == ogs_list_count(&sess->bearer_list));

    bearer = test_bearer_add(sess, 5);
    ogs_assert(bearer);

    ogs_debug("Create Session Request");
    memset(&gtp_message, 0, sizeof(ogs_gtp2_message_t));

    if (handover_ind == true) {
        memset(&indication, 0, sizeof(ogs_gtp2_indication_t));
        indication.handover_indication = 1;
        req->indication_flags.presence = 1;
        req->indication_flags.data = &indication;
        req->indication_flags.len = sizeof(ogs_gtp2_indication_t);
    }

    ogs_assert(test_ue->imsi_len);
    req->imsi.presence = 1;
    req->imsi.data = test_ue->imsi_buf;
    req->imsi.len = test_ue->imsi_len;

    ogs_bcd_to_buffer(TEST_MSISDN, msisdn_buf, &msisdn_len);

    req->msisdn.presence = 1;
    req->msisdn.data = msisdn_buf;
    req->msisdn.len = msisdn_len;

    memset(&uli, 0, sizeof(ogs_gtp2_uli_t));
    uli.flags.e_cgi = 1;
    uli.flags.tai = 1;
    ogs_nas_from_plmn_id(&uli.tai.nas_plmn_id, &test_ue->e_tai.plmn_id);
    uli.tai.tac = test_ue->e_tai.tac;
    ogs_nas_from_plmn_id(&uli.e_cgi.nas_plmn_id, &test_ue->e_cgi.plmn_id);
    uli.e_cgi.cell_id = test_ue->e_cgi.cell_id;
    req->user_location_information.presence = 1;
    ogs_gtp2_build_uli(&req->user_location_information, &uli,
            uli_buf, OGS_GTP2_MAX_ULI_LEN);

    req->serving_network.presence = 1;
    req->serving_network.data = &uli.tai.nas_plmn_id;
    req->serving_network.len = sizeof(uli.tai.nas_plmn_id);

    req->rat_type.presence = 1;
    req->rat_type.u8 = OGS_GTP2_RAT_TYPE_WLAN;

    memset(&test_s2b_c_teid, 0, sizeof(ogs_gtp2_f_teid_t));
    test_s2b_c_teid.interface_type = OGS_GTP2_F_TEID_S2B_EPDG_GTP_C;
    test_s2b_c_teid.teid = htobe32(sess->epdg_s2b_c_teid);
    ogs_assert(sess->gnode->sock);
    rv = ogs_gtp2_sockaddr_to_f_teid(
            &sess->gnode->sock->local_addr, NULL, &test_s2b_c_teid, &len);
    ogs_assert(rv == OGS_OK);
    req->sender_f_teid_for_control_plane.presence = 1;
    req->sender_f_teid_for_control_plane.data = &test_s2b_c_teid;
    req->sender_f_teid_for_control_plane.len = len;

    req->access_point_name.presence = 1;
    req->access_point_name.len = ogs_fqdn_build(
            apn, sess->apn, strlen(sess->apn));
    req->access_point_name.data = apn;

    req->selection_mode.presence = 1;
    req->selection_mode.u8 =
        OGS_GTP2_SELECTION_MODE_MS_OR_NETWORK_PROVIDED_APN;

    memset(&paa, 0, sizeof(paa));
    paa.session_type = OGS_PDU_SESSION_TYPE_IPV4V6;

    req->pdn_address_allocation.presence = 1;
    req->pdn_address_allocation.data = &paa;
    req->pdn_address_allocation.len = OGS_PAA_IPV4V6_LEN;

    memset(&ambr, 0, sizeof(ogs_gtp2_ambr_t));
    ambr.uplink = htobe32(50000);
    ambr.downlink = htobe32(150000);
    req->aggregate_maximum_bit_rate.presence = 1;
    req->aggregate_maximum_bit_rate.data = &ambr;
    req->aggregate_maximum_bit_rate.len = sizeof(ambr);

    int i = 0;
    ogs_list_for_each(&sess->bearer_list, bearer) {
        ogs_assert(i < OGS_BEARER_PER_UE);

        req->bearer_contexts_to_be_created[i].presence = 1;
        req->bearer_contexts_to_be_created[i].eps_bearer_id.presence = 1;
        req->bearer_contexts_to_be_created[i].eps_bearer_id.u8 = bearer->ebi;

        memset(&test_s2b_u_teid[i], 0, sizeof(ogs_gtp2_f_teid_t));
        test_s2b_u_teid[i].teid = htobe32(bearer->enb_s1u_teid);
        test_s2b_u_teid[i].interface_type = OGS_GTP2_F_TEID_S2B_U_EPDG_GTP_U;
        ogs_assert(sess->gnode->sock);
        rv = ogs_gtp2_sockaddr_to_f_teid(
                &sess->gnode->sock->local_addr, NULL,
                &test_s2b_u_teid[i], &test_s2b_u_len);

        req->bearer_contexts_to_be_created[i].s2b_u_epdg_f_teid_5.presence = 1;
        req->bearer_contexts_to_be_created[i].s2b_u_epdg_f_teid_5.data =
            &test_s2b_u_teid[i];
        req->bearer_contexts_to_be_created[i].s2b_u_epdg_f_teid_5.len =
            test_s2b_u_len;

        memset(&bearer_qos, 0, sizeof(bearer_qos));
        bearer_qos.qci = 8;
        bearer_qos.priority_level = 1;
        bearer_qos.pre_emption_capability = 0;
        bearer_qos.pre_emption_vulnerability = 0;
        req->bearer_contexts_to_be_created[i].bearer_level_qos.presence = 1;
        ogs_gtp2_build_bearer_qos(
                &req->bearer_contexts_to_be_created[i].bearer_level_qos,
                &bearer_qos, bearer_qos_buf[i], GTP2_BEARER_QOS_LEN);

        i++;
    }

    req->recovery.presence = 1;
    req->recovery.u8 = 66;

    req->additional_protocol_configuration_options_apco.presence = 1;
    req->additional_protocol_configuration_options_apco.data =
        (uint8_t *)"\x80\x00\x0d\x00";
    req->additional_protocol_configuration_options_apco.len = 4;

    memset(&node_identifier, 0, sizeof(ogs_gtp2_node_identifier_t));
    node_identifier.name = "aaa.localdomain";
    node_identifier.name_len = strlen(node_identifier.name);
    node_identifier.realm = "localdomain";
    node_identifier.realm_len = strlen(node_identifier.realm);
    req->aaa_server_identifier.presence = 1;
    ogs_gtp2_build_node_identifier(
            &req->aaa_server_identifier, &node_identifier,
            node_identifier_buf, OGS_GTP2_MAX_NODE_IDENTIFIER_LEN);

    gtp_message.h.type = type;
    return ogs_gtp2_build_msg(&gtp_message);
}

ogs_pkbuf_t *test_s2b_build_delete_session_request(
        uint8_t type, test_sess_t *sess)
{
    int rv;
    test_bearer_t *bearer = NULL;

    ogs_gtp2_message_t gtp_message;
    ogs_gtp2_delete_session_request_t *req = &gtp_message.delete_session_request;

    ogs_assert(sess);
    bearer = ogs_list_first(&sess->bearer_list);
    ogs_assert(bearer);

    ogs_debug("Delete Session Request");
    memset(&gtp_message, 0, sizeof(ogs_gtp2_message_t));

    req->linked_eps_bearer_id.presence = 1;
    req->linked_eps_bearer_id.u8 = bearer->ebi;

    gtp_message.h.type = type;
    return ogs_gtp2_build_msg(&gtp_message);
}

ogs_pkbuf_t *test_s2b_build_create_bearer_response(
        uint8_t type, test_bearer_t *bearer)
{
    int rv;
    ogs_session_t *session = NULL;
    ogs_gtp2_message_t gtp_message;
    ogs_gtp2_create_bearer_response_t *rsp = NULL;

    test_sess_t *sess = NULL;

    ogs_gtp2_cause_t cause;
    ogs_gtp2_f_teid_t epdg_s2b_u_teid, smf_s2b_u_teid;
    int len;

    ogs_assert(bearer);
    sess = bearer->sess;
    ogs_assert(sess);

    ogs_debug("Create Bearer Response");
    rsp = &gtp_message.create_bearer_response;
    memset(&gtp_message, 0, sizeof(ogs_gtp2_message_t));

    memset(&cause, 0, sizeof(cause));
    cause.value = OGS_GTP2_CAUSE_REQUEST_ACCEPTED;

    rsp->cause.presence = 1;
    rsp->cause.data = &cause;
    rsp->cause.len = sizeof(cause);

    /* Bearer Context : EBI */
    rsp->bearer_contexts.presence = 1;
    rsp->bearer_contexts.eps_bearer_id.presence = 1;
    rsp->bearer_contexts.eps_bearer_id.u8 = bearer->ebi;

    /* Data Plane(DL) : ePDG-S2B-U */
    memset(&epdg_s2b_u_teid, 0, sizeof(ogs_gtp2_f_teid_t));
    epdg_s2b_u_teid.interface_type = OGS_GTP2_F_TEID_S2B_U_EPDG_GTP_U;
    epdg_s2b_u_teid.teid = htobe32(bearer->enb_s1u_teid);
    ogs_assert(sess->gnode->sock);
    rv = ogs_gtp2_sockaddr_to_f_teid(
            &sess->gnode->sock->local_addr, NULL, &epdg_s2b_u_teid, &len);
    if (rv != OGS_OK) {
        ogs_error("ogs_gtp2_sockaddr_to_f_teid() failed");
        return NULL;
    }
    rsp->bearer_contexts.s2b_u_epdg_f_teid_8.presence = 1;
    rsp->bearer_contexts.s2b_u_epdg_f_teid_8.data = &epdg_s2b_u_teid;
    rsp->bearer_contexts.s2b_u_epdg_f_teid_8.len = len;

    /* Data Plane(UL) : SMF-S2B-U */
    memset(&smf_s2b_u_teid, 0, sizeof(ogs_gtp2_f_teid_t));
    smf_s2b_u_teid.interface_type = OGS_GTP2_F_TEID_S2B_U_PGW_GTP_U;
    smf_s2b_u_teid.teid = htobe32(bearer->sgw_s1u_teid);
    rv = ogs_gtp2_ip_to_f_teid(&bearer->sgw_s1u_ip, &smf_s2b_u_teid, &len);
    if (rv != OGS_OK) {
        ogs_error("ogs_gtp2_ip_to_f_teid() failed");
        return NULL;
    }
    rsp->bearer_contexts.s2b_u_pgw_f_teid.presence = 1;
    rsp->bearer_contexts.s2b_u_pgw_f_teid.data = &smf_s2b_u_teid;
    rsp->bearer_contexts.s2b_u_pgw_f_teid.len = OGS_GTP2_F_TEID_IPV4_LEN;

    /* Bearer Context : Cause */
    rsp->bearer_contexts.cause.presence = 1;
    rsp->bearer_contexts.cause.len = sizeof(cause);
    rsp->bearer_contexts.cause.data = &cause;

    gtp_message.h.type = type;
    return ogs_gtp2_build_msg(&gtp_message);
}

ogs_pkbuf_t *test_s2b_build_delete_bearer_response(
        uint8_t type, test_bearer_t *bearer)
{
    int rv;
    ogs_session_t *session = NULL;
    ogs_gtp2_message_t gtp_message;
    ogs_gtp2_delete_bearer_response_t *rsp = NULL;

    ogs_gtp2_cause_t cause;

    ogs_assert(bearer);

    ogs_debug("Delete Bearer Response");
    rsp = &gtp_message.delete_bearer_response;
    memset(&gtp_message, 0, sizeof(ogs_gtp2_message_t));

    memset(&cause, 0, sizeof(cause));
    cause.value = OGS_GTP2_CAUSE_REQUEST_ACCEPTED;

    rsp->cause.presence = 1;
    rsp->cause.data = &cause;
    rsp->cause.len = sizeof(cause);

    rsp->linked_eps_bearer_id.presence = 1;
    rsp->linked_eps_bearer_id.u8 = bearer->ebi;

    gtp_message.h.type = type;
    return ogs_gtp2_build_msg(&gtp_message);
}
