/*
 * SPDX-FileCopyrightText: 2020 Nordic Semiconductor ASA
 * SPDX-FileContributor: 2025 Espressif Systems (Shanghai) CO LTD
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#ifndef _BLE_MESH_v11_BLOB_CLI_H_
#define _BLE_MESH_v11_BLOB_CLI_H_

#include <sys/types.h>

#include "mesh/access.h"
#include "mesh/utils.h"
#include "mesh.h"
#include "mesh/utils.h"
#include "mesh/access.h"
#include "mesh/atomic.h"
#include "mesh/timer.h"
#include "mesh/compiler.h"
#include "mesh/trace.h"
#include "transport.h"
#include "mesh_v1.1/mbt/blob.h"

#ifdef __cplusplus
extern "C" {
#endif

/**
 * @defgroup bt_mesh_blob_cli Bluetooth Mesh BLOB Transfer Client model API
 * @ingroup bt_mesh
 * @{
 */

struct bt_mesh_blob_cli;

/**
 *
 * @brief BLOB Transfer Client model Composition Data entry.
 *
 * @param _cli Pointer to a @ref bt_mesh_blob_cli instance.
 */
#define BT_MESH_MODEL_BLOB_CLI(_cli)                                             \
        BLE_MESH_MODEL_CB(BLE_MESH_MODEL_ID_BLOB_CLI, _bt_mesh_blob_cli_op,      \
                          NULL, _cli, &_bt_mesh_blob_cli_cb)

/** Target node's Pull mode (Pull BLOB Transfer Mode) context used
 *  while sending chunks to the Target node.
 */
struct bt_mesh_blob_target_pull {
    /** Timestamp when the Block Report Timeout Timer expires for this Target node. */
    int64_t block_report_timestamp;

    /** Missing chunks reported by this Target node. */
    uint8_t missing[DIV_ROUND_UP(CONFIG_BLE_MESH_BLOB_CHUNK_COUNT_MAX, 8)];
};

/** BLOB Transfer Client Target node. */
struct bt_mesh_blob_target {
    /** Linked list node */
    sys_snode_t n;

    /** Target node address. */
    uint16_t addr;

    /** Target node's Pull mode context.
     *  Needs to be initialized when sending a BLOB in Pull mode.
     */
    struct bt_mesh_blob_target_pull *pull;

    /** BLOB transfer status, see @ref bt_mesh_blob_status. */
    uint8_t status;

    uint8_t procedure_complete: 1, /* Procedure has been completed. */
            acked: 1,             /* Message has been acknowledged. Not used when sending. */
            timedout: 1,          /* Target node didn't respond after specified timeout. */
            skip: 1;              /* Skip Target node from broadcast. */
};

/** BLOB transfer information.
 *
 * If @c phase is @ref BT_MESH_BLOB_XFER_PHASE_INACTIVE,
 * the fields below @c phase are not initialized.
 * If @c phase is @ref BT_MESH_BLOB_XFER_PHASE_WAITING_FOR_START,
 * the fields below @c id are not initialized.
 */
struct bt_mesh_blob_xfer_info {
    /** BLOB transfer status. */
    enum bt_mesh_blob_status status;

    /** BLOB transfer mode. */
    enum bt_mesh_blob_xfer_mode mode;

    /** BLOB transfer phase. */
    enum bt_mesh_blob_xfer_phase phase;

    /** BLOB ID. */
    uint64_t id;

    /** BLOB size in octets. */
    uint32_t size;

    /** Logarithmic representation of the block size. */
    uint8_t block_size_log;

    /** MTU size in octets. */
    uint16_t mtu_size;

    /** Bit field indicating blocks that were not received.  */
    const uint8_t *missing_blocks;
};

/** BLOB Transfer Client transfer inputs. */
struct bt_mesh_blob_cli_inputs {
    /** Linked list of Target nodes. Each node should point to @ref
     *  bt_mesh_blob_target::n.
     */
    sys_slist_t targets;

    /** AppKey index to send with. */
    uint16_t app_idx;

    /** Group address destination for the BLOB transfer, or @ref
     *  BLE_MESH_ADDR_UNASSIGNED to send every message to each Target
     *  node individually.
     */
    uint16_t group;

    /** Time to live value of BLOB transfer messages. */
    uint8_t ttl;

