/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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.
 */
#ifndef __SYS_LOG_FULL_H__
#define __SYS_LOG_FULL_H__

#include "os/mynewt.h"
#include "cbmem/cbmem.h"
#include "log_common/log_common.h"
#if MYNEWT_VAL(LOG_STATS)
#include "stats/stats.h"
#endif
#if MYNEWT_VAL(LOG_FCB) || MYNEWT_VAL(LOG_FCB2)
#include "log/log_fcb.h"
#endif

#if MYNEWT_VAL(LOG_FLAGS_IMAGE_HASH)
#define LOG_HDR_SIZE 19
#else
#define LOG_HDR_SIZE 15
#endif

#ifdef __cplusplus
extern "C" {
#endif

struct log;
struct log_entry_hdr;

/**
 * Used for walks and reads; indicates part of log to access.
 */
struct log_offset {
    /* If   lo_ts == -1: Only access last log entry;
     * Elif lo_ts == 0:  Don't filter by timestamp;
     * Else:             Only access entries whose ts >= lo_ts.
     */
    int64_t lo_ts;

    /* Only access entries whose index >= lo_index. */
    uint32_t lo_index;

    /* On read, lo_data_len gets populated with the number of bytes read. */
    uint32_t lo_data_len;

    /* Specific to walk / read function. */
    void *lo_arg;

    bool lo_walk_backward;
};

#if MYNEWT_VAL(LOG_STORAGE_INFO)
/**
 * Log storage information
 */
struct log_storage_info {
    uint32_t size;
    uint32_t used;
#if MYNEWT_VAL(LOG_STORAGE_WATERMARK)
    uint32_t used_unread;
#endif
};
#endif

#if MYNEWT_VAL(LOG_FLAGS_TRAILER)

#define LOG_TRAILER_LEN_SIZE 2

/** @typedef log_trailer_free_func_t
 * @brief Callback that frees the trailer buffer, to be called by user of the
 * library once append returns, either successfully or unsuccessfully
 *
 * @param log                 The log the trailer is being appended to
 * @param om                  mbuf pointer to be freed
 * @param arg                 Argument to be passed to the trailer handler
 *
 * @return                    0 on success, non-zero on failure
 */
typedef int log_trailer_free_func_t(struct log *log, struct os_mbuf *om, void *arg);

/** @typedef log_trailer_get_data_func_t
 * @brief Callback that is executed each time the corresponding log entry is
 * appended to
 *
 * @param log                 The log the trailer needs to be appended to
 * @param om                  mbuf pointer to be set to mbuf which contains
 *                            trailer data
 * @param arg                 Argument to be passed to the trailer handler
 *
 * @return                    0 on success, non-zero on failure
 */
typedef int log_trailer_get_data_func_t(struct log *log, struct os_mbuf **om, void *arg);

/** @typedef log_trailer_reset_data_func_t
 * @brief Callback used to reset trailer data per log
 *
 * @param log                 The log the trailer is to be read from
 * @param arg                 Argument to be passed to the trailer handler
 *
 * @return                    0 on success, non-zero on failure.
 */
typedef int log_trailer_reset_data_func_t(struct log *log, void *arg);
#endif

typedef int (*log_walk_func_t)(struct log *, struct log_offset *log_offset,
        const void *dptr, uint16_t len);

typedef int (*log_walk_body_func_t)(struct log *log,
        struct log_offset *log_offset, const struct log_entry_hdr *hdr,
        const void *dptr, uint16_t len);

typedef int (*lh_read_func_t)(struct log *, const void *dptr, void *buf,
        uint16_t offset, uint16_t len);
typedef int (*lh_read_mbuf_func_t)(struct log *, const void *dptr, struct os_mbuf *om,
                                   uint16_t offset, uint16_t len);
typedef int (*lh_append_func_t)(struct log *, void *buf, int len);
typedef int (*lh_append_body_func_t)(struct log *log,
                                     const struct log_entry_hdr *hdr,
                                     const void *body, int body_len);
typedef int (*lh_append_mbuf_func_t)(struct log *, struct os_mbuf *om);
typedef int (*lh_append_mbuf_body_func_t)(struct log *log,
                                          const struct log_entry_hdr *hdr,
                                          struct os_mbuf *om);
typedef int (*lh_walk_func_t)(struct log *,
        log_walk_func_t walk_func, struct log_offset *log_offset);
typedef int (*lh_flush_func_t)(struct log *);
typedef uint16_t (*lh_read_entry_len_func_t)(struct log *log, const void *dptr);
#if MYNEWT_VAL(LOG_STORAGE_INFO)
typedef int (*lh_storage_info_func_t)(struct log *, struct log_storage_info *);
#endif
#if MYNEWT_VAL(LOG_STORAGE_WATERMARK)
typedef int (*lh_set_watermark_func_t)(struct log *, uint32_t);
#endif
typedef int (*lh_registered_func_t)(struct log *);

struct log_handler {
    int log_type;
    lh_read_func_t log_read;
    lh_read_mbuf_func_t log_read_mbuf;
    lh_append_func_t log_append;
    lh_append_body_func_t log_append_body;
    lh_append_mbuf_func_t log_append_mbuf;
    lh_append_mbuf_body_func_t log_append_mbuf_body;
    lh_walk_func_t log_walk;
    lh_walk_func_t log_walk_sector;
    lh_flush_func_t log_flush;
    lh_read_entry_len_func_t log_read_entry_len;
#if MYNEWT_VAL(LOG_STORAGE_INFO)
    lh_storage_info_func_t log_storage_info;
#endif
#if MYNEWT_VAL(LOG_STORAGE_WATERMARK)
    lh_set_watermark_func_t log_set_watermark;
#endif
    /* Functions called only internally (no API for apps) */
    lh_registered_func_t log_registered;
};

/* Image hash length to be looged */
#define LOG_IMG_HASHLEN 4

/* Flags used to indicate type of data in reserved payload */
#define LOG_FLAGS_IMG_HASH (1 << 0)
#define LOG_FLAGS_TRAILER  (1 << 1)

#if MYNEWT_VAL(LOG_VERSION) == 3
struct log_entry_hdr {
    int64_t ue_ts;
    uint32_t ue_index;
    uint8_t ue_module;
    uint8_t ue_level;
    uint8_t ue_etype:4;
    uint8_t ue_flags:4;
    uint8_t ue_imghash[4];
}__attribute__((__packed__));
#else
#error "Unsupported log version"
#endif

#define LOG_BASE_ENTRY_HDR_SIZE (15)

#define LOG_MODULE_STR(module)      log_module_get_name(module)

#if MYNEWT_VAL(LOG_LEVEL) <= LOG_LEVEL_DEBUG
#define LOG_DEBUG(__l, __mod, __msg, ...) log_printf(__l, __mod, \
        LOG_LEVEL_DEBUG, __msg, ##__VA_ARGS__)
#else
#define LOG_DEBUG(__l, __mod, ...) IGNORE(__VA_ARGS__)
#endif

#if MYNEWT_VAL(LOG_LEVEL) <= LOG_LEVEL_INFO
#define LOG_INFO(__l, __mod, __msg, ...) log_printf(__l, __mod, \
        LOG_LEVEL_INFO, __msg, ##__VA_ARGS__)
#else
#define LOG_INFO(__l, __mod, ...) IGNORE(__VA_ARGS__)
#endif

#if MYNEWT_VAL(LOG_LEVEL) <= LOG_LEVEL_WARN
#define LOG_WARN(__l, __mod, __msg, ...) log_printf(__l, __mod, \
        LOG_LEVEL_WARN, __msg, ##__VA_ARGS__)
#else
#define LOG_WARN(__l, __mod, ...) IGNORE(__VA_ARGS__)
#endif

#if MYNEWT_VAL(LOG_LEVEL) <= LOG_LEVEL_ERROR
#define LOG_ERROR(__l, __mod, __msg, ...) log_printf(__l, __mod, \
        LOG_LEVEL_ERROR, __msg, ##__VA_ARGS__)
#else
#define LOG_ERROR(__l, __mod, ...) IGNORE(__VA_ARGS__)
#endif

#if MYNEWT_VAL(LOG_LEVEL) <= LOG_LEVEL_CRITICAL
#define LOG_CRITICAL(__l, __mod, __msg, ...) log_printf(__l, __mod, \
        LOG_LEVEL_CRITICAL, __msg, ##__VA_ARGS__)
#else
#define LOG_CRITICAL(__l, __mod, ...) IGNORE(__VA_ARGS__)
#endif

#if MYNEWT_VAL(LOG_STATS)
STATS_SECT_START(logs)
    STATS_SECT_ENTRY(writes)
    STATS_SECT_ENTRY(drops)
    STATS_SECT_ENTRY(regerr)
    STATS_SECT_ENTRY(errs)
    STATS_SECT_ENTRY(lost)
    STATS_SECT_ENTRY(too_long)
STATS_SECT_END

#define LOG_STATS_INC(log, name)        STATS_INC(log->l_stats, name)
#define LOG_STATS_INCN(log, name, cnt)  STATS_INCN(log->l_stats, name, cnt)
#else
#define LOG_STATS_INC(log, name)
#define LOG_STATS_INCN(log, name, cnt)
#endif

#if MYNEWT_VAL(LOG_FLAGS_TRAILER)
/* Trailer support callbacks */
struct log_trailer_handler {
    /* Frees the trailer buffer */
    log_trailer_free_func_t *lth_trailer_free;
    /* Trailer append callback used to append trailer to the log entry */
    log_trailer_get_data_func_t *lth_trailer_get_data;
    /* Trailer reset data callback used to reset the trailer data
     * per log which is defined by the application registering these callbacks
     */
    log_trailer_reset_data_func_t *lth_trailer_reset_data;
};
#endif

struct log {
    const char *l_name;
    const struct log_handler *l_log;
    void *l_arg;
    STAILQ_ENTRY(log) l_next;
    log_append_cb *l_append_cb;
    log_notify_rotate_cb *l_rotate_notify_cb;
    uint8_t l_level;
    uint16_t l_max_entry_len;   /* Log body length; if 0 disables check. */
#if !MYNEWT_VAL(LOG_GLOBAL_IDX)
    uint32_t l_idx;
#endif
#if MYNEWT_VAL(LOG_STATS)
    STATS_SECT_DECL(logs) l_stats;
#endif
#if MYNEWT_VAL(LOG_INIT_CB)
    /* Custom log init callback to be called by the last hdr
     * read function to read custom data from log entries
     * at init
     */
    log_walk_func_t l_init_cb;
#endif

#if MYNEWT_VAL(LOG_FLAGS_TRAILER)
    /* Log trailer support */
    /* Void argument for callbacks registeration */
    void *l_tr_arg;
    /* Trailer mbuf */
    struct os_mbuf *l_tr_om;
    /* Trailer handler with callbacks */
    struct log_trailer_handler *l_th;
#endif
};

/* Log system level functions (for all logs.) */
void log_init(void);
struct log *log_list_get_next(struct log *);

/*
 * Register per-user log module
 *
 * This function associates user log module with given name.
 *
 * Up to `LOG_MAX_USER_MODULES` (syscfg) modules can be registered.
 *
 * @param id    Selected module id
 * @param name  Module name
 *
 * @return  module id on success, 0 on failure
 */
uint8_t log_module_register(uint8_t id, const char *name);

/*
 * Get name for module id
 *
 * This works for both system and user registered modules.
 *
 * @param id  Module id
 *
 * @return  module name or NULL if not a valid module
 */
const char *log_module_get_name(uint8_t id);

/* Log functions, manipulate a single log */
int log_register(const char *name, struct log *log, const struct log_handler *,
                 void *arg, uint8_t level);

/**
 * @brief Configures the given log with the specified append callback.
 *
 * A log's append callback is executed each time an entry is appended to the
 * log.
 *
 * @param log                   The log to configure.
 * @param cb                    The callback to associate with the log.
 */
void log_set_append_cb(struct log *log, log_append_cb *cb);

/**
 * @brief Searches the list of registered logs for one with the specified name.
 *
 * @param name                  The name of the log to search for.
 *
 * @return                      The sought after log if found, NULL otherwise.
 */
struct log *log_find(const char *name);

/**
 * @brief Writes the raw contents of a flat buffer to the specified log.
 *
 * NOTE: The flat buffer must have an initial padding of length
 * `LOG_HDR_SIZE`. This padding is *not* reflected in the specified
 * length.  So, to log the string "abc", you should pass the following
 * arguments to this function:
 *
 *     data: <padding>abc   (total of `LOG_HDR_SIZE`+3 bytes.)
 *     len: 3
 *
 * @param log                   The log to write to.
 * @param module                The log module of the entry to write.
 * @param level                 The severity of the log entry to write.
 * @param etype                 The type of data being written; one of the
 *                                  `LOG_ETYPE_[...]` constants.
 * @param data                  The flat buffer to write.
 * @param len                   The number of bytes in the *message body*.
 *
 * @return                      0 on success; nonzero on failure.
 */
int log_append_typed(struct log *log, uint8_t module, uint8_t level,
                     uint8_t etype, void *data, uint16_t len);

/**
 * @brief Logs the contents of the provided mbuf, only freeing the mbuf on
 * failure.
 *
 * Logs the contents of the provided mbuf, only freeing the mbuf on failure.
 * On success, the mbuf remains allocated, but its structure may have been
 * modified by pullup operations.  The updated mbuf address is passed back to
 * the caller via a write to the supplied mbuf pointer-to-pointer.
 *
 * NOTE: The mbuf must have an initial padding of length
 * `LOG_HDR_SIZE`. So, to log the string "abc", you should pass an mbuf
 * with the following characteristics:
 *
 *     om_data: <padding>abc
 *     om_len: `LOG_HDR_SIZE` + 3
 *
 * @param log                   The log to write to.
 * @param module                The module ID of the entry to write.
 * @param level                 The severity of the entry to write; one of the
 *                                  `LOG_LEVEL_[...]` constants.
 * @param etype                 The type of data to write; one of the
 *                                  `LOG_ETYPE_[...]` constants.
 * @param om_ptr                Indirectly points to the mbuf to write.  This
 *                                  function updates the mbuf address if it
 *                                  changes.
 *
 * @return                      0 on success; nonzero on failure.
 */
int log_append_mbuf_typed_no_free(struct log *log, uint8_t module,
                                  uint8_t level, uint8_t etype,
                                  struct os_mbuf **om_ptr);

/**
 * @brief Logs the contents of the provided mbuf.
 *
 * Logs the contents of the provided mbuf.  This function always frees the mbuf
 * regardless of the outcome.
 *
 * NOTE: The mbuf must have an initial padding of length
 * `LOG_HDR_SIZE`. So, to log the string "abc", you should pass an mbuf
 * with the following characteristics:
 *
 *     om_data: <padding>abc
 *     om_len: `LOG_HDR_SIZE` + 3
 *
 * @param log                   The log to write to.
 * @param module                The module ID of the entry to write.
 * @param level                 The severity of the entry to write; one of the
 *                                  `LOG_LEVEL_[...]` constants.
 * @param etype                 The type of data to write; one of the
 *                                  `LOG_ETYPE_[...]` constants.
 * @param om                    The mbuf to write.
 *
 * @return                      0 on success; nonzero on failure.
 */
int log_append_mbuf_typed(struct log *log, uint8_t module, uint8_t level,
                          uint8_t etype, struct os_mbuf *om);

/**
 * @brief Writes the contents of a flat buffer to the specified log.
 *
 * @param log                   The log to write to.
 * @param module                The log module of the entry to write.
 * @param level                 The severity of the log entry to write.
 * @param etype                 The type of data being written; one of the
 *                                  `LOG_ETYPE_[...]` constants.
 * @param data                  The flat buffer to write.
 * @param len                   The number of bytes in the message body.
 *
 * @return                      0 on success; nonzero on failure.
 */
int log_append_body(struct log *log, uint8_t module, uint8_t level,
                    uint8_t etype, const void *body, uint16_t body_len);

/**
 * @brief Logs the contents of the provided mbuf, only freeing the mbuf on
 * failure.
 *
 * Logs the contents of the provided mbuf, only freeing the mbuf on failure.
 * On success, the mbuf remains allocated, but its structure may have been
 * modified by pullup operations.  The updated mbuf address is passed back to
 * the caller via a write to the supplied mbuf pointer-to-pointer.
 *
 * @param log                   The log to write to.
 * @param module                The module ID of the entry to write.
 * @param level                 The severity of the entry to write; one of the
 *                                  `LOG_LEVEL_[...]` constants.
 * @param etype                 The type of data to write; one of the
 *                                  `LOG_ETYPE_[...]` constants.
 * @param om_ptr                Indirectly points to the mbuf to write.  This
 *                                  function updates the mbuf address if it
 *                                  changes.
 *
 * @return                      0 on success; nonzero on failure.
 */
int log_append_mbuf_body_no_free(struct log *log, uint8_t module,
                                 uint8_t level, uint8_t etype,
                                 struct os_mbuf *om);

/**
 * @brief Logs the contents of the provided mbuf.
 *
 * Logs the contents of the provided mbuf.  This function always frees the mbuf
 * regardless of the outcome.
 *
 * @param log                   The log to write to.
 * @param module                The module ID of the entry to write.
 * @param level                 The severity of the entry to write; one of the
 *                                  `LOG_LEVEL_[...]` constants.
 * @param etype                 The type of data to write; one of the
 *                                  `LOG_ETYPE_[...]` constants.
 * @param om                    The mbuf to write.
 *
 * @return                      0 on success; nonzero on failure.
 */
int log_append_mbuf_body(struct log *log, uint8_t module, uint8_t level,
                         uint8_t etype, struct os_mbuf *om);

#if MYNEWT_VAL(LOG_CONSOLE)
struct log *log_console_get(void);
void log_console_init(void);
#endif

/**
 * @brief Writes the raw contents of a flat buffer to the specified log.
 *
 * NOTE: The flat buffer must have an initial padding of length
 * `LOG_HDR_SIZE`. This padding is *not* reflected in the specified
 * length.  So, to log the string "abc", you should pass the following
 * arguments to this function:
 *
 *     data: <padding>abc   (total of `LOG_HDR_SIZE`+3 bytes.)
 *     len: 3
 *
 * @param log                   The log to write to.
 * @param module                The log module of the entry to write.
 * @param level                 The severity of the log entry to write.
 * @param data                  The flat buffer to write.
 * @param len                   The number of byte in the *message body*.
 *
 * @return                      0 on success; nonzero on failure.
 */
static inline int
log_append(struct log *log, uint8_t module, uint8_t level, void *data,
           uint16_t len)
{
    return log_append_typed(log, module, level, LOG_ETYPE_STRING, data, len);
}

/**
 * @brief Logs the contents of the provided mbuf, only freeing the mbuf on
 * failure.
 *
 * Logs the contents of the provided mbuf, only freeing the mbuf on failure.
 * On success, the mbuf remains allocated, but its structure may have been
 * modified by pullup operations.  The updated mbuf address is passed back to
 * the caller via a write to the supplied mbuf pointer-to-pointer.
 *
 * NOTE: The mbuf must have an initial padding of length
 * `LOG_HDR_SIZE`. So, to log the string "abc", you should pass an mbuf
 * with the following characteristics:
 *
 *     om_data: <padding>abc
 *     om_len: `LOG_HDR_SIZE` + 3
 *
 * @param log                   The log to write to.
 * @param module                The module ID of the entry to write.
 * @param level                 The severity of the entry to write; one of the
 *                                  `LOG_LEVEL_[...]` constants.
 * @param om_ptr                Indirectly points to the mbuf to write.  This
 *                                  function updates the mbuf address if it
 *                                  changes.
 *
 * @return                      0 on success; nonzero on failure.
 */
static inline int
log_append_mbuf_no_free(struct log *log, uint8_t module, uint8_t level,
                        struct os_mbuf **om)
{
    return log_append_mbuf_typed_no_free(log, module, level, LOG_ETYPE_STRING,
                                         om);
}

/**
 * @brief Logs the contents of the provided mbuf.
 *
 * Logs the contents of the provided mbuf.  This function always frees the mbuf
 * regardless of the outcome.
 *
 * NOTE: The mbuf must have an initial padding of length
 * `LOG_HDR_SIZE`. So, to log the string "abc", you should pass an mbuf
 * with the following characteristics:
 *
 *     om_data: <padding>abc
 *     om_len: `LOG_HDR_SIZE` + 3
 *
 * @param log                   The log to write to.
 * @param module                The module ID of the entry to write.
 * @param level                 The severity of the entry to write; one of the
 *                                  `LOG_LEVEL_[...]` constants.
 * @param om                    The mbuf to write.
 *
 * @return                      0 on success; nonzero on failure.
 */
static inline int
log_append_mbuf(struct log *log, uint8_t module, uint8_t level,
                struct os_mbuf *om)
{
    return log_append_mbuf_typed(log, module, level, LOG_ETYPE_STRING, om);
}

void log_printf(struct log *log, uint8_t module, uint8_t level,
        const char *msg, ...);
int log_read(struct log *log, const void *dptr, void *buf, uint16_t off,
        uint16_t len);

#if MYNEWT_VAL(LOG_INIT_CB)
/**
 * Reads entry length from the specified log.
 *
 * @return                      The number of bytes of entry length; 0 on failure.
 */
uint16_t
log_read_entry_len(struct log *log, const void *dptr);

#if MYNEWT_VAL(LOG_FLAGS_TRAILER)

/**
 * @brief                Read log trailer of a specific entry
 *
 * @param log            The log to read trailer data from
 * @param dptr           Data pointer to the log entry
 * @param buf            Pointer to the buffer to read the trailer into
 *
 * @return 0 on success; nonzero on failure.
 */
uint16_t
log_read_trailer(struct log *log, const void *dptr, uint8_t *buf);

/**
 * @brief                Read log trailer length of a specific entry
 *
 * @param log            The log to read trailer data from
 * @param dptr           Data pointer to the log entry
 *
 * @return length of the trailer for a specific log entry;
 */
uint16_t log_read_trailer_len(struct log *log, const void *dptr);

/**Add commentMore actions
 * @brief Returns trailer data in an mbuf specified by the caller
 *
 * @param log                   The log to append to
 * @param om                    mbuf pointer to be set to buffer which contains
 *                              trailer data
 * @param arg                   Argument to be passed to the trailer handler
 *
 * @return                      0 on success; nonzero on failure.
 */
static inline int
log_trailer_get_data(struct log *log, struct os_mbuf **om, void *arg)
{
    if (log->l_th && log->l_th->lth_trailer_get_data) {
        return log->l_th->lth_trailer_get_data(log, om, arg);
    }
    return SYS_ENOTSUP;
}

/**
 * @brief Callback that frees the trailer buffer, to be called by user of the library
 * once append returns, either successfully or unsuccessfully
 *
 * @param log                   The log the trailer is being appended to
 * @param om                    mbuf pointer to be freed
 * @param arg                   Argument to be passed to the trailer handler
 *
 * @return                      0 on success; nonzero on failure.
 */
static inline int
log_trailer_free(struct log *log, struct os_mbuf *om, void *arg)
{
    if (log->l_th && log->l_th->lth_trailer_free) {
        return log->l_th->lth_trailer_free(log, om, arg);
    }
    return SYS_ENOTSUP;
}

/**
 * @brief Resets the trailer data in the log, calling user callback
 *
 * @param log                   The log to reset.
 * @param arg                   Argument to be passed to the trailer handler
 *
 * @return                      0 on success; nonzero on failure.
 */
static inline int
log_trailer_reset_data(struct log *log, void *arg)
{
    if (log->l_th && log->l_th->lth_trailer_reset_data) {
        return log->l_th->lth_trailer_reset_data(log, arg);
    }
    return SYS_ENOTSUP;
}

/**
 * @brief Register trailer callbacks, this is an experimental API
 *        and may be changed/deprecated later. This needs to be
 *        called before log_register() if the trailer data needs
 *        to be initialized when the log gets registered.
 *
 * @param log            The log to read from.
 * @param lth            The trailer handler to register.
 * @param arg            Argument to be passed to the trailer handler
 *
 * @return 0 on success; nonzero on failure.
 */
static inline void
log_trailer_cbs_register(struct log *log, struct log_trailer_handler *lth,
                         void *arg)
{
    if (!lth || !log) {
        return;
    }

    log->l_th = lth;
    log->l_tr_arg = arg;
}
#endif

/**
 * @brief Register Log init callback to be called by log_read_last_hdr
 * while reading the last log entry at init
 *
 * @param log                   The log to register.
 * @param cb                    The callback to associate with the log.
 *
 */
static inline void
log_register_init_cb(struct log *log, log_walk_func_t cb)
{
    log->l_init_cb = cb;
}
#endif

/**
 * @brief Reads a single log entry header.
 *
 * @param log                   The log to read from.
 * @param dptr                  Medium-specific data describing the area to
 *                                  read from; typically obtained by a call to
 *                                  `log_walk`.
 * @param hdr                   The destination header to read into.
 *
 * @return                      0 on success; nonzero on failure.
 */
int log_read_hdr(struct log *log, const void *dptr, struct log_entry_hdr *hdr);

/**
 * @brief Reads the header length
 *
 * @param hdr Ptr to the header
 *
 * @return Length of the header
 */
uint16_t
log_hdr_len(const struct log_entry_hdr *hdr);

/**
 * @brief Reads data from the body of a log entry into a flat buffer.
 *
 * @param log                   The log to read from.
 * @param dptr                  Medium-specific data describing the area to
 *                                  read from; typically obtained by a call to
 *                                  `log_walk`.
 * @param buf                   The destination buffer to read into.
 * @param off                   The offset within the log entry at which to
 *                                  start the read.
 * @param len                   The number of bytes to read.
 *
 * @return                      The number of bytes actually read on success;
 *                              -1 on failure.
 */
int log_read_body(struct log *log, const void *dptr, void *buf, uint16_t off,
                  uint16_t len);
int log_read_mbuf(struct log *log, const void *dptr, struct os_mbuf *om, uint16_t off,
                  uint16_t len);
/**
 * @brief Reads data from the body of a log entry into an mbuf.
 *
 * @param log                   The log to read from.
 * @param dptr                  Medium-specific data describing the area to
 *                                  read from; typically obtained by a call to
 *                                  `log_walk`.
 * @param om                    The destination mbuf to read into.
 * @param off                   The offset within the log entry at which to
 *                                  start the read.
 * @param len                   The number of bytes to read.
 *
 * @return                      The number of bytes actually read on success;
 *                              -1 on failure.
 */
int log_read_mbuf_body(struct log *log, const void *dptr, struct os_mbuf *om,
                       uint16_t off, uint16_t len);
int log_walk(struct log *log, log_walk_func_t walk_func,
        struct log_offset *log_offset);

/**
 * @brief Applies a callback to each message in the specified log.
 *
 * Similar to `log_walk`, except it passes the message header and body
 * separately to the callback.
 *
 * @param log                   The log to iterate.
 * @param walk_body_func        The function to apply to each log entry.
 * @param log_offset            Specifies the range of entries to process.
 *                                  Entries not matching these criteria are
 *                                  skipped during the walk.
 *
 * @return                      0 if the walk completed successfully;
 *                              nonzero on error or if the walk was aborted.
 */
int log_walk_body(struct log *log, log_walk_body_func_t walk_body_func,
        struct log_offset *log_offset);
int log_flush(struct log *log);

/**
 * @brief      Walking a section of FCB.
 *
 * @param log                   The log to iterate.
 * @param walk_body_func        The function to apply to each log entry.
 * @param log_offset            Specifies the range of entries to process.
 *                                  Entries not matching these criteria are
 *                                  skipped during the walk.
 *
 * @return                      0 if the walk completed successfully;
 *                              nonzero on error or if the walk was aborted.
 */
int log_walk_body_section(struct log *log, log_walk_body_func_t walk_body_func,
              struct log_offset *log_offset);

#if MYNEWT_VAL(LOG_MODULE_LEVELS)
/**
 * @brief Retrieves the globally configured minimum log level for the specified
 * module ID.
 *
 * Writes with a level less than the module's minimum level are discarded.
 *
 * @param module                The module whose level should be retrieved.
 *
 * @return                      The configured minimum level, or 0
 *                                  (LOG_LEVEL_DEBUG) if unconfigured.
 */
uint8_t log_level_get(uint8_t module);

/**
 * @brief Sets the globally configured minimum log level for the specified
 * module ID.
 *
 * Writes with a level less than the module's minimum level are discarded.
 *
 * @param module                The module to configure.
 * @param level                 The minimum level to assign to the module
 *                                  (0-15, inclusive).
 */
int log_level_set(uint8_t module, uint8_t level);
#else
static inline uint8_t
log_level_get(uint8_t module)
{
    /* All levels enabled. */
    return 0;
}

static inline int
log_level_set(uint8_t module, uint8_t level)
{
    return SYS_ENOTSUP;
}
#endif

/**
 * @brief Set log level for a logger.
 *
 * @param log                   The log to set level to.
 * @param level                 New log level
 */
void log_set_level(struct log *log, uint8_t level);

/**
 * @brief Get log level for a logger.
 *
 * @param log                   The log to set level to.
 *
 * @return                      current value of log level.
 */
uint8_t log_get_level(const struct log *log);

/**
 * @brief Set maximum length of an entry in the log. If set to
 *        0, no check will be made for maximum write length.
 *        Note that this is maximum log body length; the log
 *        entry header is not included in the check.
 *
 * @param log                   Log to set max entry length
 * @param level                 New max entry length
 */
void log_set_max_entry_len(struct log *log, uint16_t max_entry_len);

/**
 * Return last entry index in log.
 *
 * @param log Log to check last entry index from.
 *
 * @return last entry index
 */
uint32_t log_get_last_index(struct log *log);

#if MYNEWT_VAL(LOG_STORAGE_INFO)
/**
 * Return information about log storage
 *
 * This return information about size and usage of storage on top of which log
 * instance is created.
 *
 * @param log   The log to query.
 * @param info  The destination to write information to.
 *
 * @return 0 on success, error code otherwise
 *
 */
int log_storage_info(struct log *log, struct log_storage_info *info);
#endif

/**
 * Assign a callback function to be notified when the log is about to be rotated.
 *
 * @param log   The log
 * @param cb    The callback function to be executed.
 */
void
log_set_rotate_notify_cb(struct log *log, log_notify_rotate_cb *cb);

#if MYNEWT_VAL(LOG_STORAGE_WATERMARK)
/**
 * Set watermark on log
 *
 * This sets watermark on log item with given index. This information is used
 * to calculate size of entries which were logged after watermark item, i.e.
 * unread items. The watermark is stored persistently for each log.
 *
 * @param log    The log to set watermark on.
 * @param index  The index of a watermarked item.
 *
 * @return 0 on success, error code otherwise.
 */
int log_set_watermark(struct log *log, uint32_t index);
#endif

/**
 * Fill log current image hash
 *
 * @param hdr Ptr to the header
 *
 * @return 0 on success, non-zero on failure
 */
int
log_fill_current_img_hash(struct log_entry_hdr *hdr);

/**
 * @brief Reads the final log entry's header and processes trailer if the flag
 * indicates so from the specified log only if the log supports trailers
 * and mynewt val LOG_FLAGS_TRAILER is 1
 *
 * @param log            The log to read from.
 * @param out_hdr        On success, the last entry header gets written
 *                       here.
 * @param trailer_exists Pointer to a boolean
 *
 * @return 0 on success; nonzero on failure.
 */
int log_read_last_hdr_trailer(struct log *log, struct log_entry_hdr *out_hdr,
                              bool *trailer_exists);

/* Handler exports */
#if MYNEWT_VAL(LOG_CONSOLE)
extern const struct log_handler log_console_handler;
#endif
extern const struct log_handler log_cbmem_handler;
#if MYNEWT_VAL(LOG_FCB) || MYNEWT_VAL(LOG_FCB2)
extern const struct log_handler log_fcb_handler;
#endif

#ifdef __cplusplus
}
#endif

#endif /* __SYS_LOG_FULL_H__ */
