/**
 * Copyright (c) 2016 - 2020, 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 "sdk_common.h"
#if NRF_MODULE_ENABLED(NRF_LOG) && NRF_MODULE_ENABLED(NRF_LOG_BACKEND_FLASH)
#include "nrf_log_backend_flash.h"
#include "nrf_log_str_formatter.h"
#include "nrf_fstorage_nvmc.h"
#include "nrf_log.h"
#include "nrf_atomic.h"
#include "nrf_queue.h"
#include "app_error.h"
#include <stdbool.h>

#if (NRF_LOG_BACKEND_FLASHLOG_ENABLED == 0) && (NRF_LOG_BACKEND_CRASHLOG_ENABLED == 0)
#error "No flash backend enabled."
#endif

/** @brief Maximum logger message payload (arguments or data in hexdump) which can be stored. */
#define FLASH_LOG_MAX_PAYLOAD_SIZE (NRF_LOG_BACKEND_FLASH_SER_BUFFER_SIZE - sizeof(nrf_log_header_t))

/** @brief Size of serialization buffer in words. */
#define FLASH_LOG_SER_BUFFER_WORDS (NRF_LOG_BACKEND_FLASH_SER_BUFFER_SIZE/sizeof(uint32_t))

/** @brief Length of logger header. */
#define LOG_HEADER_LEN             (sizeof(nrf_log_header_t))

/** @brief Length of logger header given in 32 bit words. */
#define LOG_HEADER_LEN_WORDS       (LOG_HEADER_LEN/sizeof(uint32_t))

/** @brief Maximum possible length of standard log message. */
#define STD_LOG_MSG_MAX_LEN (LOG_HEADER_LEN + NRF_LOG_MAX_NUM_OF_ARGS*sizeof(uint32_t))

/* Buffer must be multiple of 4. */
STATIC_ASSERT((NRF_LOG_BACKEND_FLASH_SER_BUFFER_SIZE % sizeof(uint32_t)) == 0);

/* Buffer must fit standard log message. */
STATIC_ASSERT(NRF_LOG_BACKEND_FLASH_SER_BUFFER_SIZE >= STD_LOG_MSG_MAX_LEN);

/** @brief Flash page size in bytes. */
#define CODE_PAGE_SIZE        4096

/** @brief Start address of the area dedicated for flash log. */
#define FLASH_LOG_START_ADDR  (NRF_LOG_BACKEND_FLASH_START_PAGE * CODE_PAGE_SIZE)

/** @brief End address of the area dedicated for flash log. */
#define FLASH_LOG_END_ADDR    (FLASH_LOG_START_ADDR + (NRF_LOG_BACKEND_PAGES * CODE_PAGE_SIZE) - 1)

/** @brief Size of the area dedicated for flash log. */
#define FLASH_LOG_SIZE        (NRF_LOG_BACKEND_PAGES * CODE_PAGE_SIZE)

/** @brief Start address determined in runtime.
 *
 * If configuration indicates that flash log should be placed after application.
 * */
#if defined ( __CC_ARM )
#define RUNTIME_START_ADDR                                                                           \
               _Pragma("diag_suppress 170")                                                          \
               ((NRF_LOG_BACKEND_FLASH_START_PAGE == 0) ?                                            \
               (CODE_PAGE_SIZE*CEIL_DIV((uint32_t)CODE_END, CODE_PAGE_SIZE)) : FLASH_LOG_START_ADDR) \
               _Pragma("diag_default 170")
#else
#define RUNTIME_START_ADDR ((NRF_LOG_BACKEND_FLASH_START_PAGE == 0) ? \
               (CODE_PAGE_SIZE*CEIL_DIV((uint32_t)CODE_END, CODE_PAGE_SIZE)) : FLASH_LOG_START_ADDR)
#endif
static void fstorage_evt_handler(nrf_fstorage_evt_t * p_evt);

/** @brief Message queue for run time flash log. */
#if NRF_LOG_BACKEND_FLASHLOG_ENABLED
NRF_QUEUE_DEF(nrf_log_entry_t *,
              m_flashlog_queue,
              NRF_LOG_BACKEND_FLASHLOG_QUEUE_SIZE,
              NRF_QUEUE_MODE_NO_OVERFLOW);
