/**
 * Copyright (c) 2015 - 2018, Nordic Semiconductor ASA
 *
 * All rights reserved.
 *
 * 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, except as embedded into a Nordic
 *    Semiconductor ASA integrated circuit in a product or a software update for
 *    such product, 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 Nordic Semiconductor ASA nor the names of its
 *    contributors may be used to endorse or promote products derived from this
 *    software without specific prior written permission.
 *
 * 4. This software, with or without modification, must only be used with a
 *    Nordic Semiconductor ASA integrated circuit.
 *
 * 5. Any software provided in binary form under this license must not be reverse
 *    engineered, decompiled, modified and/or disassembled.
 *
 * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA 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 <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>

#include "lwm2m_api.h"
#include "lwm2m_register.h"
#include "lwm2m_bootstrap.h"
#include "sdk_os.h"
#include "lwm2m.h"
#include "sdk_config.h"

#if LWM2M_CONFIG_LOG_ENABLED

#define NRF_LOG_MODULE_NAME lwm2m

#define NRF_LOG_LEVEL       LWM2M_CONFIG_LOG_LEVEL
#define NRF_LOG_INFO_COLOR  LWM2M_CONFIG_INFO_COLOR
#define NRF_LOG_DEBUG_COLOR LWM2M_CONFIG_DEBUG_COLOR

#include "nrf_log.h"
NRF_LOG_MODULE_REGISTER();

#define LWM2M_TRC     NRF_LOG_DEBUG                                                                 /**< Used for getting trace of execution in the module. */
#define LWM2M_ERR     NRF_LOG_ERROR                                                                 /**< Used for logging errors in the module. */
#define LWM2M_DUMP    NRF_LOG_HEXDUMP_DEBUG                                                         /**< Used for dumping octet information to get details of bond information etc. */

#define LWM2M_ENTRY() LWM2M_TRC(">> %s", __func__)
#define LWM2M_EXIT()  LWM2M_TRC("<< %s", __func__)

#else // LWM2M_CONFIG_LOG_ENABLED

#define LWM2M_TRC(...)                                                                              /**< Disables traces. */
#define LWM2M_DUMP(...)                                                                             /**< Disables dumping of octet streams. */
#define LWM2M_ERR(...)                                                                              /**< Disables error logs. */

#define LWM2M_ENTRY(...)
#define LWM2M_EXIT(...)

#endif // LWM2M_CONFIG_LOG_ENABLED

#if (LWM2M_CONFIG_LOG_ENABLED != 0)

static uint8_t op_desc_idx_lookup(uint8_t bitmask)
{
    for (uint8_t i = 0; i < 8; i++)
    {
        if ((bitmask > i) == 0x1)
        {
            return i;
        }
    }

    // If no bits where set in the bitmask.
    return 0;
}

static const char m_operation_desc[8][9] = {
    "NONE",
    "READ",
    "WRITE",
    "EXECUTE",
    "DELETE",
    "CREATE",
    "DISCOVER",
    "OBSERVE"
};

#endif

SDK_MUTEX_DEFINE(m_lwm2m_mutex)                                                           /**< Mutex variable. Currently unused, this declaration does not occupy any space in RAM. */

static lwm2m_object_prototype_t *   m_objects[LWM2M_COAP_HANDLER_MAX_OBJECTS];
static lwm2m_instance_prototype_t * m_instances[LWM2M_COAP_HANDLER_MAX_INSTANCES];
static uint16_t m_num_objects;
static uint16_t m_num_instances;

static void coap_error_handler(uint32_t error_code, coap_message_t * p_message)
{
    LWM2M_ERR("[CoAP]: Unhandled coap message recieved. Error code: %lu", error_code);
}

static void internal_coap_handler_init(void)
{
    memset(m_objects, 0, sizeof(m_objects));
    memset(m_instances, 0, sizeof(m_instances));

    m_num_objects   = 0;
    m_num_instances = 0;
}


static bool numbers_only(const char * p_str, uint16_t str_len)
{
    for (uint16_t i = 0; i < str_len; i++)
    {
        if (isdigit(p_str[i]) == 0)
        {
            return false;
        }
    }
    return true;
}


