/**
 * @file
 * @brief Helper Network port objects for implement secure connect attributes
 * @author Mikhail Antropov <michail.antropov@dsr-corporation.com>
 * @date June 2022
 * @copyright SPDX-License-Identifier: MIT
 */
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <bacnet/config.h>
#include <bacnet/basic/binding/address.h>
#include <bacnet/bacdef.h>
#include <bacnet/bacapp.h>
#include <bacnet/bacint.h>
#include <bacnet/bacdcode.h>
#include <bacnet/npdu.h>
#include <bacnet/apdu.h>
#include <bacnet/datalink/datalink.h>
#include <bacnet/basic/object/device.h>
#include <bacnet/timestamp.h>
/* me */
#include <bacnet/basic/object/bacfile.h>
#include <bacnet/basic/object/netport.h>
#include <bacnet/basic/object/sc_netport.h>
#include <bacnet/basic/object/netport_internal.h>
#include <bacnet/datalink/bsc/bsc-util.h>

#define SC_MIN_RECONNECT_MIN 2
#define SC_MIN_RECONNECT_MAX 300

#define SC_MAX_RECONNECT_MIN 2
#define SC_MAX_RECONNECT_MAX 600

#define SC_WAIT_CONNECT_MIN 5
#define SC_WAIT_CONNECT_MAX 300

static int
string_splite(const char *str, char separator, int *indexes, int length);
static bool
string_subsstr(char *str, int length, int index, const char *substr);

static void sc_binding_parse(char *str, uint16_t *port, char **ifname)
{
    char *p = NULL;

    *port = 0;
    *ifname = NULL;

    if (str != NULL) {
        for (p = str; (*p != 0) && (*p != BACNET_SC_BINDING_SEPARATOR); ++p)
            ;

        if (p != str) {
            if (*p == BACNET_SC_BINDING_SEPARATOR) {
                *p = 0;
                *port = (uint16_t)strtol(p + 1, NULL, 0);
                *ifname = (char *)str;
            } else {
                *port = (uint16_t)strtol(str, NULL, 0);
                *ifname = NULL;
            }
        }
    }
}

BACNET_UNSIGNED_INTEGER
Network_Port_Max_BVLC_Length_Accepted(uint32_t object_instance)
{
    BACNET_UNSIGNED_INTEGER max_length = 0;
    BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance);

    if (params) {
        max_length = params->Max_BVLC_Length_Accepted;
    }

    return max_length;
}

bool Network_Port_Max_BVLC_Length_Accepted_Set(
    uint32_t object_instance, BACNET_UNSIGNED_INTEGER value)
{
    BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance);
    if (!params) {
        return false;
    }

    params->Max_BVLC_Length_Accepted = value;

    return true;
}

bool Network_Port_Max_BVLC_Length_Accepted_Dirty_Set(
    uint32_t object_instance, BACNET_UNSIGNED_INTEGER value)
{
    BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance);
    if (!params) {
        return false;
    }

    params->Max_BVLC_Length_Accepted_dirty = value;

    return true;
}

BACNET_UNSIGNED_INTEGER
Network_Port_Max_NPDU_Length_Accepted(uint32_t object_instance)
{
    BACNET_UNSIGNED_INTEGER max_length = 0;
    BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance);

    if (params) {
        max_length = params->Max_NPDU_Length_Accepted;
    }

    return max_length;
}

bool Network_Port_Max_NPDU_Length_Accepted_Set(
    uint32_t object_instance, BACNET_UNSIGNED_INTEGER value)
{
    BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance);
    if (!params) {
        return false;
    }

    params->Max_NPDU_Length_Accepted = value;

    return true;
}

bool Network_Port_Max_NPDU_Length_Accepted_Dirty_Set(
    uint32_t object_instance, BACNET_UNSIGNED_INTEGER value)
{
    BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance);
    if (!params) {
        return false;
    }

    params->Max_NPDU_Length_Accepted_dirty = value;

    return true;
}

bool Network_Port_SC_Primary_Hub_URI(
    uint32_t object_instance, BACNET_CHARACTER_STRING *str)
{
    bool status = false;
    BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance);

    if (params) {
        status = characterstring_init_ansi(str, params->SC_Primary_Hub_URI);
    }

    return status;
}

const char *Network_Port_SC_Primary_Hub_URI_char(uint32_t object_instance)
{
    char *p = NULL;
    BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance);

    if (params && params->SC_Primary_Hub_URI[0]) {
        p = params->SC_Primary_Hub_URI;
    }

    return p;
}

bool Network_Port_SC_Primary_Hub_URI_Set(
    uint32_t object_instance, const char *str)
{
    BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance);
    if (!params) {
        return false;
    }

    if (str) {
        snprintf(
            params->SC_Primary_Hub_URI, sizeof(params->SC_Primary_Hub_URI),
            "%s", str);
    } else {
        params->SC_Primary_Hub_URI[0] = 0;
    }

    return true;
}

bool Network_Port_SC_Primary_Hub_URI_Dirty_Set(
    uint32_t object_instance, const char *str)
{
    BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance);
    if (!params) {
        return false;
    }

    if (str) {
        snprintf(
            params->SC_Primary_Hub_URI_dirty,
            sizeof(params->SC_Primary_Hub_URI_dirty), "%s", str);
    } else {
        params->SC_Primary_Hub_URI_dirty[0] = 0;
    }

    Network_Port_Changes_Pending_Set(object_instance, true);

    return true;
}

bool Network_Port_SC_Failover_Hub_URI(
    uint32_t object_instance, BACNET_CHARACTER_STRING *str)
{
    bool status = false;
    BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance);

    if (params) {
        status = characterstring_init_ansi(str, params->SC_Failover_Hub_URI);
    }

    return status;
}

const char *Network_Port_SC_Failover_Hub_URI_char(uint32_t object_instance)
{
    char *p = NULL;
    BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance);

    if (params && params->SC_Failover_Hub_URI[0]) {
        p = params->SC_Failover_Hub_URI;
    }

    return p;
}

bool Network_Port_SC_Failover_Hub_URI_Set(
    uint32_t object_instance, const char *str)
{
    BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance);
    if (!params) {
        return false;
    }

    if (str) {
        snprintf(
            params->SC_Failover_Hub_URI, sizeof(params->SC_Failover_Hub_URI),
            "%s", str);
    } else {
        params->SC_Failover_Hub_URI[0] = 0;
    }

    return true;
}

bool Network_Port_SC_Failover_Hub_URI_Dirty_Set(
    uint32_t object_instance, const char *str)
{
    BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance);
    if (!params) {
        return false;
    }

    if (str) {
        snprintf(
            params->SC_Failover_Hub_URI_dirty,
            sizeof(params->SC_Failover_Hub_URI_dirty), "%s", str);
    } else {
        params->SC_Failover_Hub_URI_dirty[0] = 0;
    }

    Network_Port_Changes_Pending_Set(object_instance, true);

    return true;
}

BACNET_UNSIGNED_INTEGER
Network_Port_SC_Minimum_Reconnect_Time(uint32_t object_instance)
{
    BACNET_UNSIGNED_INTEGER timeout = 0;
    BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance);

    if (params) {
        timeout = params->SC_Minimum_Reconnect_Time;
    }

    return timeout;
}

