/*
 * Copyright (c) 2014-2019, Arm Limited and affiliates.
 * SPDX-License-Identifier: BSD-3-Clause
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the copyright holder nor the
 *    names of its contributors may be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */
#include "nsconfig.h"
#include <string.h>
#include "ns_types.h"
#include "mlme.h"
#include "NWK_INTERFACE/Include/protocol.h"
#include "thread_management_if.h"
#include <nsdynmemLIB.h>
#include "eventOS_event.h"
#include <ns_list.h>
#include <net_thread_test.h>
#include <net_ipv6_api.h>
#include "ns_trace.h"
#include "Core/include/ns_buffer.h"
#include "common_functions.h"
#include "6LoWPAN/Thread/thread_config.h"
#include "6LoWPAN/Thread/thread_common.h"
#include "6LoWPAN/Thread/thread_bootstrap.h"
#include "6LoWPAN/Thread/thread_border_router_api_internal.h"
#include "6LoWPAN/Thread/thread_routing.h"
#include "6LoWPAN/Thread/thread_network_data_lib.h"
#include "6LoWPAN/Thread/thread_network_data_storage.h"
#include "6LoWPAN/Thread/thread_leader_service.h"
#include "6LoWPAN/Thread/thread_nd.h"
#include "thread_diagnostic.h"
#include "DHCPv6_client/dhcpv6_client_api.h"
#include "6LoWPAN/Thread/thread_discovery.h"
#include "6LoWPAN/Thread/thread_network_synch.h"
#include "6LoWPAN/Thread/thread_management_internal.h"
#include "6LoWPAN/Thread/thread_management_server.h"
#include "6LoWPAN/Thread/thread_joiner_application.h"
#include "6LoWPAN/Thread/thread_management_client.h"
#include "6LoWPAN/Thread/thread_nvm_store.h"
#include "6LoWPAN/Thread/thread_ccm.h"
#include "6LoWPAN/Thread/thread_tmfcop_lib.h"
#include "6LoWPAN/Thread/thread_constants.h"
#include "6LoWPAN/Thread/thread_bbr_api_internal.h"
#include "6LoWPAN/Bootstraps/protocol_6lowpan.h"
#include "Service_Libs/mle_service/mle_service_security.h"
#include "RPL/rpl_control.h" // insanity - bootstraps shouldn't be doing each others' clean-up
#include "MLE/mle.h"
#include "MLE/mle_tlv.h"
#include "thread_meshcop_lib.h"
#include "thread_commissioning_if.h"
#include "shalib.h"
#include "Common_Protocols/icmpv6.h"
#include "DHCPv6_Server/DHCPv6_server_service.h"
#include "6LoWPAN/Thread/thread_dhcpv6_server.h"
#include "Service_Libs/mle_service/mle_service_api.h"
#include "Service_Libs/blacklist/blacklist.h"
#include "6LoWPAN/MAC/mac_helper.h"
#include "6LoWPAN/MAC/mac_pairwise_key.h"
#include "6LoWPAN/MAC/mpx_api.h"
#include "6LoWPAN/lowpan_adaptation_interface.h"
#include "Service_Libs/mac_neighbor_table/mac_neighbor_table.h"
#include "mac_common_defines.h"
#include "mlme.h"
#include "mac_api.h"

#ifdef HAVE_THREAD
#define TRACE_GROUP "thrm"

static const uint8_t thread_discovery_key[16] = {0x78, 0x58, 0x16, 0x86, 0xfd, 0xb4, 0x58, 0x0f, 0xb0, 0x92, 0x54, 0x6a, 0xec, 0xbd, 0x15, 0x66};
static const uint8_t thread_discovery_extented_address[8] = {0x35, 0x06, 0xfe, 0xb8, 0x23, 0xd4, 0x87, 0x12};

uint32_t thread_delay_timer_default = THREAD_DELAY_TIMER_DEFAULT_SECONDS;
uint32_t thread_router_selection_jitter = THREAD_ROUTER_SELECTION_JITTER;
uint16_t thread_joiner_port = THREAD_DEFAULT_JOINER_PORT;
uint8_t thread_max_mcast_addr = THREAD_MCAST_ADDR_PER_MSG;

/*
 * Prototypes
 */

static void thread_discover_key_descriptor_set(struct mac_api_s *api, const uint8_t *key, uint8_t id, uint32_t key32_bit_src, uint8_t attribute_index)
{
    mlme_set_t set_req;
    mlme_key_id_lookup_descriptor_t lookup_description;
    mlme_key_descriptor_entry_t key_description;
    mlme_key_device_descriptor_t dev_descriptor;
    if (key) {
        memset(lookup_description.LookupData, 0, 9);
        common_write_32_bit(key32_bit_src, lookup_description.LookupData);
        lookup_description.LookupData[4] = id;
        lookup_description.LookupDataSize = 0;
        memset(&key_description, 0, sizeof(mlme_key_descriptor_entry_t));
        memcpy(key_description.Key, key, 16);
        key_description.KeyIdLookupList = &lookup_description;
        key_description.KeyIdLookupListEntries = 1;
        dev_descriptor.Blacklisted = false;
        dev_descriptor.DeviceDescriptorHandle = attribute_index;
        dev_descriptor.UniqueDevice = true;
        key_description.KeyDeviceList = &dev_descriptor;
        key_description.KeyDeviceListEntries = 1;

    } else {
        memset(&key_description, 0, sizeof(mlme_key_descriptor_entry_t));
    }
    set_req.attr = macKeyTable;
    set_req.attr_index = 3; //Allwayd firth key
    set_req.value_pointer = &key_description;
    set_req.value_size = sizeof(mlme_key_descriptor_entry_t);

    api->mlme_req(api, MLME_SET, &set_req);
}

static void thread_discover_device_descriptor_set(struct mac_api_s *api, const uint8_t *device_extended_address, uint8_t attribute_index)
{
    if (!api) {
        return;
    }

    mlme_device_descriptor_t device_desc;
    mlme_set_t set_req;
    device_desc.FrameCounter = 0;
    device_desc.Exempt = false;
    device_desc.ShortAddress = 0xffff;
    memcpy(device_desc.ExtAddress, device_extended_address, 8);
    device_desc.PANId = 0xffff;


    set_req.attr = macDeviceTable;
    set_req.attr_index = attribute_index;
    set_req.value_pointer = (void *)&device_desc;
    set_req.value_size = sizeof(mlme_device_descriptor_t);
    tr_debug("Register Discovery device descriptor");
    api->mlme_req(api, MLME_SET, &set_req);
}

