/*
 * SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
 *
 * SPDX-License-Identifier: Apache-2.0
 */
#pragma once

#include <stdint.h>
#include "esp_err.h"        // esp_err_t
#include "nvs.h"            // nvs entry data types
#include "nvs_flash.h"      // nvs_sec_cfg_t

#ifdef __cplusplus
extern "C" {
#endif

/**
 * @file nvs_bootloader.h
 * @brief Definition of a structures and public functions provided for NVS bootloader operations.
 */

/**
 * @brief Placeholders for buffer pointer and length of string type
 */
typedef struct {
    char* buff_ptr;     /**< Pointer to the buffer where string and terminating zero character will be read */
    size_t buff_len;    /**< Buffer length in bytes */
} nvs_bootloader_str_value_placeholder_t;

/**
 * @brief Union of value placeholders for all nvs_type_t supported by bootloader code
 */
typedef union {
    uint8_t   u8_val;   /**< Placeholder for unsigned 8 bit integer variable */
    int8_t    i8_val;   /**< Placeholder for signed 8 bit integer variable */
    uint16_t u16_val;   /**< Placeholder for unsigned 16 bit integer variable */
    int16_t   i16_val;  /**< Placeholder for signed 16 bit integer variable */
    uint32_t u32_val;   /**< Placeholder for unsigned 32 bit integer variable */
    int32_t   i32_val;  /**< Placeholder for signed 32 bit integer variable */
    uint64_t u64_val;   /**< Placeholder for unsigned 64 bit integer variable */
    int64_t   i64_val;  /**< Placeholder for signed 64 bit integer variable */
    nvs_bootloader_str_value_placeholder_t str_val; /**< Placeholder for string buffer information */
} nvs_bootloader_value_placeholder_t;

/**
 * @brief Structure representing one NVS bootloader entry.
 *
 * This structure serves as read operation input parameters and result value and status placeholder.
 * Before calling the `nvs_bootloader_read` function, populate the namespace_name, key_name and value_type members.
 * If string value has to be read, provide also buffer and its length in the `value.str_val` member.
 *
 * The result_code member will be populated by the function with the result of the read operation.
 * There are 2 possible situations and interpretations of the result_code:
 * If the return value of the `nvs_bootloader_read` was ESP_OK, the result_code will be one of the following:
 * - `ESP_OK`: Entry found, value member contains the data. This is the only case when the value member is populated.
 * - `ESP_ERR_NVS_TYPE_MISMATCH`: Entry was found, but requested datatype doesn't match datatype found in NVS
 * - `ESP_ERR_NVS_NOT_FOUND`: Data was not found.
 * - `ESP_ERR_INVALID_SIZE`: the value found for string is longer than the space provided in placeholder (str_val.buff_len)
 * If the return value of the function was ESP_ERR_INVALID_ARG, the result_code will be one of the following:
 * - `ESP_ERR_NVS_NOT_FOUND`: Check of this parameters was successful.
 * - `ESP_ERR_NVS_INVALID_NAME`: namespace_name is NULL or too long
 * - `ESP_ERR_NVS_KEY_TOO_LONG`: key_name NULL or too long
 * - `ESP_ERR_INVALID_SIZE`: the size of the buffer provided for NVS_TYPE_STR in placeholder (str_val.buff_len) is zero or exceeds maximum value NVS_CONST_STR_LEN_MAX_SIZE
 * - `ESP_ERR_INVALID_ARG`: Invalid datatype requested
 *
 */
typedef struct {
    const char* namespace_name;                 /**< Namespace of the entry */
    const char* key_name;                       /**< Key of the entry */
    nvs_type_t value_type;                      /**< Expected datatype to be read, can be any of NVS_TYPE_U*, NVS_TYPE_I* or NVS_TYPE_STR */
    esp_err_t result_code;                      /**< Result code of this entry. Explanation is in general description of the struct nvs_bootloader_read_list_t*/
    nvs_bootloader_value_placeholder_t value;   /**< Placeholder for value read */
    uint8_t namespace_index;                    /**< Index of the namespace (internal variable, do not use) */
} nvs_bootloader_read_list_t;

/**
 * @brief Reads data specified from the specified NVS partition.
 *
 * This function reads data from the NVS partition specified by `partition_name`.
 * Multiple NVS entries can be read in a single call. The list of entries to read is specified in the `read_list` array.
 * Function indicates overall success or failure by its return value. In case it is ESP_OK or ESP_ERR_INVALID_ARG,
 * result of validation / reading of individual entry is returned in the `result_code` member of each element of the `read_list` array.
 *
 * @param partition_name The name of the NVS partition to read from.
 * @param read_list_count The number of elements in the `read_list` array.
 * @param read_list An array of `nvs_bootloader_read_list_t` structures specifying the keys and buffers for reading data.
 *
 * @return
 * The return value of the function in this file can be one of the following:
 * - `ESP_OK`: The function successfully checked all input parameters and executed successfully.
 *   The individual `result_code` in `read_list` indicates the result of the lookup for a particular requested key.
 * - `ESP_ERR_INVALID_ARG`: The validity of all `read_list` input parameters was
 *   checked and failed for at least one of the parameters. The individual `result_code`
 *   in `read_list` provides the detailed reason. This error code is also returned when read_list is null or read_list_count is 0.
 * - `ESP_ERR_NVS_INVALID_NAME`: The partition name specified is too long or is null.
 * - `ESP_ERR_NVS_PART_NOT_FOUND`: The partition was not found in the partition table.
 * - `ESP_ERR_NVS_CORRUPT_KEY_PART`: Encryption-related problems.
 * - `ESP_ERR_NVS_WRONG_ENCRYPTION`: Encryption-related problems.
 * - `ESP_ERR_INVALID_STATE`: NVS partition or pages related errors - wrong size of partition, header inconsistent / entries
 *   inconsistencies, multiple active pages, page in state INVALID.
 * - `ESP_ERR_NO_MEM`: Cannot allocate memory required to perform the function.
 * - Technical errors in underlying storage.
 */
esp_err_t nvs_bootloader_read(const char* partition_name,
                              const size_t read_list_count,
                              nvs_bootloader_read_list_t read_list[]);

/**
 * @brief Initialize internal NVS security context, thus, enabling the NVS bootloader read API to decrypt encrypted NVS partitions
 *
 * @note Once `nvs_bootloader_secure_init()` is performed, `nvs_bootloader_read()` can correctly read only those NVS partitions
 * that are encrypted using the given `nvs_sec_cfg_t` security config, until `nvs_bootloader_secure_deinit()` clears the internal
 * NVS security context.
 *
 * @param sec_cfg NVS security key that would be used for decrypting the NVS partition
 * @return ESP_OK if security initialization is successful
 */
esp_err_t nvs_bootloader_secure_init(const nvs_sec_cfg_t *sec_cfg);

/**
 * @brief Clear the internal NVS security context
 */
void nvs_bootloader_secure_deinit(void);

/**
 * @brief Reads NVS bootloader security configuration set by the specified security scheme
 *
 * @param[in] scheme_cfg   Security scheme specific configuration
 *
 * @param[out] cfg         Security configuration (encryption keys)
 *
 * @return
 *      - ESP_OK, if cfg was read successfully;
 *      - ESP_ERR_INVALID_ARG, if scheme_cfg or cfg is NULL;
 *      - ESP_FAIL, if the key reading process fails
 */
esp_err_t nvs_bootloader_read_security_cfg(nvs_sec_scheme_t *scheme_cfg, nvs_sec_cfg_t* cfg);

#ifdef __cplusplus
}
#endif