bool Network_Port_SC_Minimum_Reconnect_Time_Set(
    uint32_t object_instance, BACNET_UNSIGNED_INTEGER value)
{
    BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance);
    if (!params) {
        return false;
    }

    if ((value < SC_MIN_RECONNECT_MIN) || (value > SC_MIN_RECONNECT_MAX)) {
        return false;
    }

    params->SC_Minimum_Reconnect_Time = value;

    return true;
}

bool Network_Port_SC_Minimum_Reconnect_Time_Dirty_Set(
    uint32_t object_instance, BACNET_UNSIGNED_INTEGER value)
{
    BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance);
    if (!params) {
        return false;
    }

    if ((value < SC_MIN_RECONNECT_MIN) || (value > SC_MIN_RECONNECT_MAX)) {
        return false;
    }

    params->SC_Minimum_Reconnect_Time_dirty = value;

    return true;
}

BACNET_UNSIGNED_INTEGER
Network_Port_SC_Maximum_Reconnect_Time(uint32_t object_instance)
{
    BACNET_UNSIGNED_INTEGER timeout = 0;
    BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance);

    if (params) {
        timeout = params->SC_Maximum_Reconnect_Time;
    }

    return timeout;
}

bool Network_Port_SC_Maximum_Reconnect_Time_Set(
    uint32_t object_instance, BACNET_UNSIGNED_INTEGER value)
{
    BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance);
    if (!params) {
        return false;
    }

    if ((value < SC_MAX_RECONNECT_MIN) || (value > SC_MAX_RECONNECT_MAX)) {
        return false;
    }

    params->SC_Maximum_Reconnect_Time = value;

    return true;
}

bool Network_Port_SC_Maximum_Reconnect_Time_Dirty_Set(
    uint32_t object_instance, BACNET_UNSIGNED_INTEGER value)
{
    BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance);
    if (!params) {
        return false;
    }

    if ((value < SC_MAX_RECONNECT_MIN) || (value > SC_MAX_RECONNECT_MAX)) {
        return false;
    }

    params->SC_Maximum_Reconnect_Time_dirty = value;

    return true;
}

BACNET_UNSIGNED_INTEGER
Network_Port_SC_Connect_Wait_Timeout(uint32_t object_instance)
{
    BACNET_UNSIGNED_INTEGER timeout = 0;
    BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance);

    if (params) {
        timeout = params->SC_Connect_Wait_Timeout;
    }

    return timeout;
}

bool Network_Port_SC_Connect_Wait_Timeout_Set(
    uint32_t object_instance, BACNET_UNSIGNED_INTEGER value)
{
    BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance);
    if (!params) {
        return false;
    }

    if ((value < SC_WAIT_CONNECT_MIN) || (value > SC_WAIT_CONNECT_MAX)) {
        return false;
    }

    params->SC_Connect_Wait_Timeout = value;

    return true;
}

bool Network_Port_SC_Connect_Wait_Timeout_Dirty_Set(
    uint32_t object_instance, BACNET_UNSIGNED_INTEGER value)
{
    BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance);
    if (!params) {
        return false;
    }

    if ((value < SC_WAIT_CONNECT_MIN) || (value > SC_WAIT_CONNECT_MAX)) {
        return false;
    }

    params->SC_Connect_Wait_Timeout_dirty = value;

    return true;
}

BACNET_UNSIGNED_INTEGER
Network_Port_SC_Disconnect_Wait_Timeout(uint32_t object_instance)
{
    BACNET_UNSIGNED_INTEGER timeout = 0;
    BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance);

    if (params) {
        timeout = params->SC_Disconnect_Wait_Timeout;
    }

    return timeout;
}

bool Network_Port_SC_Disconnect_Wait_Timeout_Set(
    uint32_t object_instance, BACNET_UNSIGNED_INTEGER value)
{
    BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance);
    if (!params) {
        return false;
    }

    params->SC_Disconnect_Wait_Timeout = value;

    return true;
}

bool Network_Port_SC_Disconnect_Wait_Timeout_Dirty_Set(
    uint32_t object_instance, BACNET_UNSIGNED_INTEGER value)
{
    BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance);
    if (!params) {
        return false;
    }

    params->SC_Disconnect_Wait_Timeout_dirty = value;

    return true;
}

BACNET_UNSIGNED_INTEGER
Network_Port_SC_Heartbeat_Timeout(uint32_t object_instance)
{
    BACNET_UNSIGNED_INTEGER timeout = 0;
    BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance);

    if (params) {
        timeout = params->SC_Heartbeat_Timeout;
    }

    return timeout;
}

bool Network_Port_SC_Heartbeat_Timeout_Set(
    uint32_t object_instance, BACNET_UNSIGNED_INTEGER value)
{
    BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance);
    if (!params) {
        return false;
    }

    params->SC_Heartbeat_Timeout = value;

    return true;
}

bool Network_Port_SC_Heartbeat_Timeout_Dirty_Set(
    uint32_t object_instance, BACNET_UNSIGNED_INTEGER value)
{
    BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance);
    if (!params) {
        return false;
    }

    params->SC_Heartbeat_Timeout_dirty = value;

    return true;
}

BACNET_SC_HUB_CONNECTOR_STATE
Network_Port_SC_Hub_Connector_State(uint32_t object_instance)
{
    BACNET_SC_HUB_CONNECTOR_STATE state =
        BACNET_SC_HUB_CONNECTOR_STATE_NO_HUB_CONNECTION;
    BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance);

    if (params) {
        state = params->SC_Hub_Connector_State;
    }

    return state;
}

bool Network_Port_SC_Hub_Connector_State_Set(
    uint32_t object_instance, BACNET_SC_HUB_CONNECTOR_STATE value)
{
    BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance);
    if (!params) {
        return false;
    }

    params->SC_Hub_Connector_State = value;

    return true;
}

uint32_t Network_Port_Operational_Certificate_File(uint32_t object_instance)
{
    uint32_t id = 0;
    BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance);

    if (params) {
        id = params->Operational_Certificate_File;
    }

    return id;
}

bool Network_Port_Operational_Certificate_File_Set(
    uint32_t object_instance, uint32_t value)
{
    BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance);
    if (!params) {
        return false;
    }

    params->Operational_Certificate_File = value;

    return true;
}

uint32_t
Network_Port_Issuer_Certificate_File(uint32_t object_instance, uint8_t index)
{
    uint32_t id = 0;
    BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance);

    if (params && (index < BACNET_ISSUER_CERT_FILE_MAX)) {
        id = params->Issuer_Certificate_Files[index];
    }

    return id;
}

bool Network_Port_Issuer_Certificate_File_Set(
    uint32_t object_instance, uint8_t index, uint32_t value)
{
    BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance);
    if (!params || (index >= BACNET_ISSUER_CERT_FILE_MAX)) {
        return false;
    }

    params->Issuer_Certificate_Files[index] = value;

    return true;
}

/**
 * @brief Encode one BACnetARRAY property element; a function template
 * @param object_instance [in] BACnet network port object instance number
 * @param index [in] array index requested:
 *    0 to n for individual array members
 * @param apdu [out] Buffer in which the APDU contents are built, or NULL to
 * return the length of buffer if it had been built
 * @return The length of the apdu encoded or
 *   BACNET_STATUS_ERROR for invalid array index
 */
int Network_Port_Issuer_Certificate_File_Encode(
    uint32_t object_instance, BACNET_ARRAY_INDEX index, uint8_t *apdu)
{
    int apdu_len = 0;
    uint32_t file_instance = 0;

    if (index >= BACNET_ISSUER_CERT_FILE_MAX) {
        apdu_len = BACNET_STATUS_ERROR;
    } else {
        file_instance =
            Network_Port_Issuer_Certificate_File(object_instance, index);
        apdu_len = encode_application_unsigned(apdu, file_instance);
    }

    return apdu_len;
}