static void thread_discover_security_material_update(protocol_interface_info_entry_t *cur, const mlme_security_t *security_params)
{
    if (!security_params || !cur) {
        return;
    }

    if (security_params->SecurityLevel != SEC_ENC_MIC32 || security_params->KeyIdMode != MAC_KEY_ID_MODE_SRC4_IDX || security_params->KeyIndex != THREAD_DISCOVERY_SECURITY_KEY_INDEX) {
        return;
    }
    mac_description_storage_size_t buffer;
    if (common_read_32_bit(security_params->Keysource) != THREAD_DISCOVERY_SECURITY_KEY_SOURCE) {
        return;
    }

    if (!cur->mac_api || !cur->mac_api->mac_storage_sizes_get || cur->mac_api->mac_storage_sizes_get(cur->mac_api, &buffer) != 0) {
        return;
    }
    thread_discover_device_descriptor_set(cur->mac_api, thread_discovery_extented_address, buffer.device_decription_table_size - 1);
}

static void thread_security_trig_pending_key(protocol_interface_info_entry_t *cur)
{
    if (cur->mac_parameters->mac_next_key_index) {

        //Call MAC Key Update
        uint8_t key_id = cur->mac_parameters->mac_next_key_index;

        mac_helper_security_key_swap_next_to_default(cur);

        if (mle_service_security_key_trig(cur->id, key_id)) {
            thread_security_update_from_mac(cur);
        }
    }
}

static void thread_mac_security_key_update_cb(protocol_interface_info_entry_t *cur, const mlme_security_t *security_params)
{
    if (!cur->thread_info || !cur->mac_parameters) {
        return;
    }
    if (cur->mac_parameters->mac_next_key_index && (security_params->KeyIndex == cur->mac_parameters->mac_next_key_index)) {
        if (cur->thread_info->masterSecretMaterial.keySwitchGuardTimer == 0) {
            tr_debug("Trig Next Key");
            thread_security_trig_pending_key(cur);
        }
    } else {
        thread_discover_security_material_update(cur, security_params);
    }
}

int8_t thread_node_bootstrap_init(int8_t interface_id, net_6lowpan_mode_e bootstrap_mode)
{
    protocol_interface_info_entry_t *cur;
    uint8_t table_size = 4;

    cur = protocol_stack_interface_info_get_by_id(interface_id);
    if (!cur) {
        return -1;
    }

    //Read MAC device table sizes
    mac_description_storage_size_t buffer;
    if (!cur->mac_api || !cur->mac_api->mac_storage_sizes_get || cur->mac_api->mac_storage_sizes_get(cur->mac_api, &buffer) != 0) {
        return -1;
    }

    cur->configure_flags &= ~INTERFACE_BOOTSTRAP_DEFINED;
    mac_pairwise_key_interface_unregister(cur->id);

    if (buffer.key_description_table_size < 4) {
        tr_error("MAC key_description_table_size too short %d<4", buffer.key_description_table_size);
        return -1;
    }

    switch (bootstrap_mode) {
        case NET_6LOWPAN_HOST:
        case NET_6LOWPAN_SLEEPY_HOST:
            protocol_6lowpan_host_init(cur, bootstrap_mode == NET_6LOWPAN_SLEEPY_HOST);
            break;

        case NET_6LOWPAN_ROUTER:
            protocol_6lowpan_router_init(cur);
            table_size = buffer.key_description_table_size;
            break;

        default:
            return -3;
    }

    if (mac_pairwise_key_interface_register(cur->id, table_size, 4) < 0) {
        tr_error("MAC pairwise key in registration failed");
        return -1;
    }

    if (blacklist_init() != 0) {
        tr_debug("6LoWPAN MLE blacklist init failed.");
        return -1;
    }

    blacklist_params_set(
        THREAD_BLACKLIST_ENTRY_LIFETIME,
        THREAD_BLACKLIST_TIMER_MAX_TIMEOUT,
        THREAD_BLACKLIST_TIMER_TIMEOUT,
        THREAD_BLACKLIST_ENTRY_MAX_NBR,
        THREAD_BLACKLIST_PURGE_NBR,
        THREAD_BLACKLIST_PURGE_TIMER_TIMEOUT);

    if (thread_info_allocate_and_init(cur) < 0) {
        mac_pairwise_key_interface_unregister(cur->id);
        return -3;
    }

    if (thread_discovery_init(cur->id, cur, thread_info(cur)->version, bootstrap_mode == NET_6LOWPAN_ROUTER) != 0) {
        tr_error("Discovery init fail");
        mac_pairwise_key_interface_unregister(cur->id);
        return -3;
    }

    cur->configure_flags |= INTERFACE_BOOTSTRAP_DEFINED;
    cur->lowpan_info |= INTERFACE_NWK_BOOTSRAP_MLE;
    rpl_control_remove_domain_from_interface(cur);
    //SET MAC key id mode 2 key and device
    thread_discover_key_descriptor_set(cur->mac_api, thread_discovery_key, THREAD_DISCOVERY_SECURITY_KEY_INDEX, THREAD_DISCOVERY_SECURITY_KEY_SOURCE, buffer.device_decription_table_size - 1);

    thread_discover_device_descriptor_set(cur->mac_api, thread_discovery_extented_address, buffer.device_decription_table_size - 1);

    cur->mac_security_key_usage_update_cb = thread_mac_security_key_update_cb;
    return 0;
}


void arm_thread_private_ula_prefix_set(protocol_interface_info_entry_t *cur, const uint8_t *ula_prefix)
{
    //Define Local Thread
    memcpy(cur->thread_info->threadPrivatePrefixInfo.ulaPrefix, ula_prefix, 8);
    cur->thread_info->threadPrivatePrefixInfo.ulaValid = true;
}

/**
 * Calculate New Key Material by given security key material and sequency number
 *
 * \param key Thread Master key pointer
 * \param key_material_buf Buffer for calculated MAC & MLE key: first 128-bit for MLE and rest 128-bit for MAC
 * \param threadSequency Key material key index.
 *
 */
