//
// Created by lydxh on 24-11-27.
//

#ifndef HW_LIB_FLASH_H
#define HW_LIB_FLASH_H

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include <flash_cfg.h>
#include "flash_def.h"

#ifdef __cplusplus
extern "C" {
#endif

/* debug print function. Must be implement by user. */
#ifdef FLASH_DEBUG_MODE
#ifndef FLASH_DEBUG
#define FLASH_DEBUG(...) flash_log_debug(__FILE__, __LINE__, __VA_ARGS__)
#endif /* FLASH_DEBUG */
#else
#define FLASH_DEBUG(...)
#endif /* FLASH_DEBUG_MODE */

#ifndef FLASH_INFO
#define FLASH_INFO(...)  flash_log_info(__VA_ARGS__)
#endif

/* assert for developer. */
#ifdef FLASH_DEBUG_MODE
#define FLASH_ASSERT(EXPR)                                                      \
if (!(EXPR))                                                                   \
{                                                                              \
    FLASH_DEBUG("(%s) has assert failed at %s.", #EXPR, __FUNCTION__);          \
    while (1);                                                                 \
}
#else
#define FLASH_ASSERT(EXPR)
#endif

typedef struct FLASH_Dev FLASH_t;

struct FLASH_Dev {
    char *name;                                  /**< serial flash name */
    size_t index;                                /**< index of flash device information table  @see flash_table */
    flash_chip chip;                        /**< flash chip information */
    flash_spi spi;                                /**< SPI device */
    bool init_ok;                                /**< initialize OK flag */
    bool addr_in_4_byte;                         /**< flash is in 4-Byte addressing */
    struct {
        void (*delay)(void);                     /**< every retry's delay */
        size_t times;                            /**< default times for error retry */
    } retry;

    void *user_data;                             /**< some user data */

#ifdef FLASH_USING_QSPI
    flash_qspi_read_cmd_format read_cmd_format;   /**< fast read cmd format */
#endif

#ifdef FLASH_USING_SFDP
    flash_sfdp sfdp;                              /**< serial flash discoverable parameters by JEDEC standard */
#endif
};


/**
 * FLASH library initialize.
 *
 * @return result
 */
flash_err flash_init(void);

/**
 * FLASH initialize by flash device
 *
 * @param flash flash device
 *
 * @return result
 */
flash_err flash_device_init(FLASH_t *flash);

/**
 * get flash device by its index which in the flash information table
 *
 * @param index the index which in the flash information table  @see flash_table
 *
 * @return flash device
 */
FLASH_t *flash_get_device(size_t index);

/**
 * get flash device total number on flash device information table  @see flash_table
 *
 * @return flash device total number
 */
size_t flash_get_device_num(void);

/**
 * get flash device information table  @see flash_table
 *
 * @return flash device table pointer
 */
const FLASH_t *flash_get_device_table(void);

#ifdef FLASH_USING_QSPI
/**
 * Enbale the fast read mode in QSPI flash mode. Default read mode is normal SPI mode.
 *
 * it will find the appropriate fast-read instruction to replace the read instruction(0x03)
 * fast-read instruction @see FLASH_FLASH_EXT_INFO_TABLE
 *
 * @note When Flash is in QSPI mode, the method must be called after flash_device_init().
 *
 * @param flash flash device
 * @param data_line_width the data lines max width which QSPI bus supported, such as 1, 2, 4
 *
 * @return result
 */
flash_err flash_qspi_fast_read_enable(FLASH_t *flash, uint8_t data_line_width);
#endif /* FLASH_USING_QSPI */

/**
 * read flash data
 *
 * @param flash flash device
 * @param addr start address
 * @param size read size
 * @param data read data pointer
 *
 * @return result
 */
flash_err flash_read(const FLASH_t *flash, uint32_t addr, size_t size, uint8_t *data);

/**
 * erase flash data
 *
 * @note It will erase align by erase granularity.
 *
 * @param flash flash device
 * @param addr start address
 * @param size erase size
 *
 * @return result
 */
flash_err flash_erase(const FLASH_t *flash, uint32_t addr, size_t size);

/**
 * write flash data (no erase operate)
 *
 * @param flash flash device
 * @param addr start address
 * @param data write data
 * @param size write size
 *
 * @return result
 */
flash_err flash_write(const FLASH_t *flash, uint32_t addr, size_t size, const uint8_t *data);

/**
 * erase and write flash data
 *
 * @param flash flash device
 * @param addr start address
 * @param size write size
 * @param data write data
 *
 * @return result
 */
flash_err flash_erase_write(const FLASH_t *flash, uint32_t addr, size_t size, const uint8_t *data);

/**
 * erase all flash data
 *
 * @param flash flash device
 *
 * @return result
 */
flash_err flash_chip_erase(const FLASH_t *flash);

/**
 * read flash register status
 *
 * @param flash flash device
 * @param status register status
 *
 * @return result
 */
flash_err flash_read_status(const FLASH_t *flash, uint8_t *status);

/**
 * write status register
 *
 * @param flash flash device
 * @param is_volatile true: volatile mode, false: non-volatile mode
 * @param status register status
 *
 * @return result
 */
flash_err flash_write_status(const FLASH_t *flash, bool is_volatile, uint8_t status);

#ifdef __cplusplus
}
#endif
#endif //HW_LIB_FLASH_H