uint32_t Network_Port_Certificate_Signing_Request_File(uint32_t object_instance)
{
    uint32_t id = 0;
    BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance);

    if (params) {
        id = params->Certificate_Signing_Request_File;
    }

    return id;
}

bool Network_Port_Certificate_Signing_Request_File_Set(
    uint32_t object_instance, uint32_t value)
{
    BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance);
    if (!params) {
        return false;
    }

    params->Certificate_Signing_Request_File = value;

    return true;
}

#ifdef BACNET_SECURE_CONNECT_ROUTING_TABLE

#if 0
/* This function will needed if the Network_Port_Routing_Table_Find()
    or Network_Port_Routing_Table_Get() will return individual fields,
     not the entire struct. */
static bool MAC_Address_Get(uint8_t network_type, uint8_t *mac,
    BACNET_OCTET_STRING *mac_address)
{
    bool status = false;
    uint8_t ip_mac[4 + 2] = { 0 };
    size_t mac_len = 0;

    switch (network_type) {
        case PORT_TYPE_ETHERNET:
            mac_len = 6;
            break;
        case PORT_TYPE_MSTP:
            mac_len = 1;
            break;
        case PORT_TYPE_BIP:
            memcpy(&ip_mac[0], mac, 4);
            /* convert port from host-byte-order to network-byte-order */
            encode_unsigned16(&ip_mac[4], *(uint16_t*)(mac + 4));
            mac = &ip_mac[0];
            mac_len = sizeof(ip_mac);
            break;
        case PORT_TYPE_BIP6:
            mac_len = 3;
            break;
        default:
            break;
    }
    if (mac) {
        status = octetstring_init(mac_address, mac, mac_len);
    }
    return status;
}
#endif

static bool MAC_Address_Set(
    uint8_t network_type, uint8_t *mac_dest, uint8_t *mac_src, uint8_t mac_len)
{
    bool status = false;
    size_t mac_size = 0;
    uint8_t ip_mac[4 + 2] = { 0 };

    switch (network_type) {
        case PORT_TYPE_ETHERNET:
            mac_size = 6;
            break;
        case PORT_TYPE_MSTP:
            mac_size = 1;
            break;
        case PORT_TYPE_BIP:
            memcpy(&ip_mac[0], mac_src, 4);
            /* convert port from host-byte-order to network-byte-order */
            decode_unsigned16(&mac_src[4], (uint16_t *)&ip_mac[4]);
            mac_src = &ip_mac[0];
            mac_len = sizeof(ip_mac);
            break;
        case PORT_TYPE_BIP6:
            mac_size = 3;
            break;
        default:
            break;
    }
    if (mac_src && mac_dest && (mac_len == mac_size)) {
        memcpy(mac_dest, mac_src, mac_size);
        status = true;
    }
    return status;
}

BACNET_ROUTER_ENTRY *Network_Port_Routing_Table_Find(
    uint32_t object_instance, uint16_t network_number)
{
    BACNET_ROUTER_ENTRY *entry = NULL;
    BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance);
    if (!params) {
        return entry;
    }

    entry = Keylist_Data(params->Routing_Table, network_number);

    return entry;
}

BACNET_ROUTER_ENTRY *
Network_Port_Routing_Table_Get(uint32_t object_instance, uint8_t index)
{
    BACNET_ROUTER_ENTRY *entry = NULL;
    BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance);
    if (!params) {
        return entry;
    }

    entry = Keylist_Data_Index(params->Routing_Table, index);

    return entry;
}

bool Network_Port_Routing_Table_Add(
    uint32_t object_instance,
    uint16_t network_number,
    uint8_t *mac,
    uint8_t mac_len,
    uint8_t status,
    uint8_t performance_index)
{
    int index;
    uint8_t network_type;
    BACNET_ROUTER_ENTRY *entry;
    BACNET_SC_PARAMS *params;

    params = Network_Port_SC_Params(object_instance);
    if (!params) {
        return false;
    }
    network_type = Network_Port_Type(object_instance);
    entry = calloc(1, sizeof(BACNET_ROUTER_ENTRY));
    if (!entry) {
        return false;
    }
    entry->Network_Number = network_number;
    MAC_Address_Set(network_type, entry->Mac_Address, mac, mac_len);
    entry->Status = status;
    entry->Performance_Index = performance_index;
    index = Keylist_Data_Add(params->Routing_Table, network_number, entry);
    if (index < 0) {
        free(entry);
        return false;
    }

    return true;
}

bool Network_Port_Routing_Table_Delete(
    uint32_t object_instance, uint16_t network_number)
{
    BACNET_ROUTER_ENTRY *entry;
    BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance);
    if (!params) {
        return false;
    }

    entry = Keylist_Data_Delete(params->Routing_Table, network_number);
    free(entry);

    return true;
}

bool Network_Port_Routing_Table_Delete_All(uint32_t object_instance)
{
    BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance);
    if (!params) {
        return false;
    }

    Keylist_Delete(params->Routing_Table);
    params->Routing_Table = Keylist_Create();

    return true;
}

uint8_t Network_Port_Routing_Table_Count(uint32_t object_instance)
{
    uint8_t count = 0;
    BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance);
    if (!params) {
        return 0;
    }

    count = Keylist_Count(params->Routing_Table);

    return count;
}

/**
 * @brief Handle a request to encode all the the routing table entries
 * @param apdu [out] Buffer in which the APDU contents are built.
 * @param max_apdu [in] Max length of the APDU buffer.
 *
 * @return How many bytes were encoded in the buffer, or
 *   BACNET_STATUS_ABORT if the response would not fit within the buffer.
 */
int Network_Port_Routing_Table_Encode(
    uint32_t object_instance, uint8_t *apdu, int max_apdu)
{
    BACNET_ROUTER_ENTRY *entry = NULL;
    int apdu_len = 0;
    unsigned index = 0;
    unsigned size = 0;

    size = Network_Port_Routing_Table_Count(object_instance);
    for (index = 0; index < size; index++) {
        entry = Network_Port_Routing_Table_Get(object_instance, index);
        apdu_len += bacapp_encode_RouterEntry(NULL, entry);
    }
    if (apdu_len > max_apdu) {
        return BACNET_STATUS_ABORT;
    }
    apdu_len = 0;
    for (index = 0; index < size; index++) {
        entry = Network_Port_Routing_Table_Get(object_instance, index);
        apdu_len += bacapp_encode_RouterEntry(&apdu[apdu_len], entry);
    }

    return apdu_len;
}

#endif /* BACNET_SECURE_CONNECT_ROUTING_TABLE */

#if BSC_CONF_HUB_FUNCTIONS_NUM != 0

BACNET_SC_HUB_CONNECTION_STATUS *
Network_Port_SC_Primary_Hub_Connection_Status(uint32_t object_instance)
{
    BACNET_SC_HUB_CONNECTION_STATUS *connection = NULL;
    BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance);

    if (params) {
        connection = &params->SC_Primary_Hub_Connection_Status;
    }

    return connection;
}