void thread_key_get(uint8_t *key, uint8_t *key_material_buf,  uint32_t key_sequence_counter)
{
    static uint8_t key_seed[6] = {'T', 'h', 'r', 'e', 'a', 'd'};
    static uint8_t temp32_buf[4];
    common_write_32_bit(key_sequence_counter, temp32_buf);
    SHALIB_init_HMAC(key, 16);
    SHALIB_push_data_HMAC(temp32_buf, 4);
    SHALIB_push_data_HMAC(key_seed, 6);
    SHALIB_finish_HMAC(key_material_buf, 8);
}

/**
 * Thread key sequence & key synch
 *
 */
void thread_management_key_synch_req(int8_t interface_id, uint32_t keySequnce)
{
    protocol_interface_info_entry_t *cur;
    link_configuration_s *linkConfiguration;
    linkConfiguration = thread_joiner_application_get_config(interface_id);
    if (!linkConfiguration) {
        return;
    }
    cur = protocol_stack_interface_info_get_by_id(interface_id);
    if (!cur || !cur->thread_info) {
        return;
    }
    if (!cur->thread_info->masterSecretMaterial.valid_Info) {
        return;
    }
    //tr_debug("Sync key material by %"PRIu32, keySequnce);

    if (keySequnce <= linkConfiguration->key_sequence) {
        // Smaller or equal request ignored
        //tr_debug("Sync key material no change");
        return;
    }
    if ((cur->thread_info->masterSecretMaterial.keySwitchGuardTimer > 0)) {
        // Guard time prevent the change
        //tr_debug("Sync key material guard blocked");
        return;
    }
    // Calculate new keys
    tr_debug("Sync key material by %"PRIu32, keySequnce);
    thread_management_key_sets_calc(cur, linkConfiguration, keySequnce);
    thread_key_guard_timer_calculate(cur, linkConfiguration, false);
}

static void thread_history_key_material_push(thread_info_t *thread_info, uint8_t *mleKeyPtr, uint8_t keyId)
{
    thread_info->masterSecretMaterial.historyKeyValid = true;
    thread_info->masterSecretMaterial.historyKeyId = keyId;
    memcpy(thread_info->masterSecretMaterial.historyKey, mleKeyPtr, 16);
}

void thread_security_update_from_mac(protocol_interface_info_entry_t *cur)
{
    uint8_t *mleKey;
    uint8_t historyKeyId;
    link_configuration_s *linkConfiguration;
    linkConfiguration = thread_joiner_application_get_config(cur->id);
    if (!linkConfiguration) {
        return;
    }

    mleKey = mle_service_security_next_key_get(cur->id);
    if (!mleKey) {
        return;
    }
    historyKeyId = mle_service_security_next_key_id_get(cur->id);

    /* Set old secondary to history */
    thread_history_key_material_push(cur->thread_info, mleKey, historyKeyId);
    linkConfiguration->key_sequence++;
    mac_helper_link_frame_counter_set(cur->id, 0);
    thread_security_next_key_generate(cur, linkConfiguration->master_key, linkConfiguration->key_sequence);
    thread_key_guard_timer_calculate(cur, linkConfiguration, false);
}

void thread_security_key_generate(protocol_interface_info_entry_t *cur, uint8_t *masterKey, uint32_t keySequence)
{
    uint8_t key_material[32];
    uint8_t key_index;
    //primary key is generated from the current value of sequence counter
    uint32_t thrKeySequenceCounter = keySequence;
    /* Produced keys from Thread security material: MAC key | MLE key */
    thread_key_get(masterKey, key_material, thrKeySequenceCounter);
    /* Update keys as primary keys */
    key_index = THREAD_KEY_INDEX(thrKeySequenceCounter);
    tr_debug("Set current key Id: %u", key_index);
    mac_helper_security_default_key_set(cur, &key_material[16], key_index, MAC_KEY_ID_MODE_IDX);
    mle_service_security_set_security_key(cur->id, key_material, key_index, true);
}


void thread_security_prev_key_generate(protocol_interface_info_entry_t *cur, uint8_t *masterKey, uint32_t keySequence)
{
    uint8_t key_material[32];
    uint8_t key_index;
    uint32_t thrKeySequenceCounter;
    if (keySequence == 0) {
        // in initial value there is no prev available
        return;
    }
    thrKeySequenceCounter = keySequence - 1;
    /* Produced keys from Thread security material: MAC key | MLE key */
    thread_key_get(masterKey, key_material, thrKeySequenceCounter);
    /* Update keys as primary keys */
    key_index = THREAD_KEY_INDEX(thrKeySequenceCounter);
    tr_debug("Set previous key Id: %u", key_index);
    mac_helper_security_prev_key_set(cur, &key_material[16], key_index, MAC_KEY_ID_MODE_IDX);
    mle_service_security_set_security_key(cur->id, key_material, key_index, false);
    //copy master secret material to history
    thread_history_key_material_push(cur->thread_info, key_material, key_index);
}

void thread_security_next_key_generate(protocol_interface_info_entry_t *cur, uint8_t *masterKey, uint32_t keySequence)
{
    uint8_t key_material[32];
    uint8_t key_index;
    uint32_t thrKeySequenceCounter;
    /* Will wrap modulo 32 bits */
    thrKeySequenceCounter = keySequence + 1;
    /* Produced keys from Thread security material: MAC key | MLE key */
    thread_key_get(masterKey, key_material, thrKeySequenceCounter);
    /* Update keys as non-primary keys */
    key_index = THREAD_KEY_INDEX(thrKeySequenceCounter);
    tr_debug("Set next key Id: %u", key_index);
    mac_helper_security_next_key_set(cur, &key_material[16], key_index, MAC_KEY_ID_MODE_IDX); /* Auth counter not relevant for Thread */
    mle_service_security_set_security_key(cur->id, key_material, key_index, false);
}