static const nrf_queue_t * mp_flashlog_queue = &m_flashlog_queue;
#else
static const nrf_queue_t * mp_flashlog_queue = NULL;
#endif


/** @brief Message FIFO for crash log. */
#if NRF_LOG_BACKEND_CRASHLOG_ENABLED
NRF_QUEUE_DEF(nrf_log_entry_t *,
              m_crashlog_queue,
              NRF_LOG_BACKEND_CRASHLOG_FIFO_SIZE,
              NRF_QUEUE_MODE_NO_OVERFLOW);
static const nrf_queue_t * mp_crashlog_queue = &m_crashlog_queue;
#else
static const nrf_queue_t * mp_crashlog_queue = NULL;
#endif


/** @brief Fstorage instance used for flash log. */
NRF_FSTORAGE_DEF(nrf_fstorage_t m_log_flash_fstorage) =
{
    /* Set a handler for fstorage events. */
    .evt_handler = fstorage_evt_handler,
    .start_addr  = FLASH_LOG_START_ADDR,
    .end_addr    = FLASH_LOG_END_ADDR,
};

/** @brief Flash log state. */
typedef enum
{
    LOG_BACKEND_FLASH_ACTIVE,   /**< Flash backend is active. */
    LOG_BACKEND_FLASH_INACTIVE, /**< Flash backend is inactive. All incoming requests are skipped. */
    LOG_BACKEND_FLASH_IN_PANIC, /**< Flash backend is in panic mode. Incoming messages are written to flash in synchronous mode. */
} log_backend_flash_state_t;

static log_backend_flash_state_t m_state;                      /**< Flash logger backend state. */
static nrf_atomic_flag_t         m_busy_flag;                  /**< Flag indicating if module performs flash writing. */
static uint32_t                  m_flash_buf[FLASH_LOG_SER_BUFFER_WORDS]; /**< Buffer used for serializing messages. */
static uint32_t                  m_curr_addr;                  /**< Address of free spot in the storage area. */
static size_t                    m_curr_len;                   /**< Length of current message being written. */
static uint32_t                  m_dropped;                    /**< Number of dropped messages. */

/** @brief Log message string injected when entering panic mode. */
static const char crashlog_str[] =  "-----------CRASHLOG------------\r\n";

/** @brief Function saturates input to maximum possible length and rounds up value to be multiple
 *         of word size.
 *
 * @param length Length value.
 *
 * @return Modified input length.
 */
static uint32_t saturate_align_length(uint32_t length)
{
    length = (length > FLASH_LOG_MAX_PAYLOAD_SIZE) ? FLASH_LOG_MAX_PAYLOAD_SIZE : length; //saturate
    length = CEIL_DIV(length, sizeof(uint32_t))*sizeof(uint32_t);
    return length;
}


/**
 * @brief Function for copying logger message to the buffer.
 *
 * @param[in]     p_msg Logger message.
 * @param[out]    p_buf Output buffer where serialized message is placed.
 * @param[in,out] p_len Buffer size as input, length of prepared data as output.
 *
 * @return True if message fits into the buffer, false otherwise
 */
static bool msg_to_buf(nrf_log_entry_t * p_msg, uint8_t * p_buf, size_t * p_len)
{
    uint32_t         data_len;
    nrf_log_header_t header = {0};
    size_t           memobj_offset = HEADER_SIZE*sizeof(uint32_t);

    nrf_memobj_read(p_msg, &header, HEADER_SIZE*sizeof(uint32_t), 0);

    memcpy(p_buf, &header, sizeof(nrf_log_header_t));
    p_buf +=  sizeof(nrf_log_header_t);

    switch (header.base.generic.type)
    {
        case HEADER_TYPE_STD:
        {
            data_len = header.base.std.nargs * sizeof(uint32_t);
            break;
        }
        case HEADER_TYPE_HEXDUMP:
        {
            data_len = saturate_align_length(header.base.hexdump.len);
            break;
        }
        default:
            *p_len = 0;
            return false;
    }
    nrf_memobj_read(p_msg, p_buf, data_len, memobj_offset);

    if (*p_len >= sizeof(nrf_log_header_t) + data_len)
    {
        *p_len = sizeof(nrf_log_header_t) + data_len;
        return true;
    }
    else
    {
        return false;
    }
}