bool Network_Port_SC_Primary_Hub_Connection_Status_Set(
    uint32_t object_instance,
    BACNET_SC_CONNECTION_STATE state,
    BACNET_DATE_TIME *connect_ts,
    BACNET_DATE_TIME *disconnect_ts,
    BACNET_ERROR_CODE error,
    const char *error_details)
{
    BACNET_SC_HUB_CONNECTION_STATUS *st;
    BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance);
    if (!params) {
        return false;
    }

    st = &params->SC_Primary_Hub_Connection_Status;
    st->State = state;
    st->Connect_Timestamp = *connect_ts;
    st->Disconnect_Timestamp = *disconnect_ts;
    st->Error = error;
    if (error_details) {
        bsc_copy_str(
            st->Error_Details, error_details, sizeof(st->Error_Details));
    } else {
        st->Error_Details[0] = 0;
    }

    return true;
}

BACNET_SC_HUB_CONNECTION_STATUS *
Network_Port_SC_Failover_Hub_Connection_Status(uint32_t object_instance)
{
    BACNET_SC_HUB_CONNECTION_STATUS *connection = NULL;
    BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance);

    if (params) {
        connection = &params->SC_Failover_Hub_Connection_Status;
    }

    return connection;
}

bool Network_Port_SC_Failover_Hub_Connection_Status_Set(
    uint32_t object_instance,
    BACNET_SC_CONNECTION_STATE state,
    BACNET_DATE_TIME *connect_ts,
    BACNET_DATE_TIME *disconnect_ts,
    BACNET_ERROR_CODE error,
    const char *error_details)
{
    BACNET_SC_HUB_CONNECTION_STATUS *st;
    BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance);
    if (!params) {
        return false;
    }

    st = &params->SC_Failover_Hub_Connection_Status;
    st->State = state;
    st->Connect_Timestamp = *connect_ts;
    st->Disconnect_Timestamp = *disconnect_ts;
    st->Error = error;
    if (error_details) {
        bsc_copy_str(
            st->Error_Details, error_details, sizeof(st->Error_Details));
    } else {
        st->Error_Details[0] = 0;
    }

    return true;
}

bool Network_Port_SC_Hub_Function_Enable(uint32_t object_instance)
{
    bool enable = false;
    BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance);

    if (params) {
        enable = params->SC_Hub_Function_Enable;
    }

    return enable;
}

bool Network_Port_SC_Hub_Function_Enable_Set(
    uint32_t object_instance, bool value)
{
    BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance);
    if (!params) {
        return false;
    }

    params->SC_Hub_Function_Enable = value;

    return true;
}

bool Network_Port_SC_Hub_Function_Enable_Dirty_Set(
    uint32_t object_instance, bool value)
{
    BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance);
    if (!params) {
        return false;
    }

    params->SC_Hub_Function_Enable_dirty = value;

    Network_Port_Changes_Pending_Set(object_instance, true);

    return true;
}

bool Network_Port_SC_Hub_Function_Accept_URI(
    uint32_t object_instance, uint8_t index, BACNET_CHARACTER_STRING *str)
{
    bool status = false;
    int idx[BACNET_SC_DIRECT_ACCEPT_URI_MAX + 1] = { 0 };
    int len;

    BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance);

    if (params && (index < BACNET_SC_DIRECT_ACCEPT_URI_MAX)) {
        len = string_splite(
            params->SC_Hub_Function_Accept_URIs, ' ', idx, sizeof(idx));
        if (index < len) {
            status = characterstring_init_ansi_safe(
                str, params->SC_Hub_Function_Accept_URIs + idx[index],
                idx[index + 1] - idx[index] - 1);
        } else {
            status = characterstring_init_ansi(str, "");
        }
    }

    return status;
}

bool Network_Port_SC_Hub_Function_Accept_URI_Set(
    uint32_t object_instance, uint8_t index, const char *str)
{
    BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance);
    if (!params || (index >= BACNET_SC_DIRECT_ACCEPT_URI_MAX)) {
        return false;
    }

    string_subsstr(
        params->SC_Hub_Function_Accept_URIs,
        sizeof(params->SC_Hub_Function_Accept_URIs), index, str);

    return true;
}

/**
 * @brief Encode a BACnetARRAY property element; a function template
 * @param object_instance [in] BACnet network port object instance number
 * @param index [in] array index requested:
 *    0 to n for individual array members
 * @param apdu [out] Buffer in which the APDU contents are built, or NULL to
 * return the length of buffer if it had been built
 * @return The length of the apdu encoded or
 *   BACNET_STATUS_ERROR for invalid array index
 */
int Network_Port_SC_Hub_Function_Accept_URI_Encode(
    uint32_t object_instance, BACNET_ARRAY_INDEX index, uint8_t *apdu)
{
    int apdu_len = 0;
    BACNET_CHARACTER_STRING uri = { 0 };

    if (index >= BACNET_SC_DIRECT_ACCEPT_URI_MAX) {
        apdu_len = BACNET_STATUS_ERROR;
    } else {
        if (Network_Port_SC_Hub_Function_Accept_URI(
                object_instance, index, &uri)) {
            apdu_len = encode_application_character_string(apdu, &uri);
        }
    }

    return apdu_len;
}

bool Network_Port_SC_Hub_Function_Accept_URI_Dirty_Set(
    uint32_t object_instance, uint8_t index, const char *str)
{
    BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance);
    if (!params || (index >= BACNET_SC_DIRECT_ACCEPT_URI_MAX)) {
        return false;
    }

    string_subsstr(
        params->SC_Hub_Function_Accept_URIs_dirty,
        sizeof(params->SC_Hub_Function_Accept_URIs_dirty), index, str);

    return true;
}

const char *
Network_Port_SC_Hub_Function_Accept_URIs_char(uint32_t object_instance)
{
    char *p = NULL;
    BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance);

    if (params) {
        p = params->SC_Hub_Function_Accept_URIs;
    }

    return p;
}

bool Network_Port_SC_Hub_Function_Accept_URIs_Set(
    uint32_t object_instance, const char *str)
{
    bool status = false;
    BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance);

    if (params) {
        if (str) {
            snprintf(
                params->SC_Hub_Function_Accept_URIs,
                sizeof(params->SC_Hub_Function_Accept_URIs), "%s", str);
        } else {
            params->SC_Hub_Function_Accept_URIs[0] = 0;
        }
    }

    return status;
}

bool Network_Port_SC_Hub_Function_Binding(
    uint32_t object_instance, BACNET_CHARACTER_STRING *str)
{
    bool status = false;
    BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance);
    char tmp[sizeof(params->SC_Hub_Function_Binding)];

    if (params) {
        snprintf(
            tmp, sizeof(tmp), "%s:%d",
            params->SC_Hub_Function_Binding + sizeof(uint16_t),
            *(uint16_t *)params->SC_Hub_Function_Binding);

        status = characterstring_init_ansi(str, tmp);
    }

    return status;
}

void Network_Port_SC_Hub_Function_Binding_get(
    uint32_t object_instance, uint16_t *port, char **ifname)
{
    BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance);
    *port = 0;
    *ifname = NULL;

    if (params && params->SC_Hub_Function_Binding[0]) {
        *port = *(uint16_t *)params->SC_Hub_Function_Binding;
        *ifname = params->SC_Hub_Function_Binding + sizeof(uint16_t);
        if (**ifname == 0) {
            *ifname = NULL;
        }
    }
}