int thread_management_key_sets_calc(protocol_interface_info_entry_t *cur, link_configuration_s *linkConfiguration, uint32_t thrKeySequenceCounter)
{
    int ret_val = -1;

    if (thrKeySequenceCounter == linkConfiguration->key_sequence) {
        /* Same key - do not change */
        ret_val = 0;
    } else if (thrKeySequenceCounter == (linkConfiguration->key_sequence + 1)) {
        /* Next key - trig pending key */
        thread_security_trig_pending_key(cur);
        ret_val = 0;
    } else {
        /* Generate new key set */
        //Clean All Keys
        mac_helper_security_key_clean(cur);

        // Update key sequence
        linkConfiguration->key_sequence = thrKeySequenceCounter;
        // Zero all frame counters. MLE does it automatically
        mac_helper_link_frame_counter_set(cur->id, 0);
        // Store all frame counters as zero and update the sequence counter
        thread_nvm_store_fast_data_write_all(0, 0, thrKeySequenceCounter);

        thread_security_prev_key_generate(cur, linkConfiguration->master_key, linkConfiguration->key_sequence);
        thread_security_key_generate(cur, linkConfiguration->master_key, linkConfiguration->key_sequence);
        thread_security_next_key_generate(cur, linkConfiguration->master_key, linkConfiguration->key_sequence);
        ret_val = 0;
    }
    return ret_val;
}

int thread_management_get_my_iid16(int8_t interface_id, uint8_t *iidPtr)
{
    protocol_interface_info_entry_t *cur;
    cur = protocol_stack_interface_info_get_by_id(interface_id);
    if (!cur) {
        return -1;
    }

    if (!cur->thread_info) {
        return -1;
    }

    if (thread_attach_ready(cur) != 0) {
        return -1;
    }

    if (!cur->thread_info->threadPrivatePrefixInfo.ulaValid) {
        return -1;
    }

    memcpy(iidPtr, ADDR_SHORT_ADR_SUFFIC, 6);
    common_write_16_bit(mac_helper_mac16_address_get(cur), (iidPtr + 6));

    return 0;
}

int thread_management_get_current_keysequence(int8_t interface_id, uint32_t *sequencePtr)
{

    link_configuration_s *linkConfiguration;
    linkConfiguration = thread_joiner_application_get_config(interface_id);
    if (!linkConfiguration) {
        return -1;
    }
    protocol_interface_info_entry_t *cur;
    cur = protocol_stack_interface_info_get_by_id(interface_id);
    if (!cur) {
        return -1;
    }

    if (!cur->thread_info) {
        return -1;
    }

    if (!cur->thread_info->masterSecretMaterial.valid_Info) {
        return -1;
    }

    *sequencePtr = linkConfiguration->key_sequence;

    return 0;
}

int thread_management_increment_key_sequence_counter(int8_t interface_id)
{
    protocol_interface_info_entry_t *cur;

    cur = protocol_stack_interface_info_get_by_id(interface_id);
    if (cur && cur->thread_info) {
        /* Not sure if this check is needed - not sure exactly what the flag means! */
        if ((cur->lowpan_info & INTERFACE_NWK_ACTIVE) == 0) {
            return -1;
        }

        if ((cur->configure_flags & INTERFACE_BOOTSTRAP_DEFINED) == 0) {
            return -1;
        }

        //Trig Pending Key
        thread_security_trig_pending_key(cur);
        return 0;
    }
    return -1;
}

int thread_management_get_ml_prefix(int8_t interface_id, uint8_t *prefix_ptr)
{
    // TODO get from static configuration
    protocol_interface_info_entry_t *cur;
    cur = protocol_stack_interface_info_get_by_id(interface_id);
    if (!cur || !prefix_ptr) {
        return -1;
    }

    if (!cur->thread_info) {
        return -1;
    }

    if (thread_attach_ready(cur) != 0) {
        return -1;
    }

    if (!cur->thread_info->threadPrivatePrefixInfo.ulaValid) {
        return -1;
    }

    memcpy(prefix_ptr, cur->thread_info->threadPrivatePrefixInfo.ulaPrefix, 8);

    return 0;
}

/* Get my mesh local prefix /112 */
int thread_management_get_ml_prefix_112(int8_t interface_id, uint8_t *prefix_ptr)
{
    protocol_interface_info_entry_t *cur;
    cur = protocol_stack_interface_info_get_by_id(interface_id);
    if (!cur || !prefix_ptr) {
        return -1;
    }

    if (!cur->thread_info) {
        return -1;
    }

    if (thread_attach_ready(cur) != 0) {
        return -1;
    }

    if (!cur->thread_info->threadPrivatePrefixInfo.ulaValid) {
        return -1;
    }

    memcpy(prefix_ptr, cur->thread_info->threadPrivatePrefixInfo.ulaPrefix, 8);
    memcpy(prefix_ptr + 8, ADDR_SHORT_ADR_SUFFIC, 6);

    return 0;
}

#endif

/**
 *  Public interface functions.
 */

/**
 * Set DHCPV6 server for Thread GP data purpose
 *
 * \param interface_id Network Interface
 * \param prefix_ptr pointer DHCPv6 Server Given Prefix
 *
 * return 0, Set OK
 * return <0 Set Not OK
 */
int thread_dhcpv6_server_add(int8_t interface_id, uint8_t *prefix_ptr, uint32_t max_client_cnt, bool stableData)
{
#if defined(HAVE_THREAD) && defined(HAVE_DHCPV6_SERVER)
    protocol_interface_info_entry_t *cur;
    thread_prefix_tlv_t prefixTlv;
    thread_border_router_tlv_entry_t service;
    uint8_t temp[16];
    uint8_t *ptr = temp;

    cur = protocol_stack_interface_info_get_by_id(interface_id);
    if (!cur) {
        return -1;
    }

    if (!cur->thread_info) {
        return -1;
    }

    if (thread_dhcp6_server_init(interface_id, prefix_ptr, cur->mac, THREAD_MIN_PREFIX_LIFETIME) != 0) {
        tr_warn("SerVER alloc fail");
        return -1;
    }

    prefixTlv.domainId = 0;
    prefixTlv.Prefix = prefix_ptr;
    prefixTlv.PrefixLen = 64;

    memset(&service, 0, sizeof(thread_border_router_tlv_entry_t));
    service.Prf = 1;
    service.P_dhcp = true;
    service.P_on_mesh = true;
    service.stableData = stableData;

    // SET maximum number of accepted clients
    DHCPv6_server_service_set_max_clients_accepts_count(interface_id, prefix_ptr, max_client_cnt);

    tr_debug("GUA server Generate OK");
    memcpy(ptr, prefix_ptr, 8);
    memset(ptr + 8, 0, 8);
    //Network Data
    if (thread_local_server_list_add_on_mesh_server(&cur->thread_info->localServerDataBase, &prefixTlv, &service) != 0) {
        return -1;
    }

    return 0;
#else
    (void) interface_id;
    (void) prefix_ptr;
    (void) max_client_cnt;
    (void) stableData;
    return -1;
#endif
}

