/*
 * 模块名称：NAND FTL 适配层（dhara + FatFS diskio 胶水）
 * 模块功能：实现 dhara NAND HAL 和 FatFS 磁盘 I/O，连接 GD5F2GQ5UE 底层驱动
 * 适用平台：STM32F407ZGT6
 * 作者：王建锋
 * 创建日期：2026-07-20
 * 修改记录：
 *     2026-07-20  王建锋  创建初始版本
 */

/* 头文件包含区 */
#include <string.h>
#include "gd5f2gq5ue.h"
#include "ff.h"
#include "diskio.h"
#include "nand.h"
#include "map.h"

/* FTL 分区起始地址（块 0，偏移 0，占用全部 256MB） */
#define FTL_FATFS_OFFSET    0U

/* 调试输出配置 */
#define DBG_TAG "[NAND_FTL]"
#include "dbg_log.h"

/* 私有宏定义区 */
#define FTL_PAGE_SIZE           GD5F_PAGE_SIZE
#define FTL_SECTORS_PER_PAGE    (FTL_PAGE_SIZE / 512)
#define FTL_START_BLOCK         (FTL_FATFS_OFFSET / GD5F_BLOCK_SIZE)
#define FTL_NUM_BLOCKS          (GD5F_TOTAL_BLOCKS - FTL_START_BLOCK)
#define FTL_START_PAGE          (FTL_START_BLOCK * GD5F_PAGES_PER_BLOCK)

/* dhara 逻辑地址到物理地址转换 */
static inline uint32_t ftl_phy_block(dhara_block_t b) { return b + FTL_START_BLOCK; }
static inline uint32_t ftl_phy_page(dhara_page_t p)   { return p + FTL_START_PAGE; }

/* ======================== 私有变量 ======================== */

/* dhara 核心数据结构 */
static struct dhara_nand      s_nand;
static struct dhara_map       s_map;
static uint8_t                s_page_buf[FTL_PAGE_SIZE];  /* dhara 内部页缓冲区 */

/* 页面缓存（FatFS 读写缓存） */
static uint8_t                s_cache_buf[FTL_PAGE_SIZE];
static dhara_sector_t         s_cached_lpn;
static uint8_t                s_cache_dirty;

/* 拷贝临时缓冲区 */
static uint8_t                s_copy_buf[FTL_PAGE_SIZE];

/* 初始化状态 */
static uint8_t                s_initialized;

/* ======================== 私有函数声明 ======================== */

static int ftl_flush_cache(void);
static int ftl_read_page(dhara_sector_t lpn);

/* SPI 原语从 gd5f2gq5ue.h 获取 */

/* ======================== 页面缓存管理 ======================== */

static int ftl_flush_cache(void) {
    if (!s_cache_dirty) {
        return GD5F_OK;
    }

    dhara_error_t err;
    if (dhara_map_write(&s_map, s_cached_lpn, s_cache_buf, &err) < 0) {
        DBG_ERROR("Cache flush failed: LPN=%lu err=%d", s_cached_lpn, err);
        return GD5F_ERROR;
    }

    s_cache_dirty = 0;
    return GD5F_OK;
}

static int ftl_read_page(dhara_sector_t lpn) {
    dhara_error_t err;

    if (dhara_map_read(&s_map, lpn, s_cache_buf, &err) < 0) {
        DBG_ERROR("Page read failed: LPN=%lu err=%d", lpn, err);
        return GD5F_ERROR;
    }

    s_cached_lpn = lpn;
    s_cache_dirty = 0;
    return GD5F_OK;
}

/* ======================== dhara NAND HAL ======================== */

int dhara_nand_is_bad(const struct dhara_nand *n, dhara_block_t b) {
    (void)n;
    return gd5f2gq5ue_is_block_bad(ftl_phy_block(b));
}

void dhara_nand_mark_bad(const struct dhara_nand *n, dhara_block_t b) {
    (void)n;
    gd5f2gq5ue_mark_block_bad(ftl_phy_block(b));
}

int dhara_nand_erase(const struct dhara_nand *n, dhara_block_t b, dhara_error_t *err) {
    (void)n;
    int ret = gd5f_block_erase(ftl_phy_block(b));
    if (ret == GD5F_ERASE_FAIL || ret != GD5F_OK) {
        dhara_nand_mark_bad(n, b);
        dhara_set_error(err, DHARA_E_BAD_BLOCK);
        return -1;
    }
    return 0;
}