/**
 * @brief Function for getting logger message stored in flash.
 *
 * @param[in]  p_buf     Pointer to the location where message is stored.
 * @param[out] pp_header Pointer to the log message header.
 * @param[out] pp_data   Pointer to the log message data (arguments or data in case of hexdump).
 *
 * @return True if message was successfully fetched, false otherwise.
 */
static bool msg_from_buf(uint32_t *           p_buf,
                         nrf_log_header_t * * pp_header,
                         uint8_t * *          pp_data,
                         uint32_t *           p_len)
{
    *pp_header = (nrf_log_header_t *)p_buf;
    *pp_data = (uint8_t *)&p_buf[LOG_HEADER_LEN_WORDS];

    uint32_t data_len;

    switch ((*pp_header)->base.generic.type)
    {
        case HEADER_TYPE_STD:
        {
            data_len = ((*pp_header)->base.std.nargs)*sizeof(uint32_t);
            break;
        }
        case HEADER_TYPE_HEXDUMP:
        {

            data_len = saturate_align_length((*pp_header)->base.hexdump.len);
            break;
        }
        default:
            return false;
    }

    *p_len = LOG_HEADER_LEN + data_len;
    return true;
}

/**
 * @brief Function for processing log message queue.
 *
 * If writing to flash is synchronous then function drains the queue and writes all messages to flash.
 * If writing to flash is asynchronous then function starts single write operation. In asynchronous mode
 * function is called when new message is put into the queue from from flash operation callback.
 *
 * Function detects the situation that flash module reports attempt to write outside dedicated area.
 * In that case flash backend stops writing any new messages.
 *
 * @param p_queue           Queue will log messages
 * @param fstorage_blocking If true it indicates that flash operations are blocking, event handler is not used.
 */
static void log_msg_queue_process(nrf_queue_t const * p_queue, bool fstorage_blocking)
{
    nrf_log_entry_t * p_msg;
    bool              busy = false;
    while (nrf_queue_pop(p_queue, &p_msg) == NRF_SUCCESS)
    {
        ret_code_t err_code;

        m_curr_len = sizeof(m_flash_buf);
        if (!msg_to_buf(p_msg, (uint8_t *)m_flash_buf, &m_curr_len))
        {
            nrf_memobj_put(p_msg);
            continue;
        }

        err_code = nrf_fstorage_write(&m_log_flash_fstorage, m_curr_addr, m_flash_buf, m_curr_len, p_msg);

        if (err_code == NRF_SUCCESS)
        {
            if (fstorage_blocking)
            {
                m_curr_addr += m_curr_len;

                nrf_memobj_put(p_msg);
            }
            else
            {
                busy = true;
                break;
            }
        }
        else if (!fstorage_blocking && (err_code == NRF_ERROR_NO_MEM))
        {
            // fstorage queue got full. Drop entry.
            nrf_memobj_put(p_msg);
            m_dropped++;
            break;
        }
        else if (err_code == NRF_ERROR_INVALID_ADDR)
        {
            // Trying to write outside the area, flash log is full. Skip any new writes.
            nrf_memobj_put(p_msg);
            m_state = LOG_BACKEND_FLASH_INACTIVE;
        }
        else
        {
            ASSERT(false);
        }
    }

    if (!busy)
    {
        UNUSED_RETURN_VALUE(nrf_atomic_flag_clear(&m_busy_flag));
    }
}

static void queue_element_drop(nrf_queue_t const * p_queue)
{
    nrf_log_entry_t * p_msg;
    if (nrf_queue_pop(p_queue, &p_msg) == NRF_SUCCESS)
    {
        m_dropped++;
        nrf_memobj_put(p_msg);
    }
}

static void fstorage_evt_handler(nrf_fstorage_evt_t * p_evt)
{
    if (m_state == LOG_BACKEND_FLASH_ACTIVE)
    {
        switch (p_evt->id)
        {
            case NRF_FSTORAGE_EVT_WRITE_RESULT:
            {
                if (p_evt->result == NRF_SUCCESS)
                {
                    m_curr_addr += m_curr_len;
                    m_curr_len = 0;
                    log_msg_queue_process(mp_flashlog_queue, false);
                }
                else
                {
                    m_dropped++;
                }

                if (p_evt->p_param)
                {
                    nrf_memobj_put((nrf_log_entry_t *)p_evt->p_param);
                }
                break;
            }
            default:
                break;
        }
    }
    else if ((m_state == LOG_BACKEND_FLASH_INACTIVE) &&
             (p_evt->id == NRF_FSTORAGE_EVT_ERASE_RESULT) &&
             (p_evt->addr == RUNTIME_START_ADDR))
    {
        m_state = LOG_BACKEND_FLASH_ACTIVE;
    }
}