    /** Additional response time for the Target nodes, in 10-second increments.
     *
     *  The extra time can be used to give the Target nodes more time to respond
     *  to messages from the Client. The actual timeout will be calculated
     *  according to the following formula:
     *
     *  @verbatim
     *  timeout = 20 seconds + (10 seconds * timeout_base) + (100 ms * TTL)
     *  @endverbatim
     *
     *  If a Target node fails to respond to a message from the Client within the
     *  configured transfer timeout, the Target node is dropped.
     */
    uint16_t timeout_base;
};

/** Transfer capabilities of a Target node. */
struct bt_mesh_blob_cli_caps {
    /** Max BLOB size. */
    size_t max_size;

    /** Logarithmic representation of the minimum block size. */
    uint8_t min_block_size_log;

    /** Logarithmic representation of the maximum block size. */
    uint8_t max_block_size_log;

    /** Max number of chunks per block. */
    uint16_t max_chunks;

    /** Max chunk size. */
    uint16_t max_chunk_size;

    /** Max MTU size. */
    uint16_t mtu_size;

    /** Supported transfer modes. */
    enum bt_mesh_blob_xfer_mode modes;
};

/** BLOB Transfer Client state. */
enum bt_mesh_blob_cli_state {
    /** No transfer is active. */
    BT_MESH_BLOB_CLI_STATE_NONE,
    /** Retrieving transfer capabilities. */
    BT_MESH_BLOB_CLI_STATE_CAPS_GET,
    /** Sending transfer start. */
    BT_MESH_BLOB_CLI_STATE_START,
    /** Sending block start. */
    BT_MESH_BLOB_CLI_STATE_BLOCK_START,
    /** Sending block chunks. */
    BT_MESH_BLOB_CLI_STATE_BLOCK_SEND,
    /** Checking block status. */
    BT_MESH_BLOB_CLI_STATE_BLOCK_CHECK,
    /** Checking transfer status. */
    BT_MESH_BLOB_CLI_STATE_XFER_CHECK,
    /** Cancelling transfer. */
    BT_MESH_BLOB_CLI_STATE_CANCEL,
    /** Transfer is suspended. */
    BT_MESH_BLOB_CLI_STATE_SUSPENDED,
    /** Checking transfer progress. */
    BT_MESH_BLOB_CLI_STATE_XFER_PROGRESS_GET,
};

/** Event handler callbacks for the BLOB Transfer Client model.
 *
 *  All handlers are optional.
 */
struct bt_mesh_blob_cli_cb {
    /** @brief Capabilities retrieval completion callback.
     *
     *  Called when the capabilities retrieval procedure completes, indicating that
     *  a common set of acceptable transfer parameters have been established
     *  for the given list of Target nodes. All compatible Target nodes have
     *  status code @ref BT_MESH_BLOB_SUCCESS.
     *
     *  @param cli     BLOB Transfer Client instance.
     *  @param caps    Safe transfer capabilities if the transfer capabilities
     *                 of at least one Target node has satisfied the Client, or NULL otherwise.
     */
    void (*caps)(struct bt_mesh_blob_cli *cli,
                 const struct bt_mesh_blob_cli_caps *caps);

    /** @brief Target node loss callback.
     *
     *  Called whenever a Target node has been lost due to some error in the
     *  transfer. Losing a Target node is not considered a fatal error for
     *  the Client until all Target nodes have been lost.
     *
     *  @param cli    BLOB Transfer Client instance.
     *  @param target Target node that was lost.
     *  @param reason Reason for the Target node loss.
     */
    void (*lost_target)(struct bt_mesh_blob_cli *cli,
                        struct bt_mesh_blob_target *target,
                        enum bt_mesh_blob_status reason);

    /** @brief Transfer is suspended.
     *
     * Called when the transfer is suspended due to response timeout from all Target nodes.
     *
     * @param cli    BLOB Transfer Client instance.
     */
    void (*suspended)(struct bt_mesh_blob_cli *cli);

    /** @brief Transfer end callback.
     *
     *  Called when the transfer ends.
     *
     *  @param cli     BLOB Transfer Client instance.
     *  @param xfer    Completed transfer.
     *  @param success Status of the transfer.
     *                 Is @c true if at least one Target
     *                 node received the whole transfer.
     */
    void (*end)(struct bt_mesh_blob_cli *cli,
                const struct bt_mesh_blob_xfer *xfer, bool success);