static uint32_t instance_resolve(lwm2m_instance_prototype_t ** p_instance,
                                          uint16_t             object_id,
                                          uint16_t             instance_id)
{
    for (int i = 0; i < m_num_instances; ++i)
    {
        if (m_instances[i]->object_id == object_id &&
            m_instances[i]->instance_id == instance_id)
        {
            if (m_instances[i]->callback == NULL)
            {
                return NRF_ERROR_NULL;
            }

            *p_instance = m_instances[i];

            return NRF_SUCCESS;
        }
    }

    return NRF_ERROR_NOT_FOUND;
}


static uint32_t object_resolve(lwm2m_object_prototype_t ** p_instance,
                               uint16_t                    object_id)
{
    for (int i = 0; i < m_num_objects; ++i)
    {
        if (m_objects[i]->object_id == object_id)
        {
            if (m_objects[i]->callback == NULL)
            {
                return NRF_ERROR_NULL;
            }

            *p_instance = m_objects[i];

            return NRF_SUCCESS;
        }
    }

    return NRF_ERROR_NOT_FOUND;
}

static uint32_t op_code_resolve(lwm2m_instance_prototype_t * p_instance,
                                uint16_t                     resource_id,
                                uint8_t *                    operation)
{
    uint8_t *  operations     = (uint8_t *) p_instance + p_instance->operations_offset;
    uint16_t * operations_ids = (uint16_t *)((uint8_t *) p_instance +
                                p_instance->resource_ids_offset);

    for (int j = 0; j < p_instance->num_resources; ++j)
    {
        if (operations_ids[j] == resource_id)
        {
            *operation = operations[j];
            return NRF_SUCCESS;
        }
    }

    return NRF_ERROR_NOT_FOUND;
}