int thread_dhcpv6_server_set_lifetime(int8_t interface_id, uint8_t *prefix_ptr, uint32_t valid_lifetime)
{
#if defined(HAVE_THREAD) && defined(HAVE_DHCPV6_SERVER)
    if (!prefix_ptr) {
        return -1;
    }

    return DHCPv6_server_service_set_address_validlifetime(interface_id, prefix_ptr, valid_lifetime);
#else
    (void) interface_id;
    (void) prefix_ptr;
    (void) valid_lifetime;
    return -1;
#endif
}

int thread_dhcpv6_server_set_max_client(int8_t interface_id, uint8_t *prefix_ptr, uint32_t max_client_count)
{
#if defined(HAVE_THREAD) && defined(HAVE_DHCPV6_SERVER)
    if (!prefix_ptr) {
        return -1;
    }

    return DHCPv6_server_service_set_max_clients_accepts_count(interface_id, prefix_ptr, max_client_count);
#else
    (void) interface_id;
    (void) prefix_ptr;
    (void) max_client_count;
    return -1;
#endif
}

int thread_dhcpv6_server_set_anonymous_addressing(int8_t interface_id, uint8_t *prefix_ptr, bool anonymous)
{
#if defined(HAVE_THREAD) && defined(HAVE_DHCPV6_SERVER)
    if (!prefix_ptr) {
        return -1;
    }

    return DHCPv6_server_service_set_address_autonous_flag(interface_id, prefix_ptr, anonymous);
#else
    (void) interface_id;
    (void) prefix_ptr;
    (void) anonymous;
    return -1;
#endif

}


int thread_dhcpv6_server_delete(int8_t interface_id, uint8_t *prefix_ptr)
{
#if defined(HAVE_THREAD) && defined(HAVE_DHCPV6_SERVER)
    uint8_t temp[16];
    protocol_interface_info_entry_t *cur;
    thread_prefix_tlv_t prefixTlv;
    tr_debug("GUA server Delete");
    cur = protocol_stack_interface_info_get_by_id(interface_id);
    if (!cur) {
        return -1;
    }

    if (!cur->thread_info) {
        return -1;
    }

    memcpy(temp, prefix_ptr, 8);
    memset(temp + 8, 0, 8);

    prefixTlv.domainId = 0;
    prefixTlv.Prefix = prefix_ptr;
    prefixTlv.PrefixLen = 64;
    //Delete dhcp service
    DHCPv6_server_service_delete(interface_id, prefix_ptr, false);
    ipv6_route_delete(temp, 64, cur->id, NULL, ROUTE_THREAD);
    thread_local_server_list_del_on_mesh_server(&cur->thread_info->localServerDataBase, &prefixTlv);

    return 0;
#else
    (void) interface_id;
    (void) prefix_ptr;
    return -1;
#endif
}

#ifdef HAVE_THREAD
static mac_neighbor_table_entry_t *neighbor_data_poll_referesh(protocol_interface_info_entry_t *cur, const uint8_t *address, addrtype_t type)
{
    mac_neighbor_table_entry_t *entry = mac_neighbor_table_address_discover(mac_neighbor_info(cur), address, type);
    if (!entry || !entry->connected_device) {
        return NULL;
    }

    entry->lifetime = entry->link_lifetime;
    return entry;
}
void thread_comm_status_indication_cb(int8_t if_id, const mlme_comm_status_t *status)
{
    protocol_interface_info_entry_t *cur = protocol_stack_interface_info_get_by_id(if_id);
    if (!cur) {
        return;
    }
    mac_neighbor_table_entry_t *entry;

    switch (status->status) {

        case MLME_SECURITY_FAIL:

            break;

        case MLME_COUNTER_ERROR:

            break;

        case MLME_DATA_POLL_NOTIFICATION:
            entry = neighbor_data_poll_referesh(cur, status->SrcAddr, (addrtype_t)status->SrcAddrMode);
            if (entry) {
                thread_neighbor_communication_update(cur, entry->index);
            }
            break;
        default:
            break;
    }
}
#endif