int dhara_nand_prog(const struct dhara_nand *n, dhara_page_t p, const uint8_t *data, dhara_error_t *err) {
    int ret;
    uint32_t phy_pg = ftl_phy_page(p);

    gd5f_write_enable();
    ret = gd5f_program_load(0, data, FTL_PAGE_SIZE);
    if (ret != GD5F_OK) {
        dhara_nand_mark_bad(n, p >> n->log2_ppb);
        dhara_set_error(err, DHARA_E_BAD_BLOCK);
        return -1;
    }

    ret = gd5f_program_exec(phy_pg);
    if (ret == GD5F_PROGRAM_FAIL || ret != GD5F_OK) {
        dhara_nand_mark_bad(n, p >> n->log2_ppb);
        dhara_set_error(err, DHARA_E_BAD_BLOCK);
        return -1;
    }

    return 0;
}

int dhara_nand_is_free(const struct dhara_nand *n, dhara_page_t p) {
    (void)n;
    uint32_t i;
    uint8_t buf[64];
    uint32_t phy_pg = ftl_phy_page(p);

    if (gd5f_page_read(phy_pg) != GD5F_OK) {
        return 0;
    }
    gd5f_read_from_cache(0, buf, sizeof(buf));

    for (i = 0; i < sizeof(buf); i++) {
        if (buf[i] != 0xFF) {
            return 0;
        }
    }
    return 1;
}

int dhara_nand_read(const struct dhara_nand *n, dhara_page_t p, size_t offset, size_t length, uint8_t *data, dhara_error_t *err) {
    (void)n;
    uint32_t phy_pg = ftl_phy_page(p);

    if (gd5f_page_read(phy_pg) != GD5F_OK) {
        dhara_set_error(err, DHARA_E_ECC);
        return -1;
    }

    if (gd5f_check_ecc() != GD5F_OK) {
        dhara_set_error(err, DHARA_E_ECC);
        return -1;
    }

    gd5f_read_from_cache((uint16_t)offset, data, length);
    return 0;
}

int dhara_nand_copy(const struct dhara_nand *n, dhara_page_t src, dhara_page_t dst, dhara_error_t *err) {
    (void)n;

    if (dhara_nand_read(n, src, 0, FTL_PAGE_SIZE, s_copy_buf, err) < 0) {
        return -1;
    }

    if (dhara_nand_prog(n, dst, s_copy_buf, err) < 0) {
        return -1;
    }

    return 0;
}

/* ======================== FatFS 磁盘 I/O ======================== */

DSTATUS disk_initialize(BYTE pdrv) {
    if (pdrv != 0) {
        return STA_NOINIT;
    }

    if (s_initialized) {
        return 0;
    }

    DBG_INFO("Initializing NAND FTL...");

    s_nand.log2_page_size = 11;
    s_nand.log2_ppb = 6;
    s_nand.num_blocks = FTL_NUM_BLOCKS;

    dhara_map_init(&s_map, &s_nand, s_page_buf, 4);

    memset(s_cache_buf, 0, FTL_PAGE_SIZE);
    s_cached_lpn = 0;
    s_cache_dirty = 0;

    {
        dhara_error_t err;
        if (dhara_map_resume(&s_map, &err) < 0) {
            DBG_INFO("No valid map found, creating fresh (err=%d)", err);
            dhara_map_clear(&s_map);
        } else {
            DBG_INFO("Map resumed: %lu/%lu sectors used",
                     dhara_map_size(&s_map), dhara_map_capacity(&s_map));
        }
    }

    s_initialized = 1;
    DBG_INFO("NAND FTL ready (capacity: %lu pages = %lu MB)",
             dhara_map_capacity(&s_map),
             (dhara_map_capacity(&s_map) * FTL_PAGE_SIZE) / (1024 * 1024));

    return 0;
}

DSTATUS disk_status(BYTE pdrv) {
    if (pdrv != 0) {
        return STA_NOINIT;
    }
    if (!s_initialized) {
        return STA_NOINIT;
    }
    return 0;
}