bool Network_Port_SC_Hub_Function_Binding_Set(
    uint32_t object_instance, const char *str)
{
    BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance);
    uint16_t port;
    char *ifname;
    char tmp[BACNET_BINDING_STRING_LENGTH];

    if (!params) {
        return false;
    }

    if (str && str[0]) {
        bsc_copy_str(tmp, str, sizeof(tmp));
        sc_binding_parse(tmp, &port, &ifname);
        *(uint16_t *)params->SC_Hub_Function_Binding = port;
        if (ifname) {
            bsc_copy_str(
                params->SC_Hub_Function_Binding + sizeof(uint16_t), ifname,
                sizeof(params->SC_Hub_Function_Binding) - sizeof(uint16_t));
        } else {
            *(params->SC_Hub_Function_Binding + sizeof(uint16_t)) = 0;
        }
    } else {
        memset(
            params->SC_Hub_Function_Binding, 0,
            sizeof(params->SC_Hub_Function_Binding));
    }

    return true;
}

bool Network_Port_SC_Hub_Function_Binding_Dirty_Set(
    uint32_t object_instance, const char *str)
{
    BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance);
    if (!params) {
        return false;
    }

    if (str) {
        snprintf(
            params->SC_Hub_Function_Binding_dirty,
            sizeof(params->SC_Hub_Function_Binding_dirty), "%s", str);
    } else {
        params->SC_Hub_Function_Binding_dirty[0] = 0;
    }

    Network_Port_Changes_Pending_Set(object_instance, true);

    return true;
}

BACNET_SC_HUB_FUNCTION_CONNECTION_STATUS *
Network_Port_SC_Hub_Function_Connection_Status_Get(
    uint32_t object_instance, uint8_t index)
{
    BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance);

    if (!params) {
        return NULL;
    }

    if (index >= params->SC_Hub_Function_Connection_Status_Count) {
        return NULL;
    }

    return &params->SC_Hub_Function_Connection_Status[index];
}

bool Network_Port_SC_Hub_Function_Connection_Status_Add(
    uint32_t object_instance,
    BACNET_SC_CONNECTION_STATE state,
    BACNET_DATE_TIME *connect_ts,
    BACNET_DATE_TIME *disconnect_ts,
    BACNET_HOST_N_PORT_DATA *peer_address,
    uint8_t *peer_VMAC,
    uint8_t *peer_UUID,
    BACNET_ERROR_CODE error,
    const char *error_details)
{
    BACNET_SC_HUB_FUNCTION_CONNECTION_STATUS *st;
    BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance);
    if (!params) {
        return false;
    }

    if (params->SC_Hub_Function_Connection_Status_Count + 1 >
        ARRAY_SIZE(params->SC_Hub_Function_Connection_Status)) {
        return false;
    }

    st = &params->SC_Hub_Function_Connection_Status
              [params->SC_Hub_Function_Connection_Status_Count];
    params->SC_Hub_Function_Connection_Status_Count += 1;

    st->State = state;
    st->Connect_Timestamp = *connect_ts;
    st->Disconnect_Timestamp = *disconnect_ts;
    st->Peer_Address = *peer_address;
    memcpy(st->Peer_VMAC, peer_VMAC, sizeof(st->Peer_VMAC));
    memcpy(
        st->Peer_UUID.uuid.uuid128, peer_UUID,
        sizeof(st->Peer_UUID.uuid.uuid128));
    st->Error = error;
    if (error_details) {
        bsc_copy_str(
            st->Error_Details, error_details, sizeof(st->Error_Details));
    } else {
        st->Error_Details[0] = 0;
    }

    return true;
}

bool Network_Port_SC_Hub_Function_Connection_Status_Delete_All(
    uint32_t object_instance)
{
    BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance);
    if (!params) {
        return false;
    }

    params->SC_Hub_Function_Connection_Status_Count = 0;

    return true;
}

int Network_Port_SC_Hub_Function_Connection_Status_Count(
    uint32_t object_instance)
{
    BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance);
    if (!params) {
        return 0;
    }

    return params->SC_Hub_Function_Connection_Status_Count;
}

/**
 * @brief Handle a request to encode all the the entries
 * @param object_instance [in] BACnet network port object instance number
 * @param apdu [out] Buffer in which the APDU contents are built.
 * @param max_apdu [in] Max length of the APDU buffer.
 *
 * @return How many bytes were encoded in the buffer, or
 *   BACNET_STATUS_ABORT if the response would not fit within the buffer.
 */
int Network_Port_SC_Hub_Function_Connection_Status_Encode(
    uint32_t object_instance, uint8_t *apdu, int max_apdu)
{
    BACNET_SC_HUB_FUNCTION_CONNECTION_STATUS *entry = NULL;
    int apdu_len = 0;
    unsigned index = 0;
    unsigned size = 0;

    size =
        Network_Port_SC_Hub_Function_Connection_Status_Count(object_instance);
    for (index = 0; index < size; index++) {
        entry = Network_Port_SC_Hub_Function_Connection_Status_Get(
            object_instance, index);
        apdu_len += bacapp_encode_SCHubFunctionConnection(NULL, entry);
    }
    if (apdu_len > max_apdu) {
        return BACNET_STATUS_ABORT;
    }
    apdu_len = 0;
    for (index = 0; index < size; index++) {
        entry = Network_Port_SC_Hub_Function_Connection_Status_Get(
            object_instance, index);
        apdu_len +=
            bacapp_encode_SCHubFunctionConnection(&apdu[apdu_len], entry);
    }

    return apdu_len;
}

#endif /* BSC_CONF_HUB_FUNCTIONS_NUM!=0 */

#if BSC_CONF_HUB_CONNECTORS_NUM != 0

bool Network_Port_SC_Direct_Connect_Initiate_Enable(uint32_t object_instance)
{
    bool enable = false;
    BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance);

    if (params) {
        enable = params->SC_Direct_Connect_Initiate_Enable;
    }

    return enable;
}

bool Network_Port_SC_Direct_Connect_Initiate_Enable_Set(
    uint32_t object_instance, bool value)
{
    BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance);
    if (!params) {
        return false;
    }

    params->SC_Direct_Connect_Initiate_Enable = value;

    return true;
}
bool Network_Port_SC_Direct_Connect_Initiate_Enable_Dirty_Set(
    uint32_t object_instance, bool value)
{
    BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance);
    if (!params) {
        return false;
    }

    params->SC_Direct_Connect_Initiate_Enable_dirty = value;

    Network_Port_Changes_Pending_Set(object_instance, true);

    return true;
}

bool Network_Port_SC_Direct_Connect_Accept_Enable(uint32_t object_instance)
{
    bool enable = false;
    BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance);

    if (params) {
        enable = params->SC_Direct_Connect_Accept_Enable;
    }

    return enable;
}

bool Network_Port_SC_Direct_Connect_Accept_Enable_Set(
    uint32_t object_instance, bool value)
{
    BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance);
    if (!params) {
        return false;
    }

    params->SC_Direct_Connect_Accept_Enable = value;

    return true;
}

bool Network_Port_SC_Direct_Connect_Accept_Enable_Dirty_Set(
    uint32_t object_instance, bool value)
{
    BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance);
    if (!params) {
        return false;
    }

    params->SC_Direct_Connect_Accept_Enable_dirty = value;

    Network_Port_Changes_Pending_Set(object_instance, true);

    return true;
}

/* return number of urls, */
/* indexes are fill first position of urls plus position of end string */
static int
string_splite(const char *str, char separator, int *indexes, int length)
{
    int i;
    int k = 0;
    indexes[0] = 0;
    for (i = 0; str[i] != 0 && k < length; i++) {
        if (str[i] == separator) {
            k++;
            indexes[k] = i + 1;
        }
    }

    if (k < length) {
        k++;
        indexes[k] = i + 1;
    }

    return k;
}