    /** @brief Transfer progress callback
     *
     * The content of @c info is invalidated upon exit from the callback.
     * Therefore it needs to be copied if it is planned to be used later.
     *
     *  @param cli      BLOB Transfer Client instance.
     *  @param target   Target node that responded to the request.
     *  @param info     BLOB transfer information.
     */
    void (*xfer_progress)(struct bt_mesh_blob_cli *cli,
                          struct bt_mesh_blob_target *target,
                          const struct bt_mesh_blob_xfer_info *info);

    /** @brief End of Get Transfer Progress procedure.
     *
     * Called when all Target nodes have responded or the procedure timed-out.
     *
     *  @param cli     BLOB Transfer Client instance.
     */
    void (*xfer_progress_complete)(struct bt_mesh_blob_cli *cli);
};

/** @cond INTERNAL_HIDDEN */
struct blob_cli_broadcast_ctx {
    /** Called for every Target node in unicast mode, or once in case of multicast mode. */
    void (*send)(struct bt_mesh_blob_cli *cli, uint16_t dst);
    /** Called after every @ref blob_cli_broadcast_ctx::send callback. */
    void (*send_complete)(struct bt_mesh_blob_cli *cli, uint16_t dst);
    /** If @ref blob_cli_broadcast_ctx::acked is true, called after all Target nodes
     *  have confirmed reception by @ref blob_cli_broadcast_rsp. Otherwise, called
     *  after transmission has been completed.
     */
    void (*next)(struct bt_mesh_blob_cli *cli);
    /** If true, every transmission needs to be confirmed by @ref blob_cli_broadcast_rsp before
     * @ref blob_cli_broadcast_ctx::next is called.
     */
    bool acked;
    /** If true, the message is always sent in a unicast way. */
    bool force_unicast;
    /** If true, non-responsive Target nodes won't be dropped after transfer has timed out. */
    bool optional;
    /** Set to true by the BLOB Transfer Client between blob_cli_broadcast
     *  and broadcast_complete calls.
     */
    bool is_inited;
    /* Defines a time in ms by which the broadcast API postpones sending the message to a next
     * target or completing the broadcast.
     */
    uint32_t post_send_delay_ms;
};
/** INTERNAL_HIDDEN @endcond */

/** BLOB Transfer Client model instance. */
struct bt_mesh_blob_cli {
    /** Event handler callbacks */
    const struct bt_mesh_blob_cli_cb *cb;

    /* Runtime state */
    const struct bt_mesh_model *mod;

    struct {
        struct bt_mesh_blob_target *target;
        struct blob_cli_broadcast_ctx ctx;
        struct k_work_delayable retry;
        /* Represents Client Timeout timer in a timestamp. Used in Pull mode only. */
        int64_t cli_timestamp;
        struct k_work_delayable complete;
        uint16_t pending;
        uint8_t retries;
        uint8_t sending : 1,
                cancelled : 1;
    } tx;

    const struct bt_mesh_blob_io *io;
    const struct bt_mesh_blob_cli_inputs *inputs;
    const struct bt_mesh_blob_xfer *xfer;
    uint32_t chunk_interval_ms;
    uint16_t block_count;
    uint16_t chunk_idx;
    uint16_t mtu_size;
    enum bt_mesh_blob_cli_state state;
    struct bt_mesh_blob_block block;
    struct bt_mesh_blob_cli_caps caps;
};

/** @brief Retrieve transfer capabilities for a list of Target nodes.
 *
 *  Queries the availability and capabilities of all Target nodes, producing a
 *  cumulative set of transfer capabilities for the Target nodes, and returning
 *  it through the @ref bt_mesh_blob_cli_cb::caps callback.
 *
 *  Retrieving the capabilities may take several seconds, depending on the
 *  number of Target nodes and mesh network performance. The end of the procedure
 *  is indicated through the @ref bt_mesh_blob_cli_cb::caps callback.
 *
 *  This procedure is not required, but strongly recommended as a
 *  preparation for a transfer to maximize performance and the chances of
 *  success.
 *
 *  @param cli     BLOB Transfer Client instance.
 *  @param inputs  Statically allocated BLOB Transfer Client transfer inputs.
 *
 *  @return 0 on success, or (negative) error code otherwise.
 */
int bt_mesh_blob_cli_caps_get(struct bt_mesh_blob_cli *cli,
                              const struct bt_mesh_blob_cli_inputs *inputs);