DRESULT disk_read(BYTE pdrv, BYTE *buff, LBA_t sector, UINT count) {
    if (pdrv != 0 || !buff) {
        return RES_PARERR;
    }
    if (!s_initialized && disk_initialize(pdrv) != 0) {
        return RES_NOTRDY;
    }

    while (count > 0) {
        dhara_sector_t lpn = (dhara_sector_t)(sector / FTL_SECTORS_PER_PAGE);
        uint32_t offset = (uint32_t)(sector % FTL_SECTORS_PER_PAGE) * 512;
        uint32_t batch = FTL_SECTORS_PER_PAGE - (sector % FTL_SECTORS_PER_PAGE);

        if (batch > count) {
            batch = count;
        }

        if (lpn != s_cached_lpn) {
            if (ftl_flush_cache() != GD5F_OK) {
                return RES_ERROR;
            }
            if (ftl_read_page(lpn) != GD5F_OK) {
                return RES_ERROR;
            }
        }

        memcpy(buff, s_cache_buf + offset, batch * 512);
        buff += batch * 512;
        sector += batch;
        count -= (UINT)batch;
    }

    return RES_OK;
}

DRESULT disk_write(BYTE pdrv, const BYTE *buff, LBA_t sector, UINT count) {
    if (pdrv != 0 || !buff) {
        return RES_PARERR;
    }
    if (!s_initialized && disk_initialize(pdrv) != 0) {
        return RES_NOTRDY;
    }

    while (count > 0) {
        dhara_sector_t lpn = (dhara_sector_t)(sector / FTL_SECTORS_PER_PAGE);
        uint32_t offset = (uint32_t)(sector % FTL_SECTORS_PER_PAGE) * 512;
        uint32_t batch = FTL_SECTORS_PER_PAGE - (sector % FTL_SECTORS_PER_PAGE);

        if (batch > count) {
            batch = count;
        }

        if (lpn != s_cached_lpn) {
            if (ftl_flush_cache() != GD5F_OK) {
                return RES_ERROR;
            }
            if (ftl_read_page(lpn) != GD5F_OK) {
                return RES_ERROR;
            }
        }

        memcpy(s_cache_buf + offset, buff, batch * 512);
        s_cache_dirty = 1;

        if (offset == 0 && batch == FTL_SECTORS_PER_PAGE) {
            if (ftl_flush_cache() != GD5F_OK) {
                return RES_ERROR;
            }
        }

        buff += batch * 512;
        sector += batch;
        count -= (UINT)batch;
    }

    return RES_OK;
}

DRESULT disk_ioctl(BYTE pdrv, BYTE cmd, void *buff) {
    if (pdrv != 0) {
        return RES_PARERR;
    }
    if (!s_initialized) {
        return RES_NOTRDY;
    }

    switch (cmd) {
    case CTRL_SYNC:
        if (ftl_flush_cache() != GD5F_OK) {
            return RES_ERROR;
        }
        {
            dhara_error_t err;
            if (dhara_map_sync(&s_map, &err) < 0) {
                DBG_ERROR("Map sync failed: %d", err);
                return RES_ERROR;
            }
        }
        return RES_OK;

    case GET_SECTOR_COUNT: {
        LBA_t *p_sectors = (LBA_t *)buff;
        dhara_sector_t pages = dhara_map_capacity(&s_map);
        *p_sectors = pages * FTL_SECTORS_PER_PAGE;
        return RES_OK;
    }

    case GET_SECTOR_SIZE:
        *(WORD *)buff = 512;
        return RES_OK;

    case GET_BLOCK_SIZE:
        *(DWORD *)buff = 1;
        return RES_OK;

    default:
        return RES_PARERR;
    }
}

/* ======================== 公共函数定义 ======================== */

int nand_ftl_format(void) {
    dhara_error_t err;

    if (!s_initialized) {
        if (disk_initialize(0) != 0) {
            return GD5F_ERROR;
        }
    }

    if (ftl_flush_cache() != GD5F_OK) {
        return GD5F_ERROR;
    }

    dhara_map_clear(&s_map);

    if (dhara_map_sync(&s_map, &err) < 0) {
        DBG_ERROR("Map sync after clear failed: %d", err);
        return GD5F_ERROR;
    }

    s_cache_dirty = 0;

    DBG_INFO("NAND FTL formatted, capacity: %lu pages", dhara_map_capacity(&s_map));
    return GD5F_OK;
}