int thread_management_node_init(
    int8_t interface_id,
    channel_list_s *channel_list,
    device_configuration_s *device_configuration,
    link_configuration_s *static_configuration)
{
#ifdef HAVE_THREAD
    protocol_interface_info_entry_t *cur;
    nwk_scan_params_t *scan_params;

    if (!device_configuration) {
        return -1;
    }

    cur = protocol_stack_interface_info_get_by_id(interface_id);

    if (!cur) {
        tr_warn("Invalid interface id");
        return -1;
    }

    if (!cur->thread_info) {
        tr_warn("Not Thread specific interface");
        return -1;
    }

    if ((cur->lowpan_info & INTERFACE_NWK_ACTIVE) !=  0) {
        return -1;
    }

    if (cur->bootsrap_mode == ARM_NWK_BOOTSRAP_MODE_6LoWPAN_BORDER_ROUTER || cur->border_router_setup) {
        return -1;
    }

    if (thread_init(cur) < 0) {
        thread_nd_service_delete(cur->id);
        tr_warn("Thread Boostarp Init Fail");
        return -1;
    }
    if (thread_management_server_init(cur->id) != 0) {
        tr_warn("Thread management server init fail");
        return -1;
    }

    if (thread_diagnostic_init(cur->id) != 0) {
        tr_warn("Thread diagnostic init fail");
        return -1;
    }
    //Store setup to application Joiner Application
    if (thread_joiner_application_init(cur->id, device_configuration, static_configuration) != 0) {
        tr_error("mandatory device configuration missing");
        return -2;
    }

    // Joiner application copies and massages the device configuration - switch to massaged version
    device_configuration = thread_joiner_application_get_device_config(cur->id);
    if (!device_configuration) {
        return -1;
    }
    cur->if_lowpan_security_params->nwk_security_mode = NET_SEC_MODE_NO_LINK_SECURITY;
    cur->mac_parameters->mac_configured_sec_level = 0;

    // If no-one has already set a secret key, use the EUI-64 - Thread
    // wants RFC 7217 IIDs. Also ensure they're on for the Thread interface.
    // (Note that this will likely enable opaque IIDs on Ethernet too).
    if (!addr_opaque_iid_key_is_set()) {
        // Could we include some private key here?
        arm_nwk_ipv6_opaque_iid_key(device_configuration->eui64, 8);
        arm_nwk_ipv6_opaque_iid_enable(cur->id, true);
    }
    // Copy the channel list
    memset(&cur->mac_parameters->mac_channel_list, 0, sizeof(channel_list_s));
    if (channel_list) {
        // Application has given limited set of channels
        cur->mac_parameters->mac_channel_list = *channel_list;
    } else {
        cur->mac_parameters->mac_channel_list.channel_page = CHANNEL_PAGE_0;
        cur->mac_parameters->mac_channel_list.channel_mask[0] = 0x07FFF800;
    }

    scan_params = &cur->mac_parameters->nwk_scan_params;
    memset(&scan_params->stack_chan_list, 0, sizeof(channel_list_s));
    if (channel_list) {
        // Application has given limited set of channels
        scan_params->stack_chan_list = *channel_list;
    } else {
        scan_params->stack_chan_list.channel_page = CHANNEL_PAGE_0;
        scan_params->stack_chan_list.channel_mask[0] = 0x07FFF800;
    }
    scan_params->scan_duration = 5;

    cur->thread_info->masterSecretMaterial.valid_Info = false;
    thread_key_guard_timer_calculate(cur, static_configuration, true);

    cur->thread_info->maxChildCount = THREAD_MAX_CHILD_COUNT;
    cur->thread_info->rfc6775 = false;
    cur->thread_info->host_link_timeout = THREAD_END_DEVICE_DEFAULT_TIMEOUT;
    /* Thread will manage the address query timing, and report negatives. Set this high so as not to interfere. */
    cur->ipv6_neighbour_cache.retrans_timer = 10000;

    // Set default partition weighting
    cur->thread_info->partition_weighting = THREAD_DEFAULT_WEIGHTING;

    /* IP forwarding is off by default */
    cur->ip_forwarding = false;

    lowpan_adaptation_indirect_queue_params_set(cur,
                                                THREAD_INDIRECT_BIG_PACKET_THRESHOLD,
                                                THREAD_INDIRECT_BIG_PACKETS_TOTAL,
                                                THREAD_INDIRECT_SMALL_PACKETS_PER_CHILD);

    if (cur->bootsrap_mode == ARM_NWK_BOOTSRAP_MODE_6LoWPAN_SLEEPY_HOST) {
        cur->thread_info->requestFullNetworkData = false;
    } else {
        cur->thread_info->requestFullNetworkData = true;
    }

    cur->lowpan_info &= ~INTERFACE_NWK_BOOTSRAP_PANA_AUTHENTICATION;
    cur->configure_flags |= INTERFACE_SECURITY_DEFINED;
    cur->comm_status_ind_cb = thread_comm_status_indication_cb;
    return 0;
#else
    (void) interface_id;
    (void) channel_list;
    (void) device_configuration;
    (void) static_configuration;
    return -1;
#endif
}

int thread_management_device_type_set(int8_t interface_id, thread_device_type_e device_type)
{
#ifdef HAVE_THREAD
    protocol_interface_info_entry_t *cur = protocol_stack_interface_info_get_by_id(interface_id);

    if (!cur || !cur->thread_info) {
        tr_warn("Not Thread specific interface");
        return -1;
    }
    if (device_type == THREAD_DEVICE_REED) {
        // Change mode to router
        cur->bootsrap_mode = ARM_NWK_BOOTSRAP_MODE_6LoWPAN_ROUTER;
    } else if (device_type == THREAD_DEVICE_FED) {
        //FED devices makes links and makes address resolutions
        cur->bootsrap_mode = ARM_NWK_BOOTSRAP_MODE_6LoWPAN_HOST;
        cur->thread_info->end_device_link_synch = true;
    } else if (device_type == THREAD_DEVICE_MED) {
        cur->bootsrap_mode = ARM_NWK_BOOTSRAP_MODE_6LoWPAN_HOST;
        cur->thread_info->end_device_link_synch = false;
    } else if (device_type == THREAD_DEVICE_SED) {
        cur->bootsrap_mode = ARM_NWK_BOOTSRAP_MODE_6LoWPAN_SLEEPY_HOST;
        cur->thread_info->end_device_link_synch = false;
    }
    if (cur->lowpan_info & INTERFACE_NWK_ACTIVE) {
        // bootstrap active need to restart
        thread_bootstrap_reset_restart(interface_id);
    }

    return 0;
#else
    (void) interface_id;
    (void) device_type;
    return -1;
#endif
}

int thread_management_max_child_count(
    int8_t interface_id,
    uint8_t maxChildCount)
{
#ifdef HAVE_THREAD
    protocol_interface_info_entry_t *cur;

    cur = protocol_stack_interface_info_get_by_id(interface_id);
    if (!cur) {
        tr_warn("Invalid interface id");
        return -1;
    }

    if (!cur->thread_info) {
        tr_warn("Not Thread specific interface");
        return -1;
    }

    mac_description_storage_size_t buffer;
    if (!cur->mac_api || !cur->mac_api->mac_storage_sizes_get || cur->mac_api->mac_storage_sizes_get(cur->mac_api, &buffer) != 0) {
        return -1;
    }

    if (maxChildCount > buffer.device_decription_table_size) {
        tr_error("Accept values are between 0-%d for max Child count", buffer.device_decription_table_size);
        return -1;
    }
    cur->thread_info->maxChildCount = maxChildCount;
    return 0;
#else
    (void) interface_id;
    (void) maxChildCount;
    return -1;
#endif
}

link_configuration_s *thread_management_configuration_get(int8_t interface_id)
{
#ifdef HAVE_THREAD
    return thread_joiner_application_get_config(interface_id);
#else
    (void) interface_id;
    return NULL;
#endif
}

device_configuration_s *thread_management_device_configuration_get(int8_t interface_id)
{
#ifdef HAVE_THREAD
    return thread_joiner_application_get_device_config(interface_id);
#else
    (void) interface_id;
    return NULL;
#endif
}