/** @brief Perform a BLOB transfer.
 *
 *  Starts sending the transfer to the Target nodes. Only Target nodes with a
 *  @c status of @ref BT_MESH_BLOB_SUCCESS will be considered.
 *
 *  The transfer will keep going either until all Target nodes have been dropped, or
 *  the full BLOB has been sent.
 *
 *  The BLOB transfer may take several minutes, depending on the number of
 *  Target nodes, size of the BLOB and mesh network performance. The end of the
 *  transfer is indicated through the @ref bt_mesh_blob_cli_cb::end callback.
 *
 *  A Client only supports one transfer at the time.
 *
 *  @param cli    BLOB Transfer Client instance.
 *  @param inputs Statically allocated BLOB Transfer Client transfer inputs.
 *  @param xfer   Statically allocated transfer parameters.
 *  @param io     BLOB stream to read the transfer from.
 *
 *  @return 0 on success, or (negative) error code otherwise.
 */
int bt_mesh_blob_cli_send(struct bt_mesh_blob_cli *cli,
                          const struct bt_mesh_blob_cli_inputs *inputs,
                          const struct bt_mesh_blob_xfer *xfer,
                          const struct bt_mesh_blob_io *io);

/** @brief Suspend the active transfer.
 *
 *  @param cli BLOB Transfer Client instance.
 *
 *  @return 0 on success, or (negative) error code otherwise.
 */
int bt_mesh_blob_cli_suspend(struct bt_mesh_blob_cli *cli);

/** @brief Resume the suspended transfer.
 *
 *  @param cli BLOB Transfer Client instance.
 *
 *  @return 0 on success, or (negative) error code otherwise.
 */
int bt_mesh_blob_cli_resume(struct bt_mesh_blob_cli *cli);

/** @brief Cancel an ongoing transfer.
 *
 *  @param cli BLOB Transfer Client instance.
 */
void bt_mesh_blob_cli_cancel(struct bt_mesh_blob_cli *cli);

void bt_mesh_blob_cli_send_cancel(struct bt_mesh_blob_cli *cli,
                                  const struct bt_mesh_blob_xfer *xfer);

/** @brief Get the progress of BLOB transfer.
 *
 *  This function can only be used if the BLOB Transfer Client is currently
 *  not performing a BLOB transfer.
 *  To get progress of the active BLOB transfer, use the
 *  @ref bt_mesh_blob_cli_xfer_progress_active_get function.
 *
 *  @param cli BLOB Transfer Client instance.
 *  @param inputs Statically allocated BLOB Transfer Client transfer inputs.
 *
 *  @return 0 on success, or (negative) error code otherwise.
 */
int bt_mesh_blob_cli_xfer_progress_get(struct bt_mesh_blob_cli *cli,
                                       const struct bt_mesh_blob_cli_inputs *inputs);

/** @brief Get the current progress of the active transfer in percent.
 *
 *  @param cli BLOB Transfer Client instance.
 *
 *  @return The current transfer progress, or 0 if no transfer is active.
 */
uint8_t bt_mesh_blob_cli_xfer_progress_active_get(struct bt_mesh_blob_cli *cli);

/** @brief Get the current state of the BLOB Transfer Client.
 *
 *  @param cli BLOB Transfer Client instance.
 *
 *  @return true if the BLOB Transfer Client is currently participating in a transfer or
 *          retrieving the capabilities and false otherwise.
 */
bool bt_mesh_blob_cli_is_busy(struct bt_mesh_blob_cli *cli);

/** @brief Set chunk sending interval in ms
 *
 *  This function is optional, and can be used to define how fast chunks are sent in the BLOB Client
 *  Model.
 *  Without an added delay, for example a Bluetooth Mesh DFU can cause network blockage by
 *  constantly sending the next chunks, especially if the chunks are sent to group addresses or
 *  multiple unicast addresses.
 *
 *  @note: Big intervals may cause timeouts. Increasing the @c timeout_base accordingly can
 *  circumvent this.
 *
 *  @param cli BLOB Transfer Client instance.
 *  @param interval_ms the delay before each chunk is sent out in ms.
 */
void bt_mesh_blob_cli_set_chunk_interval_ms(struct bt_mesh_blob_cli *cli, uint32_t interval_ms);

/** @cond INTERNAL_HIDDEN */
extern const struct bt_mesh_model_op _bt_mesh_blob_cli_op[];
extern const struct bt_mesh_model_cb _bt_mesh_blob_cli_cb;
/** @endcond */

/** @} */

#ifdef __cplusplus
}
#endif

#endif /* _BLE_MESH_v11_BLOB_CLI_H_ */