static uint32_t internal_request_handle(coap_message_t * p_request,
                                        uint16_t *       p_path,
                                        uint8_t          path_len)
{
    uint32_t err_code;
    uint8_t  operation    = LWM2M_OPERATION_CODE_NONE;
    uint32_t content_type = 0;

    err_code = coap_message_ct_mask_get(p_request, &content_type);
    if (err_code != NRF_SUCCESS)
    {
        return err_code;
    }

    /**
     * TODO: the methods should check if we have read / write / execute rights
     *       through ACL and resource operations
     */

    switch (p_request->header.code)
    {
        case COAP_CODE_GET:
        {
            LWM2M_TRC("[CoAP]: CoAP GET request");
            if (content_type == COAP_CT_APP_LINK_FORMAT) // Discover
            {
                operation = LWM2M_OPERATION_CODE_DISCOVER;
            }
            else // Read
            {
                operation = LWM2M_OPERATION_CODE_READ;
            }
            break;
        }

        case COAP_CODE_PUT:
        {
            operation = LWM2M_OPERATION_CODE_WRITE;
            break;
        }

        case COAP_CODE_POST:
        {
            operation = LWM2M_OPERATION_CODE_WRITE;
            break;
        }

        case COAP_CODE_DELETE:
        {
            operation = LWM2M_OPERATION_CODE_DELETE;
            break;
        }

        default:
            break; // Maybe send response with unsupported method not allowed?
    }

    err_code = NRF_ERROR_NOT_FOUND;

    switch (path_len)
    {
        case 0:
        {
            if (operation == LWM2M_OPERATION_CODE_DELETE)
            {
                LWM2M_TRC("[CoAP]: >> %s root /",
                          m_operation_desc[op_desc_idx_lookup(operation)]);

                LWM2M_MUTEX_UNLOCK();

                err_code = lwm2m_coap_handler_root(LWM2M_OPERATION_CODE_DELETE, p_request);

                LWM2M_MUTEX_LOCK();

                LWM2M_TRC("[CoAP]: << %s root /",
                          m_operation_desc[op_desc_idx_lookup(operation)]);
            }
            break;
        }

        case 1:
        {
            LWM2M_TRC("[CoAP]: >> %s object /%u/",
                      m_operation_desc[op_desc_idx_lookup(operation)],
                      p_path[0]);

            lwm2m_object_prototype_t * p_object;

            err_code = object_resolve(&p_object, p_path[0]);

            if (err_code != NRF_SUCCESS)
            {
                break;
            }

            LWM2M_MUTEX_UNLOCK();

            err_code = p_object->callback(p_object, LWM2M_INVALID_INSTANCE, operation, p_request);

            LWM2M_MUTEX_LOCK();

            LWM2M_TRC("[CoAP]: << %s object /%u/, result: %s",
                      m_operation_desc[op_desc_idx_lookup(operation)],
                      p_path[0],
                      (err_code == NRF_SUCCESS) ? "SUCCESS" : "NOT_FOUND");

            break;
        }

        case 2:
        {
            LWM2M_TRC("[CoAP]: >> %s instance /%u/%u/",
                      m_operation_desc[op_desc_idx_lookup(operation)],
                      p_path[0],
                      p_path[1]);

            lwm2m_instance_prototype_t * p_instance;

            err_code = instance_resolve(&p_instance, p_path[0], p_path[1]);
            if (err_code == NRF_SUCCESS)
            {
                LWM2M_MUTEX_UNLOCK();

                err_code = p_instance->callback(p_instance, LWM2M_INVALID_RESOURCE, operation, p_request);

                LWM2M_MUTEX_LOCK();

                LWM2M_TRC("[CoAP]: << %s instance /%u/%u/, result: %s",
                          m_operation_desc[op_desc_idx_lookup(operation)],
                          p_path[0],
                          p_path[1],
                          (err_code == NRF_SUCCESS) ? "SUCCESS" : "NOT_FOUND");
                break;
            }

            // Bootstrap can write to non-existing instances
            if (err_code == NRF_ERROR_NOT_FOUND         &&
                operation == LWM2M_OPERATION_CODE_WRITE &&
                p_request->header.code == COAP_CODE_PUT)
            {
                LWM2M_TRC("[CoAP]: >> %s object /%u/%u/",
                          m_operation_desc[op_desc_idx_lookup(operation)],
                          p_path[0],
                          p_path[1]);

                lwm2m_object_prototype_t * p_object;

                err_code = object_resolve(&p_object, p_path[0]);

                if (err_code != NRF_SUCCESS)
                {
                    break;
                }

                LWM2M_MUTEX_UNLOCK();

                err_code = p_object->callback(p_object, p_path[1], operation, p_request);

                LWM2M_MUTEX_LOCK();

                LWM2M_TRC("[CoAP]: << %s object /%u/%u/, result: %s",
                          m_operation_desc[op_desc_idx_lookup(operation)],
                          p_path[0],
                          p_path[1],
                          (err_code == NRF_SUCCESS) ? "SUCCESS" : "NOT_FOUND");
            }

            if (err_code == NRF_ERROR_NOT_FOUND         &&
                operation == LWM2M_OPERATION_CODE_WRITE &&
                p_request->header.code == COAP_CODE_POST)
            {
                LWM2M_TRC("[CoAP]: >> CREATE object /%u/%u/",
                          p_path[0],
                          p_path[1]);

                lwm2m_object_prototype_t * p_object;

                err_code = object_resolve(&p_object, p_path[0]);

                if (err_code != NRF_SUCCESS)
                {
                    break;
                }

                LWM2M_MUTEX_UNLOCK();

                err_code = p_object->callback(p_object, p_path[1], LWM2M_OPERATION_CODE_CREATE, p_request);

                LWM2M_MUTEX_LOCK();

                LWM2M_TRC("[CoAP]: << CREATE object /%u/%u/, result: %s",
                          p_path[0],
                          p_path[1],
                          (err_code == NRF_SUCCESS) ? "SUCCESS" : "NOT_FOUND");
                break;
            }

            break;
        }

        case 3:
        {
            if (operation == LWM2M_OPERATION_CODE_DELETE)
            {
                // Deleting resources within an instance not allowed.
                break;
            }

            if (p_request->header.code == COAP_CODE_POST)
            {
                for (int i = 0; i < m_num_instances; ++i)
                {
                    if ((m_instances[i]->object_id == p_path[0]) &&
                        (m_instances[i]->instance_id == p_path[1]))
                    {
                        if (m_instances[i]->callback == NULL)
                        {
                            err_code = NRF_ERROR_NULL;
                            break;
                        }

                        uint8_t resource_operation = 0;
                        err_code = op_code_resolve(m_instances[i], p_path[2], &resource_operation);

                        if (err_code != NRF_SUCCESS)
                            break;

                        if ((resource_operation & LWM2M_OPERATION_CODE_EXECUTE) > 0)
                        {
                            operation = LWM2M_OPERATION_CODE_EXECUTE;
                        }

                        if ((resource_operation & LWM2M_OPERATION_CODE_WRITE) > 0)
                        {
                            operation = LWM2M_OPERATION_CODE_WRITE;
                        }

                        LWM2M_TRC("[CoAP]: >> %s instance /%u/%u/%u/",
                                  m_operation_desc[op_desc_idx_lookup(operation)],
                                  m_instances[i]->object_id,
                                  m_instances[i]->instance_id,
                                  p_path[2]);

                        LWM2M_MUTEX_UNLOCK();

                        (void)m_instances[i]->callback(m_instances[i],
                                                       p_path[2],
                                                       operation,
                                                       p_request);

                        LWM2M_MUTEX_LOCK();

                        err_code = NRF_SUCCESS;

                        LWM2M_TRC("[CoAP]: << %s instance /%u/%u/%u/",
                                  m_operation_desc[op_desc_idx_lookup(operation)],
                                  m_instances[i]->object_id,
                                  m_instances[i]->instance_id,
                                  p_path[2]);

                        break;
                    }
                }
            }
            else
            {
                LWM2M_TRC("[CoAP]: >> %s instance /%u/%u/%u/",
                          m_operation_desc[op_desc_idx_lookup(operation)],
                          p_path[0],
                          p_path[1],
                          p_path[2]);

                lwm2m_instance_prototype_t * p_instance;

                err_code = instance_resolve(&p_instance, p_path[0], p_path[1]);
                if (err_code != NRF_SUCCESS)
                {
                    break;
                }

                LWM2M_MUTEX_UNLOCK();

                err_code = p_instance->callback(p_instance, p_path[2], operation, p_request);

                LWM2M_MUTEX_LOCK();

                LWM2M_TRC("[CoAP]: << %s instance /%u/%u/%u/, result: %s",
                          m_operation_desc[op_desc_idx_lookup(operation)],
                          p_path[0],
                          p_path[1],
                          p_path[2],
                          (err_code == NRF_SUCCESS) ? "SUCCESS" : "NOT_FOUND");
            }
            break;
        }

        default:
            break;
    }

    return err_code;
}


