// ----------------------------------------------------------------------------
// Copyright 2016-2019 ARM Ltd.
//
// SPDX-License-Identifier: Apache-2.0
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------------------------------------------------------

#include "update-client-common/arm_uc_config.h"

#if defined(ARM_UC_ENABLE) && (ARM_UC_ENABLE == 1)

#include "update-lwm2m-mbed-apis.h"
#include "update-client-lwm2m/lwm2m-monitor.h"
#include "update-client-lwm2m/FirmwareUpdateResource.h"
#include "update-client-lwm2m/DeviceMetadataResource.h"

/**
 * @brief Get driver version.
 * @return Driver version.
 */
uint32_t ARM_UCS_LWM2M_MONITOR_GetVersion(void)
{
    return 0;
}

/**
 * @brief Get Source capabilities.
 * @return Struct containing capabilites. See definition above.
 */
ARM_MONITOR_CAPABILITIES ARM_UCS_LWM2M_MONITOR_GetCapabilities(void)
{
    ARM_MONITOR_CAPABILITIES result;
    result.state   = 1;
    result.result  = 1;
    result.version = 1;

    return result;
}

/**
 * @brief Initialize Monitor.
 * @return Error code.
 */
arm_uc_error_t ARM_UCS_LWM2M_MONITOR_Initialize(void (*notification_handler)(void))
{
    ARM_UC_INIT_ERROR(retval, ERR_NONE);

    FirmwareUpdateResource::Initialize();
    FirmwareUpdateResource::addNotificationCallback(notification_handler);

    DeviceMetadataResource::Initialize();

    return retval;
}

/**
 * @brief Uninitialized Monitor.
 * @return Error code.
 */
arm_uc_error_t ARM_UCS_LWM2M_MONITOR_Uninitialize(void)
{
    ARM_UC_INIT_ERROR(retval, ERR_NONE);
    return retval;
}

/**
 * @brief Send Update Client state.
 * @details From the OMA LWM2M Technical Specification:
 *
 *          Indicates current state with respect to this firmware update.
 *          This value is set by the LWM2M Client in accordance with state
 *          and arm_uc_monitor_state_t type.
 *
 * @return Error code.
 */
arm_uc_error_t ARM_UCS_LWM2M_MONITOR_SendState(arm_uc_monitor_state_t an_update_state)
{
    ARM_UC_INIT_ERROR(result, ERR_NONE);

    // If out of range of a legitimate update-state, return an "invalid-parameter" error to the caller,
    //   otherwise try send the new state to the monitor.
    if (!ARM_UC_IsValidState(an_update_state)) {
        ARM_UC_SET_ERROR(result, ERR_INVALID_PARAMETER);
    } else {
        FirmwareUpdateResource::arm_ucs_lwm2m_state_t state =
            (FirmwareUpdateResource::arm_ucs_lwm2m_state_t)an_update_state;
        if (FirmwareUpdateResource::sendState(state) != 0) {
            ARM_UC_SET_ERROR(result, ERR_UNSPECIFIED);
        }
    }
    return result;
}

/**
 * @brief Send update result.
 * @details From the OMA LWM2M Technical Specification:
 *          Contains the result of downloading or updating the firmware
 *          This Resource MAY be reported by sending Observe operation.
 *
 * @return Error code.
 */
arm_uc_error_t ARM_UCS_LWM2M_MONITOR_SendUpdateResult(arm_uc_monitor_result_t an_update_result)
{
    ARM_UC_INIT_ERROR(result, ERR_NONE);

    // If out of range of a legitimate update-result, send an "unspecified-error" result.
    if (!ARM_UC_IsValidResult(an_update_result)) {
        ARM_UC_SET_ERROR(result, ERR_INVALID_PARAMETER);
    } else {
        // Cast the arm_uc_monitor_result_t to a arm_ucs_lwm2m_result_t, and send it.
        FirmwareUpdateResource::arm_ucs_lwm2m_result_t code =
            (FirmwareUpdateResource::arm_ucs_lwm2m_result_t)an_update_result;
        if (FirmwareUpdateResource::sendUpdateResult(code) != 0) {
            ARM_UC_SET_ERROR(result, ERR_UNSPECIFIED);
        }
    }
    return result;
}

/**
 * @brief Send current firmware name.
 * @details The firmware name is the SHA256 hash.
 * @param name Pointer to buffer struct. Hash is stored as byte array.
 * @return Error code.
 */
arm_uc_error_t ARM_UCS_LWM2M_MONITOR_SendName(arm_uc_buffer_t *name)
{
    ARM_UC_INIT_ERROR(result, ERR_NONE);
    if (!name || !name->ptr) {
        ARM_UC_SET_ERROR(result, ERR_INVALID_PARAMETER);
    } else if (FirmwareUpdateResource::sendPkgName(name->ptr, name->size) != 0) {
        ARM_UC_SET_ERROR(result, ERR_UNSPECIFIED);
    }
    return result;
}

/**
 * @brief Send current firmware version.
 * @details The firmware version is the timestamp from the manifest that
 *          authorized the firmware.
 * @param version Timestamp, 64 bit unsigned integer.
 * @return Error code.
 */
arm_uc_error_t ARM_UCS_LWM2M_MONITOR_SendVersion(uint64_t version)
{
    ARM_UC_INIT_ERROR(result, ERR_NONE);
    if (FirmwareUpdateResource::sendPkgVersion(version) != 0) {
        ARM_UC_SET_ERROR(result, ERR_UNSPECIFIED);
    }
    return result;
}

/**
 * @brief Set the bootloader hash.
 * @details The bootloader hash is a hash of the bootloader. This is
 *          used for tracking the version of the bootloader used.
 * @param name Pointer to buffer struct. Hash is stored as byte array.
 * @return Error code.
 */
arm_uc_error_t ARM_UCS_LWM2M_MONITOR_SetBootloaderHash(arm_uc_buffer_t *hash)
{
    ARM_UC_INIT_ERROR(result, ERR_NONE);
    if (DeviceMetadataResource::setBootloaderHash(hash) != 0) {
        ARM_UC_SET_ERROR(result, ERR_UNSPECIFIED);
    }
    return result;
}

/**
 * @brief Set the OEM bootloader hash.
 * @details If the end-user has modified the bootloader the hash of the
 *          modified bootloader can be set here.
 * @param name Pointer to buffer struct. Hash is stored as byte array.
 * @return Error code.
 */
arm_uc_error_t ARM_UCS_LWM2M_MONITOR_SetOEMBootloaderHash(arm_uc_buffer_t *hash)
{
    ARM_UC_INIT_ERROR(result, ERR_NONE);
    if (DeviceMetadataResource::setOEMBootloaderHash(hash) != 0) {
        ARM_UC_SET_ERROR(result, ERR_UNSPECIFIED);
    }
    return result;
}

#endif