int thread_management_link_configuration_store(int8_t interface_id, link_configuration_s *link_config)
{
#ifdef HAVE_THREAD
    int ret = thread_joiner_application_link_configuration_store(interface_id, link_config);

    if (interface_id == -1) {
        return ret;
    }

    if (ret >= 0) {
        protocol_interface_info_entry_t *cur =  protocol_stack_interface_info_get_by_id(interface_id);
        if (!link_config || !cur  || !cur->thread_info) {
            return -2;
        }
        if ((cur->lowpan_info & INTERFACE_NWK_ACTIVE) !=  0) {
            // take new settings into use after restart
            thread_bootstrap_reset_restart(interface_id);
        }
    }

    return ret;
#else
    (void) interface_id;
    (void) link_config;
    return -1;
#endif
}

int thread_management_link_configuration_add(int8_t interface_id, uint8_t *additional_ptr, uint8_t additional_len)
{
#ifdef HAVE_THREAD
    if (interface_id < 0) {
        return -1;
    }

    int ret = thread_joiner_application_update_configuration(interface_id, additional_ptr, additional_len, true);
    if (ret != 0) {
        return ret;
    }

    protocol_interface_info_entry_t *cur =  protocol_stack_interface_info_get_by_id(interface_id);
    if (!cur  || !cur->thread_info) {
        return -2;
    }
    if ((cur->lowpan_info & INTERFACE_NWK_ACTIVE) !=  0) {
        // take new settings into use after restart
        thread_bootstrap_reset_restart(interface_id);
    }

    return ret;
#else
    (void) interface_id;
    (void) additional_ptr;
    (void) additional_len;
    return -1;
#endif
}

int thread_management_link_configuration_delete(int8_t interface_id)
{
#ifdef HAVE_THREAD
    int ret = thread_joiner_application_link_configuration_delete(interface_id);

    if (interface_id == -1) {
        return ret;
    }

    if (ret >= 0) {
        protocol_interface_info_entry_t *cur = protocol_stack_interface_info_get_by_id(interface_id);
        if (!cur  || !cur->thread_info) {
            return -2;
        }
        if ((cur->lowpan_info & INTERFACE_NWK_ACTIVE) !=  0) {
            thread_bootstrap_reset_restart(interface_id);
        }
    }
    return ret;
#else
    (void) interface_id;
    return -1;
#endif
}

int thread_management_get_leader_address(int8_t interface_id, uint8_t *address_buffer)
{
#ifdef HAVE_THREAD

    protocol_interface_info_entry_t *cur;

    cur = protocol_stack_interface_info_get_by_id(interface_id);
    if (cur) {
        if ((cur->thread_info) && (thread_attach_ready(cur) == 0)  &&
                (cur->thread_info->threadPrivatePrefixInfo.ulaValid)) {
            thread_addr_write_mesh_local_16(address_buffer, thread_router_addr_from_id(cur->thread_info->thread_leader_data->leaderRouterId), cur->thread_info);
            return 0;
        }
    }
    return -1;
#else
    (void) interface_id;
    (void) address_buffer;
    return -1;
#endif
}

int thread_management_get_leader_aloc(int8_t interface_id, uint8_t *address_buffer)
{
#ifdef HAVE_THREAD

    protocol_interface_info_entry_t *cur;

    cur = protocol_stack_interface_info_get_by_id(interface_id);
    if (cur) {
        if ((cur->thread_info) && (thread_attach_ready(cur) == 0)  &&
                (cur->thread_info->threadPrivatePrefixInfo.ulaValid)) {
            thread_addr_write_mesh_local_16(address_buffer, 0xfc00, cur->thread_info);
            return 0;
        }
    }
    return -1;
#else
    (void) interface_id;
    (void) address_buffer;
    return -1;
#endif
}
int thread_management_get_ml64_address(int8_t interface_id, uint8_t *address_ptr)
{
#ifdef HAVE_THREAD
    protocol_interface_info_entry_t *cur;
    cur = protocol_stack_interface_info_get_by_id(interface_id);
    if (!cur) {
        return -1;
    }

    if (!address_ptr) {
        return -1;
    }

    if (0 != thread_management_get_ml_prefix(interface_id, address_ptr)) {
        return -2;
    }

    memcpy(&address_ptr[8], cur->iid_slaac, 8);
    return 0;
#else
    (void) interface_id;
    (void) address_ptr;
    return -1;
#endif
}

int thread_management_get_ml16_address(int8_t interface_id, uint8_t *address_ptr)
{
#ifdef HAVE_THREAD
    if (!address_ptr) {
        return -1;
    }

    if (0 != thread_management_get_ml_prefix(interface_id, address_ptr)) {
        return -2;
    }

    if (0 != thread_management_get_my_iid16(interface_id, address_ptr + 8)) {
        return -2;
    }

    return 0;
#else
    (void) interface_id;
    (void) address_ptr;
    return -1;
#endif
}

int thread_management_get_parent_address(int8_t interface_id, uint8_t *address_ptr)
{
#ifdef HAVE_THREAD
    protocol_interface_info_entry_t *cur;
    cur = protocol_stack_interface_info_get_by_id(interface_id);

    if (!cur || !cur->thread_info || !address_ptr) {
        return -1;
    }
    memset(address_ptr, 0, 16);
    if (cur->thread_info->thread_endnode_parent) {
        memcpy(address_ptr, ADDR_LINK_LOCAL_PREFIX, 8);
        address_ptr += 8;
        memcpy(address_ptr, cur->thread_info->thread_endnode_parent->mac64, 8);
        *address_ptr ^= 2;
    }

    return 0;
#else
    (void) interface_id;
    (void) address_ptr;
    return -1;
#endif
}