static bool string_subsstr(char *str, int length, int index, const char *substr)
{
    int idx[BACNET_SC_DIRECT_ACCEPT_URI_MAX + 1] = { 0 };
    int len;
    char *head;
    char *tail_old;
    char *tail_new;

    len = string_splite(str, ' ', idx, sizeof(idx));
    if (index < len) {
        head = str + idx[index];
        tail_old = str + idx[index + 1] - 1;
        tail_new = head + strlen(substr);
        if (tail_new + strlen(tail_old) + 1 - str >= length) {
            return false;
        }

        memmove(tail_new, tail_old, strlen(tail_old) + 1);
        memcpy(head, substr, strlen(substr));
    } else {
        head = str + strlen(str);
        if (head != str) {
            *head = ' ';
            head++;
        }
        if (head + strlen(substr) + 1 - str >= length) {
            return false;
        }
        memcpy(head, substr, strlen(substr) + 1);
    }

    return true;
}

bool Network_Port_SC_Direct_Connect_Accept_URI(
    uint32_t object_instance, uint8_t index, BACNET_CHARACTER_STRING *str)
{
    bool status = false;
    int idx[BACNET_SC_DIRECT_ACCEPT_URI_MAX + 1] = { 0 };
    int len;

    BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance);

    if (params && (index < BACNET_SC_DIRECT_ACCEPT_URI_MAX)) {
        len = string_splite(
            params->SC_Direct_Connect_Accept_URIs, ' ', idx, sizeof(idx));
        if (index < len) {
            status = characterstring_init_ansi_safe(
                str, params->SC_Direct_Connect_Accept_URIs + idx[index],
                idx[index + 1] - idx[index] - 1);
        } else {
            status = characterstring_init_ansi(str, "");
        }
    }

    return status;
}

bool Network_Port_SC_Direct_Connect_Accept_URI_Set(
    uint32_t object_instance, uint8_t index, const char *str)
{
    BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance);
    if (!params || (index >= BACNET_SC_DIRECT_ACCEPT_URI_MAX)) {
        return false;
    }

    string_subsstr(
        params->SC_Direct_Connect_Accept_URIs,
        sizeof(params->SC_Direct_Connect_Accept_URIs), index, str);

    return true;
}

/**
 * @brief Encode a BACnetARRAY property element; a function template
 * @param object_instance [in] BACnet network port object instance number
 * @param index [in] array index requested:
 *    0 to n for individual array members
 * @param apdu [out] Buffer in which the APDU contents are built, or NULL to
 * return the length of buffer if it had been built
 * @return The length of the apdu encoded or
 *   BACNET_STATUS_ERROR for ERROR_CODE_INVALID_ARRAY_INDEX
 */
int Network_Port_SC_Direct_Connect_Accept_URI_Encode(
    uint32_t object_instance, BACNET_ARRAY_INDEX index, uint8_t *apdu)
{
    int apdu_len = 0;
    BACNET_CHARACTER_STRING uri = { 0 };

    if (index >= BACNET_SC_DIRECT_ACCEPT_URI_MAX) {
        apdu_len = BACNET_STATUS_ERROR;
    } else {
        if (Network_Port_SC_Direct_Connect_Accept_URI(
                object_instance, index, &uri)) {
            apdu_len = encode_application_character_string(apdu, &uri);
        }
    }

    return apdu_len;
}

bool Network_Port_SC_Direct_Connect_Accept_URI_Dirty_Set(
    uint32_t object_instance, uint8_t index, const char *str)
{
    BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance);
    if (!params || (index >= BACNET_SC_DIRECT_ACCEPT_URI_MAX)) {
        return false;
    }

    string_subsstr(
        params->SC_Direct_Connect_Accept_URIs_dirty,
        sizeof(params->SC_Direct_Connect_Accept_URIs_dirty), index, str);

    return true;
}

char *Network_Port_SC_Direct_Connect_Accept_URIs_char(uint32_t object_instance)
{
    char *p = NULL;
    BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance);

    if (params) {
        p = params->SC_Direct_Connect_Accept_URIs;
    }

    return p;
}

bool Network_Port_SC_Direct_Connect_Accept_URIs_Set(
    uint32_t object_instance, const char *str)
{
    bool status = false;
    BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance);

    if (params) {
        if (str) {
            snprintf(
                params->SC_Direct_Connect_Accept_URIs,
                sizeof(params->SC_Direct_Connect_Accept_URIs), "%s", str);
        } else {
            params->SC_Direct_Connect_Accept_URIs[0] = 0;
        }
    }

    return status;
}

bool Network_Port_SC_Direct_Connect_Accept_URIs_Dirty_Set(
    uint32_t object_instance, const char *str)
{
    bool status = false;
    BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance);

    if (params) {
        if (str) {
            snprintf(
                params->SC_Direct_Connect_Accept_URIs_dirty,
                sizeof(params->SC_Direct_Connect_Accept_URIs_dirty), "%s", str);
        } else {
            params->SC_Direct_Connect_Accept_URIs_dirty[0] = 0;
        }
    }

    return status;
}

bool Network_Port_SC_Direct_Connect_Binding(
    uint32_t object_instance, BACNET_CHARACTER_STRING *str)
{
    bool status = false;
    BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance);
    char tmp[sizeof(params->SC_Direct_Connect_Binding)];

    if (params) {
        snprintf(
            tmp, sizeof(tmp), "%s:%d",
            params->SC_Direct_Connect_Binding + sizeof(uint16_t),
            *(uint16_t *)params->SC_Direct_Connect_Binding);

        status = characterstring_init_ansi(str, tmp);
    }

    return status;
}

void Network_Port_SC_Direct_Connect_Binding_get(
    uint32_t object_instance, uint16_t *port, char **ifname)
{
    BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance);

    *port = 0;
    *ifname = NULL;
    if (params && params->SC_Direct_Connect_Binding[0]) {
        *port = *(uint16_t *)params->SC_Direct_Connect_Binding;
        *ifname = params->SC_Direct_Connect_Binding + sizeof(uint16_t);
        if (**ifname == 0) {
            *ifname = NULL;
        }
    }
}

bool Network_Port_SC_Direct_Connect_Binding_Set(
    uint32_t object_instance, const char *str)
{
    BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance);
    uint16_t port;
    char *ifname;
    char tmp[BACNET_BINDING_STRING_LENGTH];

    if (!params) {
        return false;
    }

    if (str && str[0]) {
        bsc_copy_str(tmp, str, sizeof(tmp));
        sc_binding_parse(tmp, &port, &ifname);
        *(uint16_t *)params->SC_Direct_Connect_Binding = port;
        if (ifname) {
            bsc_copy_str(
                params->SC_Direct_Connect_Binding + sizeof(uint16_t), ifname,
                sizeof(params->SC_Direct_Connect_Binding) - sizeof(uint16_t));
        } else {
            *(params->SC_Direct_Connect_Binding + sizeof(uint16_t)) = 0;
        }
    } else {
        memset(
            params->SC_Direct_Connect_Binding, 0,
            sizeof(params->SC_Direct_Connect_Binding));
    }

    return true;
}