static uint32_t lwm2m_coap_handler_handle_request(coap_message_t * p_request)
{
    LWM2M_ENTRY();

    uint16_t index;
    uint16_t path[3];
    char *   endptr;

    bool     is_numbers_only = true;
    uint16_t path_index      = 0;
    uint32_t err_code        = NRF_SUCCESS;

    LWM2M_MUTEX_LOCK();

    for (index = 0; index < p_request->options_count; index++)
    {
        if (p_request->options[index].number == COAP_OPT_URI_PATH)
        {
            uint16_t option_len = p_request->options[index].length;
            bool     numbers    = numbers_only((char *)p_request->options[index].p_data,
                                               option_len);

            if (numbers)
            {
                // Declare a temporary array that is 1 byte longer than the
                // option data in order to leave space for a terminating character.
                uint8_t option_data[option_len + 1];
                // Set the temporary array to zero.
                memset(option_data, 0, sizeof(option_data));
                // Copy the option data string to the temporary array.
                memcpy(option_data, p_request->options[index].p_data, option_len);

                // Convert the zero-terminated string to a long int value.
                path[path_index] = strtol((char *)option_data, &endptr, 10);

                ++path_index;

                if (endptr == ((char *)option_data))
                {
                    err_code = NRF_ERROR_NOT_FOUND;
                    break;
                }

                if (endptr != ((char *)option_data + option_len))
                {
                    err_code = NRF_ERROR_NOT_FOUND;
                    break;
                }
            }
            else
            {
                is_numbers_only = false;
                break;
            }
        }
    }

    if (err_code == NRF_SUCCESS)
    {
        if (is_numbers_only == true)
        {
            err_code = internal_request_handle(p_request, path, path_index);
        }
        else
        {
            // If uri path did not consist of numbers only.
            char * requested_uri = NULL;
            for (index = 0; index < p_request->options_count; index++)
            {
                if (p_request->options[index].number == COAP_OPT_URI_PATH)
                {
                    requested_uri = (char *)p_request->options[index].p_data;

                    // Stop on first URI hit.
                    break;
                }
            }

            if (requested_uri == NULL)
            {
                err_code = NRF_ERROR_NOT_FOUND;
            }
            else
            {
                // Try to look up if there is a match with object with an alias name.
                for (int i = 0; i < m_num_objects; ++i)
                {
                    if (m_objects[i]->object_id == LWM2M_NAMED_OBJECT)
                    {
                        size_t size = strlen(m_objects[i]->p_alias_name);
                        if ((strncmp(m_objects[i]->p_alias_name, requested_uri, size) == 0))
                        {
                            if (m_objects[i]->callback == NULL)
                            {
                                err_code = NRF_ERROR_NULL;
                                break;
                            }

                            LWM2M_MUTEX_UNLOCK();

                            err_code = m_objects[i]->callback(m_objects[i],
                                                              LWM2M_INVALID_INSTANCE,
                                                              LWM2M_OPERATION_CODE_NONE,
                                                              p_request);

                            LWM2M_MUTEX_LOCK();

                            break;
                        }
                    }
                    else
                    {
                        // This is not a name object, return error code.
                        err_code = NRF_ERROR_NOT_FOUND;
                        break;
                    }
                }
            }
        }
    }

    LWM2M_MUTEX_UNLOCK();

    LWM2M_EXIT();

    return err_code;
}