/**
 * @brief Function for enqueueing new message.
 *
 * If queue is full then the oldest message is freed.
 *
 * @param p_queue Queue.
 * @param p_msg   Message.
 *
 * @return Number of dropped messages
 */
static uint32_t message_enqueue(nrf_queue_t const * p_queue, nrf_log_entry_t * p_msg)
{
    uint32_t dropped = 0;

    //flag was set, busy so enqueue message
    while (nrf_queue_push(p_queue, &p_msg) != NRF_SUCCESS)
    {

        nrf_log_entry_t * p_old_msg;
        if (nrf_queue_pop(p_queue, &p_old_msg) == NRF_SUCCESS)
        {
            nrf_memobj_put(p_old_msg);
            dropped++;
        }
    }

    return dropped;
}


void nrf_log_backend_flashlog_put(nrf_log_backend_t const * p_backend,
                                  nrf_log_entry_t * p_msg)
{
    if (m_state == LOG_BACKEND_FLASH_ACTIVE)
    {
        nrf_memobj_get(p_msg);

        m_dropped += message_enqueue(mp_flashlog_queue, p_msg);

        if (nrf_atomic_flag_set_fetch(&m_busy_flag) == 0)
        {
            log_msg_queue_process(mp_flashlog_queue, false);
        }
    }
}


void nrf_log_backend_crashlog_put(nrf_log_backend_t const * p_backend,
                                  nrf_log_entry_t * p_msg)
{
    if (m_state != LOG_BACKEND_FLASH_INACTIVE)
    {
        nrf_memobj_get(p_msg);

        UNUSED_RETURN_VALUE(message_enqueue(mp_crashlog_queue, p_msg));
    }

    if (m_state == LOG_BACKEND_FLASH_IN_PANIC)
    {
        log_msg_queue_process(mp_crashlog_queue, true);
    }
}

void nrf_log_backend_flashlog_flush(nrf_log_backend_t const * p_backend)
{
    queue_element_drop(mp_flashlog_queue);
}

void nrf_log_backend_crashlog_flush(nrf_log_backend_t const * p_backend)
{
    queue_element_drop(mp_crashlog_queue);
}

void nrf_log_backend_flashlog_panic_set(nrf_log_backend_t const * p_backend)
{
    /* Empty */
}

/**
 * @brief Function for injecting log message which will indicate start of crash log.
 */
static void crashlog_marker_inject(void)
{
    nrf_log_header_t crashlog_marker_hdr = {
            .base = {
                .std = {
                     .type = HEADER_TYPE_STD,
                     .severity = NRF_LOG_SEVERITY_INFO_RAW,
                     .nargs = 0,
                     .addr = (uint32_t)crashlog_str & STD_ADDR_MASK
                }
            },
            .module_id = 0,
            .timestamp = 0,
    };
    m_flash_buf[0] = crashlog_marker_hdr.base.raw;
    m_flash_buf[1] = crashlog_marker_hdr.module_id;
    m_flash_buf[2] = crashlog_marker_hdr.timestamp;
    (void)nrf_fstorage_write(&m_log_flash_fstorage, m_curr_addr, m_flash_buf, LOG_HEADER_LEN, NULL);
    m_curr_addr += LOG_HEADER_LEN;
}


void nrf_log_backend_crashlog_panic_set(nrf_log_backend_t const * p_backend)
{
    if (nrf_fstorage_init(&m_log_flash_fstorage, &nrf_fstorage_nvmc, NULL) == NRF_SUCCESS)
    {
        m_state = LOG_BACKEND_FLASH_IN_PANIC;

        /* In case of Softdevice MWU may protect access to NVMC. */
        NVIC_DisableIRQ(MWU_IRQn);

        log_msg_queue_process(mp_flashlog_queue, true);

        crashlog_marker_inject();

        log_msg_queue_process(mp_crashlog_queue, true);
    }
    else
    {
        m_state = LOG_BACKEND_FLASH_INACTIVE;
    }
}