int thread_management_get_commissioner_address(int8_t interface_id, uint8_t *address_ptr, uint16_t *port_ptr)
{
#ifdef HAVE_THREAD
    protocol_interface_info_entry_t *cur;
    cur = protocol_stack_interface_info_get_by_id(interface_id);

    if (!cur || !cur->thread_info || !address_ptr) {
        return -1;
    }

    if (!cur->thread_info->registered_commissioner.commissioner_valid) {
        return -2;
    }
    memcpy(address_ptr, cur->thread_info->threadPrivatePrefixInfo.ulaPrefix, 8);
    memcpy(address_ptr + 8, ADDR_SHORT_ADR_SUFFIC, 6);
    common_write_16_bit(0xfc30 + (cur->thread_info->registered_commissioner.session_id % 8), address_ptr + 14);

    if (port_ptr) {
        *port_ptr = THREAD_MANAGEMENT_PORT;// Default commissioner port
    }

    return 0;
#else
    (void) interface_id;
    (void) address_ptr;
    (void) port_ptr;
    return -1;
#endif
}
int8_t thread_management_set_link_timeout(int8_t interface_id, uint32_t link_timeout)
{
#ifdef HAVE_THREAD
    protocol_interface_info_entry_t *cur;

    cur = protocol_stack_interface_info_get_by_id(interface_id);
    if (!cur) {
        tr_warn("Invalid interface id");
        return -1;
    }
    thread_info_t *thread = cur->thread_info;
    if (!thread) {
        tr_warn("Thread not active");
        return -2;
    }
    tr_info("set new link timeout %"PRIu32" , old value %"PRIu32"", link_timeout, thread->host_link_timeout);
    thread->host_link_timeout = link_timeout;
    thread_bootstrap_child_update_trig(cur);
    return 0;
#else
    (void) interface_id;
    (void) link_timeout;
    return -1;
#endif
}

int8_t thread_management_get_link_timeout(int8_t interface_id, uint32_t *link_timeout)
{
#ifdef HAVE_THREAD
    const protocol_interface_info_entry_t *cur;

    if (!link_timeout) {
        tr_warn("Invalid input ptr");
        return -3;
    }
    cur = protocol_stack_interface_info_get_by_id(interface_id);
    if (!cur) {
        tr_warn("Invalid interface id");
        return -1;
    }
    const thread_info_t *thread = cur->thread_info;
    if (!thread) {
        tr_warn("Thread not active");
        return -2;
    }
    *link_timeout = thread->host_link_timeout;
    return 0;
#else
    (void) interface_id;
    (void) link_timeout;
    return -1;
#endif
}

int8_t thread_management_set_request_full_nwk_data(int8_t interface_id, bool full_nwk_data)
{
#ifdef HAVE_THREAD
    protocol_interface_info_entry_t *cur;
    cur = protocol_stack_interface_info_get_by_id(interface_id);
    if (!cur) {
        tr_warn("Invalid interface id");
        return -1;
    }
    if (!cur->thread_info) {
        tr_warn("Thread not active");
        return -2;
    }
    if (cur->thread_info->requestFullNetworkData != full_nwk_data) {
        cur->thread_info->requestFullNetworkData = full_nwk_data;
        thread_bootstrap_child_update_trig(cur);
    }
    return 0;
#else
    (void) interface_id;
    (void) full_nwk_data;
    return -1;
#endif
}

int8_t thread_management_get_request_full_nwk_data(int8_t interface_id, bool *full_nwk_data)
{
#ifdef HAVE_THREAD
    const protocol_interface_info_entry_t *cur;

    if (!full_nwk_data) {
        tr_warn("Invalid input ptr");
        return -3;
    }
    cur = protocol_stack_interface_info_get_by_id(interface_id);
    if (!cur) {
        tr_warn("Invalid interface id");
        return -1;
    }
    if (!cur->thread_info) {
        tr_warn("Thread not active");
        return -2;
    }
    *full_nwk_data = cur->thread_info->requestFullNetworkData;
    return 0;
#else
    (void) interface_id;
    (void) full_nwk_data;
    return -1;
#endif
}

int thread_management_device_certificate_set(int8_t interface_id, const unsigned char *device_certificate_ptr, uint16_t device_certificate_len, const unsigned char *priv_key_ptr, uint16_t priv_key_len)
{
    (void) device_certificate_ptr;
    (void) device_certificate_len;
    (void) priv_key_ptr;
    (void) priv_key_len;

#ifdef HAVE_THREAD
    protocol_interface_info_entry_t *cur;

    cur = protocol_stack_interface_info_get_by_id(interface_id);
    if (!cur) {
        tr_warn("invalid interface id");
        return -1;
    }

    return thread_ccm_device_certificate_set(cur, device_certificate_ptr, device_certificate_len, priv_key_ptr, priv_key_len);

#else
    (void) interface_id;
    return -1;
#endif
}
int thread_management_network_certificate_set(int8_t interface_id, const unsigned char *network_certificate_ptr, uint16_t network_certificate_len, const unsigned char *priv_key_ptr, uint16_t priv_key_len)
{
    (void) network_certificate_ptr;
    (void) network_certificate_len;
    (void) priv_key_ptr;
    (void) priv_key_len;

#ifdef HAVE_THREAD
    protocol_interface_info_entry_t *cur;
    int ret_val;

    cur = protocol_stack_interface_info_get_by_id(interface_id);
    if (!cur) {
        tr_debug("invalid interface id");
        return -1;
    }

    ret_val = thread_ccm_network_certificate_set(cur, network_certificate_ptr, network_certificate_len);
    if (0 > ret_val) {
        return -1;
    }

    return thread_ccm_network_private_key_set(cur, priv_key_ptr, priv_key_len);
#else
    (void) interface_id;
    return -1;
#endif
}

int thread_management_partition_weighting_set(int8_t interface_id, uint8_t partition_weighting)
{
#ifdef HAVE_THREAD
    protocol_interface_info_entry_t *cur;

    cur = protocol_stack_interface_info_get_by_id(interface_id);
    if (!cur || !cur->thread_info) {
        tr_debug("Invalid interface id");
        return -1;
    }

    if (cur->thread_info->partition_weighting == partition_weighting) {
        return 0;
    }

    bool trig_network_scan = false;
    if (cur->thread_info->thread_leader_data) {
        if (cur->thread_info->thread_leader_data->weighting < partition_weighting) {
            trig_network_scan = true;
        }
    }

    cur->thread_info->partition_weighting = partition_weighting;

    if (cur->lowpan_info & INTERFACE_NWK_ACTIVE) {
        if (trig_network_scan && thread_common_ccm_enabled(cur)) {
            thread_nvm_store_link_info_clear();
            // bootstrap active and weighting has changed
            thread_bootstrap_reset_restart(interface_id);
        }
    }

    return 0;
#else
    (void) interface_id;
    (void) partition_weighting;
    return -1;
#endif
}