uint32_t lwm2m_coap_handler_instance_add(lwm2m_instance_prototype_t * p_instance)
{
    LWM2M_ENTRY();

    NULL_PARAM_CHECK(p_instance);

    LWM2M_MUTEX_LOCK();

    if (m_num_instances == LWM2M_COAP_HANDLER_MAX_INSTANCES)
    {
        LWM2M_MUTEX_UNLOCK();

        return NRF_ERROR_NO_MEM;
    }

    m_instances[m_num_instances] = p_instance;
    ++m_num_instances;

    LWM2M_MUTEX_UNLOCK();

    return NRF_SUCCESS;
}


uint32_t lwm2m_coap_handler_instance_delete(lwm2m_instance_prototype_t * p_instance)
{
    LWM2M_ENTRY();

    NULL_PARAM_CHECK(p_instance);

    LWM2M_MUTEX_LOCK();

    for (int i = 0; i < m_num_instances; ++i)
    {
        if ((m_instances[i]->object_id == p_instance->object_id) &&
            (m_instances[i]->instance_id == p_instance->instance_id))
        {
            // Move current last entry into this index position, and trim down the length.
            // If this is the last element, it cannot be accessed because the m_num_instances
            // count is 0.
            m_instances[i] = m_instances[m_num_instances - 1];
            --m_num_instances;

            LWM2M_MUTEX_UNLOCK();

            return NRF_SUCCESS;
        }
    }

    LWM2M_MUTEX_UNLOCK();

    return NRF_ERROR_NOT_FOUND;
}


uint32_t lwm2m_coap_handler_object_add(lwm2m_object_prototype_t * p_object)
{
    LWM2M_ENTRY();

    NULL_PARAM_CHECK(p_object);

    LWM2M_MUTEX_LOCK();

    if (m_num_objects == LWM2M_COAP_HANDLER_MAX_INSTANCES)
    {
        LWM2M_MUTEX_UNLOCK();

        return NRF_ERROR_NO_MEM;
    }

    m_objects[m_num_objects] = p_object;
    ++m_num_objects;

    LWM2M_MUTEX_UNLOCK();

    return NRF_SUCCESS;
}