/**
 * @brief Function for determining first empty location in area dedicated for flash logger backend.
 */
static uint32_t empty_addr_get(void)
{
    uint32_t token = 0;
    nrf_log_header_t * p_dummy_header;
    uint8_t * p_dummy_data;

    while(nrf_log_backend_flash_next_entry_get(&token, &p_dummy_header, &p_dummy_data) == NRF_SUCCESS)
    {

    }

    return token;
}


ret_code_t nrf_log_backend_flash_init(nrf_fstorage_api_t const * p_fs_api)
{
    ret_code_t err_code;


    uint32_t start_addr = RUNTIME_START_ADDR;
    uint32_t end_addr   = start_addr + FLASH_LOG_SIZE - 1;

    m_log_flash_fstorage.start_addr = start_addr;
    m_log_flash_fstorage.end_addr   = end_addr;

    err_code = nrf_fstorage_init(&m_log_flash_fstorage, p_fs_api, NULL);
    if (err_code != NRF_SUCCESS)
    {
        return err_code;
    }

    m_curr_addr = empty_addr_get();
    m_state  = LOG_BACKEND_FLASH_ACTIVE;

    return err_code;
}


ret_code_t nrf_log_backend_flash_next_entry_get(uint32_t *                p_token,
                                                nrf_log_header_t * *      pp_header,
                                                uint8_t * *               pp_data)
{
    uint32_t * p_addr = p_token;
    uint32_t   len;

    *p_addr = (*p_addr == 0) ? RUNTIME_START_ADDR : *p_addr;

    if (nrf_fstorage_rmap(&m_log_flash_fstorage, *p_addr) == NULL)
    {
        //Supports only memories which can be mapped for reading.
        return NRF_ERROR_NOT_SUPPORTED;
    }

    if (msg_from_buf((uint32_t *)*p_addr, pp_header, pp_data, &len))
    {
        *p_addr += len;
        return NRF_SUCCESS;
    }
    else
    {
        return NRF_ERROR_NOT_FOUND;
    }
}


ret_code_t nrf_log_backend_flash_erase(void)
{
    ret_code_t err_code;

    m_state = LOG_BACKEND_FLASH_INACTIVE;
    err_code = nrf_fstorage_erase(&m_log_flash_fstorage, RUNTIME_START_ADDR, NRF_LOG_BACKEND_PAGES, NULL);
    
    m_curr_addr = RUNTIME_START_ADDR;

    return err_code;
}

#if NRF_LOG_BACKEND_FLASHLOG_ENABLED
const nrf_log_backend_api_t nrf_log_backend_flashlog_api = {
        .put       = nrf_log_backend_flashlog_put,
        .flush     = nrf_log_backend_flashlog_flush,
        .panic_set = nrf_log_backend_flashlog_panic_set,
};
#endif

#if NRF_LOG_BACKEND_CRASHLOG_ENABLED
const nrf_log_backend_api_t nrf_log_backend_crashlog_api = {
        .put       = nrf_log_backend_crashlog_put,
        .flush     = nrf_log_backend_crashlog_flush,
        .panic_set = nrf_log_backend_crashlog_panic_set,
};
#endif

#if NRF_LOG_BACKEND_FLASH_CLI_CMDS
#include "nrf_cli.h"

static uint8_t m_buffer[64];
static nrf_cli_t const * mp_cli;

static void cli_tx(void const * p_context, char const * p_buffer, size_t len);

static nrf_fprintf_ctx_t m_fprintf_ctx =
{
    .p_io_buffer    = (char *)m_buffer,
    .io_buffer_size = sizeof(m_buffer)-1,
    .io_buffer_cnt  = 0,
    .auto_flush     = true,
    .p_user_ctx     = &mp_cli,
    .fwrite         = cli_tx
};


static void flashlog_clear_cmd(nrf_cli_t const * p_cli, size_t argc, char ** argv)
{
    if (nrf_cli_help_requested(p_cli))
    {
        nrf_cli_help_print(p_cli, NULL, 0);
    }

    UNUSED_RETURN_VALUE(nrf_log_backend_flash_erase());
}