bool Network_Port_SC_Direct_Connect_Binding_Dirty_Set(
    uint32_t object_instance, const char *str)
{
    BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance);
    if (!params) {
        return false;
    }

    if (str) {
        snprintf(
            params->SC_Direct_Connect_Binding_dirty,
            sizeof(params->SC_Direct_Connect_Binding_dirty), "%s", str);
    } else {
        params->SC_Direct_Connect_Binding_dirty[0] = 0;
    }

    Network_Port_Changes_Pending_Set(object_instance, true);

    return true;
}

BACNET_SC_DIRECT_CONNECTION_STATUS *
Network_Port_SC_Direct_Connect_Connection_Status_Get(
    uint32_t object_instance, uint8_t index)
{
    BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance);
    if (!params) {
        return NULL;
    }

    if (index >= params->SC_Direct_Connect_Connection_Status_Count) {
        return NULL;
    }

    return &params->SC_Direct_Connect_Connection_Status[index];
}

bool Network_Port_SC_Direct_Connect_Connection_Status_Add(
    uint32_t object_instance,
    const char *uri,
    BACNET_SC_CONNECTION_STATE state,
    BACNET_DATE_TIME *connect_ts,
    BACNET_DATE_TIME *disconnect_ts,
    BACNET_HOST_N_PORT_DATA *peer_address,
    uint8_t *peer_VMAC,
    uint8_t *peer_UUID,
    BACNET_ERROR_CODE error,
    const char *error_details)
{
    BACNET_SC_DIRECT_CONNECTION_STATUS *st;
    BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance);
    if (!params) {
        return false;
    }

    if (params->SC_Direct_Connect_Connection_Status_Count + 1 >
        ARRAY_SIZE(params->SC_Direct_Connect_Connection_Status)) {
        return false;
    }

    st = &params->SC_Direct_Connect_Connection_Status
              [params->SC_Direct_Connect_Connection_Status_Count];
    params->SC_Direct_Connect_Connection_Status_Count += 1;

    if (uri) {
        bsc_copy_str(st->URI, uri, sizeof(st->URI));
    } else {
        st->URI[0] = 0;
    }
    st->State = state;
    st->Connect_Timestamp = *connect_ts;
    st->Disconnect_Timestamp = *disconnect_ts;

    memcpy(&st->Peer_Address, peer_address, sizeof(st->Peer_Address));
    memcpy(st->Peer_VMAC, peer_VMAC, sizeof(st->Peer_VMAC));
    memcpy(
        st->Peer_UUID.uuid.uuid128, peer_UUID,
        sizeof(st->Peer_UUID.uuid.uuid128));
    st->Error = error;
    if (error_details) {
        bsc_copy_str(
            st->Error_Details, error_details, sizeof(st->Error_Details));
    } else {
        st->Error_Details[0] = 0;
    }

    return true;
}

bool Network_Port_SC_Direct_Connect_Connection_Status_Delete_All(
    uint32_t object_instance)
{
    BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance);
    if (!params) {
        return false;
    }

    params->SC_Direct_Connect_Connection_Status_Count = 0;

    return true;
}

int Network_Port_SC_Direct_Connect_Connection_Status_Count(
    uint32_t object_instance)
{
    BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance);
    if (!params) {
        return 0;
    }

    return params->SC_Direct_Connect_Connection_Status_Count;
}

/**
 * @brief Handle a request to encode all the the entries
 * @param object_instance [in] BACnet network port object instance number
 * @param apdu [out] Buffer in which the APDU contents are built.
 * @param max_apdu [in] Max length of the APDU buffer.
 *
 * @return How many bytes were encoded in the buffer, or
 *   BACNET_STATUS_ABORT if the response would not fit within the buffer.
 */
int Network_Port_SC_Direct_Connect_Connection_Status_Encode(
    uint32_t object_instance, uint8_t *apdu, int max_apdu)
{
    BACNET_SC_DIRECT_CONNECTION_STATUS *entry = NULL;
    int apdu_len = 0;
    unsigned index = 0;
    unsigned size = 0;

    size =
        Network_Port_SC_Direct_Connect_Connection_Status_Count(object_instance);
    for (index = 0; index < size; index++) {
        entry = Network_Port_SC_Direct_Connect_Connection_Status_Get(
            object_instance, index);
        apdu_len += bacapp_encode_SCDirectConnection(NULL, entry);
    }
    if (apdu_len > max_apdu) {
        return BACNET_STATUS_ABORT;
    }
    apdu_len = 0;
    for (index = 0; index < size; index++) {
        entry = Network_Port_SC_Direct_Connect_Connection_Status_Get(
            object_instance, index);
        apdu_len += bacapp_encode_SCDirectConnection(&apdu[apdu_len], entry);
    }

    return apdu_len;
}
#endif /* BSC_CONF_HUB_CONNECTORS_NUM!=0 */

BACNET_SC_FAILED_CONNECTION_REQUEST *
Network_Port_SC_Failed_Connection_Requests_Get(
    uint32_t object_instance, uint8_t index)
{
    BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance);
    if (!params) {
        return NULL;
    }

    if (index >= params->SC_Failed_Connection_Requests_Count) {
        return NULL;
    }

    return &params->SC_Failed_Connection_Requests[index];
}

bool Network_Port_SC_Failed_Connection_Requests_Add(
    uint32_t object_instance,
    BACNET_DATE_TIME *ts,
    BACNET_HOST_N_PORT_DATA *peer_address,
    uint8_t *peer_VMAC,
    uint8_t *peer_UUID,
    BACNET_ERROR_CODE error,
    const char *error_details)
{
    BACNET_SC_FAILED_CONNECTION_REQUEST *entry;
    BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance);
    if (!params) {
        return false;
    }

    if (params->SC_Failed_Connection_Requests_Count + 1 >
        ARRAY_SIZE(params->SC_Failed_Connection_Requests)) {
        return false;
    }

    entry = &params->SC_Failed_Connection_Requests
                 [params->SC_Failed_Connection_Requests_Count];
    params->SC_Failed_Connection_Requests_Count += 1;

    entry->Timestamp = *ts;
    memcpy(&entry->Peer_Address, peer_address, sizeof(entry->Peer_Address));
    memcpy(entry->Peer_VMAC, peer_VMAC, sizeof(entry->Peer_VMAC));
    memcpy(
        entry->Peer_UUID.uuid.uuid128, peer_UUID,
        sizeof(entry->Peer_UUID.uuid.uuid128));
    entry->Error = error;
    if (error_details) {
        bsc_copy_str(
            entry->Error_Details, error_details, sizeof(entry->Error_Details));
    } else {
        entry->Error_Details[0] = 0;
    }

    return true;
}

bool Network_Port_SC_Failed_Connection_Requests_Delete_All(
    uint32_t object_instance)
{
    BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance);
    if (!params) {
        return false;
    }

    params->SC_Failed_Connection_Requests_Count = 0;

    return true;
}

int Network_Port_SC_Failed_Connection_Requests_Count(uint32_t object_instance)
{
    BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance);
    if (!params) {
        return 0;
    }

    return params->SC_Failed_Connection_Requests_Count;
}

/**
 * @brief Handle a request to encode all the the entries
 * @param object_instance [in] BACnet network port object instance number
 * @param apdu [out] Buffer in which the APDU contents are built.
 * @param max_apdu [in] Max length of the APDU buffer.
 * @return How many bytes were encoded in the buffer, or
 *   BACNET_STATUS_ABORT if the response would not fit within the buffer.
 */