uint32_t lwm2m_coap_handler_object_delete(lwm2m_object_prototype_t * p_object)
{
    LWM2M_ENTRY();

    NULL_PARAM_CHECK(p_object);

    LWM2M_MUTEX_LOCK();

    for (int i = 0; i < m_num_objects; ++i)
    {
        if ( m_objects[i]->object_id == p_object->object_id)
        {
            // Move current last entry into this index position, and trim down the length.
            // If this is the last element, it cannot be accessed because the m_num_objects
            // count is 0.
            m_objects[i] = m_objects[m_num_objects - 1];
            --m_num_objects;

            LWM2M_MUTEX_UNLOCK();

            return NRF_SUCCESS;
        }
    }

    LWM2M_MUTEX_UNLOCK();

    return NRF_ERROR_NOT_FOUND;
}


uint32_t lwm2m_coap_handler_gen_link_format(uint8_t * p_buffer, uint16_t * p_buffer_len)
{

    LWM2M_ENTRY();

    NULL_PARAM_CHECK(p_buffer_len);

    LWM2M_MUTEX_LOCK();

    uint16_t  buffer_index = 0;
    uint8_t * p_string_buffer;
    uint16_t  buffer_max_size;

    uint8_t   dry_run_buffer[16];
    bool      dry_run      = false;
    uint16_t  dry_run_size = 0;

    if (p_buffer == NULL)
    {
        // Dry-run only, in order to calculate the size of the needed buffer.
        dry_run = true;
        p_string_buffer = dry_run_buffer;
        buffer_max_size = sizeof(dry_run_buffer);
    }
    else
    {
        p_string_buffer = p_buffer;
        buffer_max_size = *p_buffer_len;
    }

    for (int i = 0; i < m_num_objects; ++i)
    {
        // We need more than 3 chars to write a new link
        if (buffer_max_size - buffer_index <= 3)
        {
            LWM2M_MUTEX_UNLOCK();

            return NRF_ERROR_NO_MEM;
        }

        uint16_t curr_object = m_objects[i]->object_id;

        if (curr_object == LWM2M_NAMED_OBJECT)
        {
            // Skip this object as it is a named object.
            continue;
        }

        bool instance_present = false;
        for (int j = 0; j < m_num_instances; ++j)
        {
            if (m_instances[j]->object_id == curr_object)
            {
                instance_present = true;

                buffer_index += snprintf((char *)&p_string_buffer[buffer_index],
                                         buffer_max_size - buffer_index,
                                         "</%u/%u>,",
                                         m_instances[j]->object_id,
                                         m_instances[j]->instance_id);
                if (dry_run == true)
                {
                    dry_run_size += buffer_index;
                    buffer_index = 0;
                }
            }
        }

        if (!instance_present)
        {
            buffer_index += snprintf((char *)&p_string_buffer[buffer_index],
                                     buffer_max_size - buffer_index,
                                     "</%u>,",
                                     curr_object);
            if (dry_run == true)
            {
                dry_run_size += buffer_index;
                buffer_index = 0;
            }
        }
    }

    if (dry_run == true)
    {
        *p_buffer_len = dry_run_size - 1;
    }
    else
    {
        *p_buffer_len = buffer_index - 1; // Remove the last comma
    }

    LWM2M_MUTEX_UNLOCK();

    return NRF_SUCCESS;
}


uint32_t lwm2m_init(void)
{
    SDK_MUTEX_INIT(m_lwm2m_mutex);

    LWM2M_MUTEX_LOCK();

    uint32_t err_code;

    err_code = internal_lwm2m_register_init();
    if (err_code != NRF_SUCCESS)
    {
        LWM2M_MUTEX_UNLOCK();
        return err_code;
    }

    err_code = internal_lwm2m_bootstrap_init();
    if (err_code != NRF_SUCCESS)
    {
        LWM2M_MUTEX_UNLOCK();
        return err_code;
    }

    err_code = coap_error_handler_register(coap_error_handler);
    if (err_code != NRF_SUCCESS)
    {
        LWM2M_MUTEX_UNLOCK();
        return err_code;
    }

    internal_coap_handler_init();

    err_code = coap_request_handler_register(lwm2m_coap_handler_handle_request);

    LWM2M_MUTEX_UNLOCK();

    return err_code;
}