#include "nrf_delay.h"
static void cli_tx(void const * p_context, char const * p_buffer, size_t len)
{
    nrf_cli_t * * pp_cli = (nrf_cli_t * *)p_context;
    char * p_strbuf = (char *)&p_buffer[len];
    *p_strbuf = '\0';
    nrf_cli_fprintf((nrf_cli_t const *)*pp_cli, NRF_CLI_DEFAULT, p_buffer);
   // nrf_delay_ms(10);
}


static void entry_process(nrf_cli_t const * p_cli, nrf_log_header_t * p_header, uint8_t * p_data)
{
    mp_cli = p_cli;

    nrf_log_str_formatter_entry_params_t params =
    {
        .timestamp  = p_header->timestamp,
        .module_id  = p_header->module_id,
        .use_colors = 0,
    };

    switch (p_header->base.generic.type)
    {
        case HEADER_TYPE_STD:
        {
            params.severity = (nrf_log_severity_t)p_header->base.std.severity;
            nrf_log_std_entry_process((const char *)((uint32_t)p_header->base.std.addr),
                                      (uint32_t *)p_data,
                                      p_header->base.std.nargs,
                                      &params,
                                      &m_fprintf_ctx);
            break;
        }
        case HEADER_TYPE_HEXDUMP:
        {
            params.severity = (nrf_log_severity_t)p_header->base.hexdump.severity;

            nrf_log_hexdump_entry_process(p_data,
                                          p_header->base.hexdump.len,
                                          &params,
                                          &m_fprintf_ctx);
            break;
        }
        default:
            ASSERT(0);
    }

}


static void flashlog_read_cmd(nrf_cli_t const * p_cli, size_t argc, char ** argv)
{
    if (nrf_cli_help_requested(p_cli))
    {
        nrf_cli_help_print(p_cli, NULL, 0);
    }

    uint32_t           token  = 0;
    uint8_t *          p_data = NULL;
    bool               empty  = true;
    nrf_log_header_t * p_header;

    while (1)
    {
        if (nrf_log_backend_flash_next_entry_get(&token, &p_header, &p_data) == NRF_SUCCESS)
        {
            entry_process(p_cli, p_header, p_data);
            empty = false;
        }
        else
        {
            break;
        }
    }

    if (empty)
    {
        nrf_cli_fprintf(p_cli, NRF_CLI_ERROR, "Flash log empty\r\n");
    }
}


static void flashlog_status_cmd(nrf_cli_t const * p_cli, size_t argc, char ** argv)
{
    if (nrf_cli_help_requested(p_cli))
    {
        nrf_cli_help_print(p_cli, NULL, 0);
    }

    nrf_cli_fprintf(p_cli, NRF_CLI_NORMAL, "Flash log status:\r\n");
    nrf_cli_fprintf(p_cli, NRF_CLI_NORMAL, "\t\t- Location (address: 0x%08X, length: %d)\r\n",
                                                                RUNTIME_START_ADDR, FLASH_LOG_SIZE);
    nrf_cli_fprintf(p_cli, NRF_CLI_NORMAL, "\t\t- Current usage:%d%% (%d of %d bytes used)\r\n",
                                       100ul * (m_curr_addr - RUNTIME_START_ADDR)/FLASH_LOG_SIZE,
                                       m_curr_addr - RUNTIME_START_ADDR,
                                       FLASH_LOG_SIZE);
    nrf_cli_fprintf(p_cli, NRF_CLI_NORMAL, "\t\t- Dropped logs: %d\r\n", m_dropped);


}


NRF_CLI_CREATE_STATIC_SUBCMD_SET(m_flashlog_cmd)
{
    NRF_CLI_CMD(clear,   NULL, "Remove logs",      flashlog_clear_cmd),
    NRF_CLI_CMD(read,    NULL, "Read stored logs", flashlog_read_cmd),
    NRF_CLI_CMD(status,  NULL, "Flash log status", flashlog_status_cmd),
    NRF_CLI_SUBCMD_SET_END
};

NRF_CLI_CMD_REGISTER(flashlog, &m_flashlog_cmd, "Commands for reading logs stored in non-volatile memory", NULL);

#endif //NRF_LOG_BACKEND_FLASH_CLI_CMDS

#endif //NRF_MODULE_ENABLED(NRF_LOG) && NRF_MODULE_ENABLED(NRF_LOG_BACKEND_FLASH)