int Network_Port_SC_Failed_Connection_Requests_Encode(
    uint32_t object_instance, uint8_t *apdu, int max_apdu)
{
    BACNET_SC_FAILED_CONNECTION_REQUEST *entry = NULL;
    int apdu_len = 0;
    unsigned index = 0;
    unsigned size = 0;

    size = Network_Port_SC_Failed_Connection_Requests_Count(object_instance);
    for (index = 0; index < size; index++) {
        entry = Network_Port_SC_Failed_Connection_Requests_Get(
            object_instance, index);
        apdu_len += bacapp_encode_SCFailedConnectionRequest(NULL, entry);
    }
    if (apdu_len > max_apdu) {
        return BACNET_STATUS_ABORT;
    }
    apdu_len = 0;
    for (index = 0; index < size; index++) {
        entry = Network_Port_SC_Failed_Connection_Requests_Get(
            object_instance, index);
        apdu_len +=
            bacapp_encode_SCFailedConnectionRequest(&apdu[apdu_len], entry);
    }

    return apdu_len;
}

uint32_t Network_Port_Certificate_Key_File(uint32_t object_instance)
{
    uint32_t id = 0;
    BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance);
    if (!params) {
        return 0;
    }

    id = params->Certificate_Key_File;

    return id;
}

bool Network_Port_Certificate_Key_File_Set(
    uint32_t object_instance, uint32_t value)
{
    BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance);
    if (!params) {
        return false;
    }

    params->Certificate_Key_File = value;

    return true;
}

const BACNET_UUID *Network_Port_SC_Local_UUID(uint32_t object_instance)
{
    const BACNET_UUID *uuid = NULL;
    BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance);
    if (!params) {
        return NULL;
    }

    uuid = &params->Local_UUID;

    return uuid;
}

bool Network_Port_SC_Local_UUID_Set(
    uint32_t object_instance, BACNET_UUID *value)
{
    BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance);
    if (!params) {
        return false;
    }

    memcpy(&params->Local_UUID, value, sizeof(*value));

    return true;
}

void Network_Port_SC_Pending_Params_Apply(uint32_t object_instance)
{
    BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance);
    if (!params) {
        return;
    }

    params->Max_BVLC_Length_Accepted = params->Max_BVLC_Length_Accepted_dirty;
    params->Max_NPDU_Length_Accepted = params->Max_NPDU_Length_Accepted_dirty;
    params->SC_Minimum_Reconnect_Time = params->SC_Minimum_Reconnect_Time_dirty;
    params->SC_Maximum_Reconnect_Time = params->SC_Maximum_Reconnect_Time_dirty;
    params->SC_Connect_Wait_Timeout = params->SC_Connect_Wait_Timeout_dirty;
    params->SC_Disconnect_Wait_Timeout =
        params->SC_Disconnect_Wait_Timeout_dirty;
    params->SC_Heartbeat_Timeout = params->SC_Heartbeat_Timeout_dirty;

#if BSC_CONF_HUB_FUNCTIONS_NUM != 0
    memcpy(
        params->SC_Primary_Hub_URI, params->SC_Primary_Hub_URI_dirty,
        sizeof(params->SC_Primary_Hub_URI));
    memcpy(
        params->SC_Failover_Hub_URI, params->SC_Failover_Hub_URI_dirty,
        sizeof(params->SC_Failover_Hub_URI));

    params->SC_Hub_Function_Enable = params->SC_Hub_Function_Enable_dirty;

    memcpy(
        params->SC_Hub_Function_Accept_URIs,
        params->SC_Hub_Function_Accept_URIs_dirty,
        sizeof(params->SC_Hub_Function_Accept_URIs));

    Network_Port_SC_Hub_Function_Binding_Set(
        object_instance, params->SC_Hub_Function_Binding_dirty);
#endif /* BSC_CONF_HUB_FUNCTIONS_NUM!=0 */

#if BSC_CONF_HUB_CONNECTORS_NUM != 0
    params->SC_Direct_Connect_Initiate_Enable =
        params->SC_Direct_Connect_Initiate_Enable_dirty;
    params->SC_Direct_Connect_Accept_Enable =
        params->SC_Direct_Connect_Accept_Enable_dirty;

    memcpy(
        params->SC_Direct_Connect_Accept_URIs,
        params->SC_Direct_Connect_Accept_URIs_dirty,
        sizeof(params->SC_Direct_Connect_Accept_URIs));

    Network_Port_SC_Direct_Connect_Binding_Set(
        object_instance, params->SC_Direct_Connect_Binding_dirty);
#endif /* BSC_CONF_HUB_CONNECTORS_NUM!=0 */
}

void Network_Port_SC_Pending_Params_Discard(uint32_t object_instance)
{
    BACNET_SC_PARAMS *params = Network_Port_SC_Params(object_instance);
    uint16_t port = 0;
    char *ifname = NULL;

    if (!params) {
        return;
    }
    params->Max_BVLC_Length_Accepted_dirty = params->Max_BVLC_Length_Accepted;
    params->Max_NPDU_Length_Accepted_dirty = params->Max_NPDU_Length_Accepted;
    params->SC_Minimum_Reconnect_Time_dirty = params->SC_Minimum_Reconnect_Time;
    params->SC_Maximum_Reconnect_Time_dirty = params->SC_Maximum_Reconnect_Time;
    params->SC_Connect_Wait_Timeout_dirty = params->SC_Connect_Wait_Timeout;
    params->SC_Disconnect_Wait_Timeout_dirty =
        params->SC_Disconnect_Wait_Timeout;
    params->SC_Heartbeat_Timeout_dirty = params->SC_Heartbeat_Timeout;
#if BSC_CONF_HUB_FUNCTIONS_NUM != 0
    memcpy(
        params->SC_Primary_Hub_URI_dirty, params->SC_Primary_Hub_URI,
        sizeof(params->SC_Primary_Hub_URI_dirty));
    memcpy(
        params->SC_Failover_Hub_URI_dirty, params->SC_Failover_Hub_URI,
        sizeof(params->SC_Failover_Hub_URI_dirty));

    params->SC_Hub_Function_Enable_dirty = params->SC_Hub_Function_Enable;

    memcpy(
        params->SC_Hub_Function_Accept_URIs_dirty,
        params->SC_Hub_Function_Accept_URIs,
        sizeof(params->SC_Hub_Function_Accept_URIs));

    Network_Port_SC_Hub_Function_Binding_get(object_instance, &port, &ifname);
    snprintf(
        params->SC_Hub_Function_Binding_dirty,
        sizeof(params->SC_Hub_Function_Binding_dirty), "%s:%d", ifname, port);
#else
    (void)port;
    (void)ifname;
#endif /* BSC_CONF_HUB_FUNCTIONS_NUM!=0 */

#if BSC_CONF_HUB_CONNECTORS_NUM != 0
    params->SC_Direct_Connect_Initiate_Enable_dirty =
        params->SC_Direct_Connect_Initiate_Enable;
    params->SC_Direct_Connect_Accept_Enable_dirty =
        params->SC_Direct_Connect_Accept_Enable;

    memcpy(
        params->SC_Direct_Connect_Accept_URIs_dirty,
        params->SC_Direct_Connect_Accept_URIs,
        sizeof(params->SC_Direct_Connect_Accept_URIs));

    Network_Port_SC_Direct_Connect_Binding_get(object_instance, &port, &ifname);
    snprintf(
        params->SC_Direct_Connect_Binding_dirty,
        sizeof(params->SC_Direct_Connect_Binding_dirty), "%s:%d", ifname, port);
#endif /* BSC_CONF_HUB_CONNECTORS_NUM!=0 */
}
