#if UART_DEBUG || DEBUG_MEMORY_LOG_ENABLED
#define DEBUG_PRINT_ENABLE 1
#else
#define DEBUG_PRINT_ENABLE 0
#endif

#include <stdlib.h>
#include <xs1.h>
#include <platform.h>
#include "lfs.h"
#include "rtos_qspi_flash.h"
#include "swlock.h"
#include "debug_print.h"
// variables used by the filesystem
lfs_t lfs;
lfs_file_t file;

// Global flash lock: protects ALL flash hardware access (LFS, DFU, key programming).
// Only one core may perform flash I/O at a time.
swlock_t flash_lock = SWLOCK_INITIAL_VALUE;

static rtos_qspi_flash_t qspi_flash_ctx_s;
#define FLASH_CLKBLK XS1_CLKBLK_3
#ifndef FS_BASE_ADDR
#define FS_BASE_ADDR (2500 * 1024)
#endif
#define SECTOR_SIZE  4096
rtos_qspi_flash_t *qspi_flash_ctx = &qspi_flash_ctx_s;

__attribute__((fptrgroup("local_block_device_read_fptr_grp")))
int local_block_device_read(const struct lfs_config *c, lfs_block_t block,
            lfs_off_t off, void *buffer, lfs_size_t size)
{
    unsigned address = (FS_BASE_ADDR + block * SECTOR_SIZE + off);
    qspi_flash_ctx->read(qspi_flash_ctx, buffer, address, size);
    return 0;
}

__attribute__((fptrgroup("local_block_device_prog_fptr_grp")))
int local_block_device_prog(const struct lfs_config *c, lfs_block_t block,
            lfs_off_t off, const void *buffer, lfs_size_t size)
{
    unsigned address = (FS_BASE_ADDR + block * SECTOR_SIZE + off);
    qspi_flash_ctx->write(qspi_flash_ctx, buffer, address, size);
    return 0;
}

__attribute__((fptrgroup("local_block_device_erase_fptr_grp")))
int local_block_device_erase(const struct lfs_config *c, lfs_block_t block)
{
    unsigned address = (FS_BASE_ADDR + block * SECTOR_SIZE);
    qspi_flash_ctx->erase(qspi_flash_ctx, address, SECTOR_SIZE);
    return 0;
}

__attribute__((fptrgroup("local_block_device_sync_fptr_grp")))
int local_block_device_sync(const struct lfs_config *c)
{
    return 0;
}

// configuration of the filesystem is provided by this struct
const struct lfs_config cfg = {
    // block device operations
    .read  = local_block_device_read,
    .prog  = local_block_device_prog,
    .erase = local_block_device_erase,
    .sync  = local_block_device_sync,

    // block device configuration
    .read_size = 16,
    .prog_size = 16,
    .block_size = 4096,
    .block_count = 128,
    .cache_size = 16,
    .lookahead_size = 16,
    .block_cycles = 500,
};

// Session-based exclusive flash access:
//   lfs_init()  — acquire lock, init QSPI hardware, mount LFS. Lock is HELD.
//   lfs_deinit() — unmount LFS, release lock.
// All flash I/O between init/deinit is protected. No per-operation locking needed.
// Only ONE core can hold the session at a time. Other cores block in lfs_init().

#if HID_DFU_EN
extern unsigned g_in_fw_upgrade;
#endif

static int lfs_session_active = 0;

int lfs_init(void) {
#if HID_DFU_EN
    // 固件升级期间禁止LFS操作，避免与DFU flash访问冲突
    if (g_in_fw_upgrade) {
        debug_printf("lfs_init: blocked during firmware upgrade\n");
        lfs_session_active = 0;
        return -2;
    }
#endif
    swlock_acquire(&flash_lock);
    lfs_session_active = 1;
    rtos_qspi_flash_init(
            qspi_flash_ctx,
            FLASH_CLKBLK,
            XS1_PORT_1B,
            XS1_PORT_1C,
            XS1_PORT_4B,
            NULL);


    // mount the filesystem
    int err = lfs_mount(&lfs, &cfg);

    // reformat if we can't mount the filesystem
    // this should only happen on the first boot
    if (err) {
        debug_printf("no lfs partiton is found, formating ...\n");
        lfs_format(&lfs, &cfg);
        err = lfs_mount(&lfs, &cfg);
        if (err) {
            debug_printf("lfs_init: format+remount failed, err=%d\n", err);
            lfs_session_active = 0;
            swlock_release(&flash_lock);
            return -1;
        }
        // Format succeeded and FS is now mounted — return 0 so callers
        // can proceed with reads/writes on the (now empty) filesystem.
        debug_printf("lfs_init: formatted and remounted successfully\n");
        return 0;
    }
    // Lock remains held until lfs_deinit() is called
    return 0;

}

void lfs_deinit(void) {
    if (!lfs_session_active) {
        return;  // lfs_init was blocked (e.g. during firmware upgrade), nothing to clean up
    }
    lfs_unmount(&lfs);
    lfs_session_active = 0;
    swlock_release(&flash_lock);
}

int lfs_format_all(void) {
    int res = lfs_init();
    if (res != 0) {
        return res; // Already failed and released lock, or blocked
    }
    
    // Mount succeeded, but we want to format, so unmount first
    lfs_unmount(&lfs);
    
    // 格式化整个LFS分区，清除所有数据
    int err = lfs_format(&lfs, &cfg);
    
    // Even if format fails, we must cleanup the session
    lfs_session_active = 0;
    swlock_release(&flash_lock);
    return err;
}

// All functions below must be called within an lfs_init()/lfs_deinit() session.
// No internal locking — the session lock protects the entire operation sequence.

#pragma stackfunction 1500
void lfs_read_config(unsigned char * config, unsigned char * buffer, unsigned size)
{
    debug_printf("lfs_read_config: %s, size: %d\n", config, size);
    int result = lfs_file_open(&lfs, &file, config, LFS_O_RDWR | LFS_O_CREAT);
    debug_printf("lfs_read_config: %s, result: %d\n", config, result);
    if (result != 0) {
        debug_printf("lfs_read_config: %s, open file failed\n", config);
        return;
    }
    debug_printf("lfs_read_config: %s, file opened\n", config);
    result = lfs_file_read(&lfs, &file, buffer, size);
    debug_printf("lfs_read_config: %s, result: %d\n", config, result);
    if (result < 0) {
        debug_printf("lfs_read_config: %s, read file failed, error: %d\n", config, result);
        lfs_file_close(&lfs, &file);
        return;
    }
    lfs_file_close(&lfs, &file);
}


#pragma stackfunction 1500
void lfs_write_config(unsigned char * config, unsigned char * buffer, unsigned size)
{
    debug_printf("lfs_write_config: %s, size: %d\n", config, size);
    int result = lfs_file_open(&lfs, &file, config, LFS_O_RDWR | LFS_O_CREAT);
    if (result != 0) {
        debug_printf("lfs_write_config: open file failed\n");
        return;
    }
    result = lfs_file_rewind(&lfs, &file);
    if (result != 0) {
        debug_printf("lfs_write_config: rewind file failed\n");
        lfs_file_close(&lfs, &file);
        return;
    }
    lfs_file_write(&lfs, &file, buffer, size);
    lfs_file_close(&lfs, &file);
}


// 检查文件是否存在
#pragma stackfunction 1200
int lfs_file_exists(const char * file_path)
{
    lfs_file_t file;
    int result = lfs_file_open(&lfs, &file, file_path, LFS_O_RDONLY);
    if (result == 0) {
        lfs_file_close(&lfs, &file);
        return 1;  // 文件存在
    }
    return 0;  // 文件不存在
}

// EQ参数专用读写函数
#pragma stackfunction 1200
void lfs_read_eq_config(const char * file_path, unsigned char * buffer, unsigned size)
{
    lfs_file_open(&lfs, &file, file_path, LFS_O_RDONLY);
    lfs_file_read(&lfs, &file, buffer, size);
    lfs_file_close(&lfs, &file);
}

#pragma stackfunction 1500
void lfs_write_eq_config(const char * file_path, unsigned char * buffer, unsigned size)
{
    lfs_file_open(&lfs, &file, file_path, LFS_O_RDWR | LFS_O_CREAT);
    lfs_file_rewind(&lfs, &file);
    lfs_file_write(&lfs, &file, buffer, size);
    lfs_file_close(&lfs, &file);
}


// 删除文件
#pragma stackfunction 1500
int lfs_remove_file(const char * file_path)
{
    return lfs_remove(&lfs, file_path);
}

// 创建目录（通过创建临时文件然后删除）
#pragma stackfunction 1500
int lfs_create_directory(const char * dir_path)
{
    char temp_file[256];
    snprintf(temp_file, sizeof(temp_file), "%s/.dir", dir_path);

    lfs_file_t file;
    int result = lfs_file_open(&lfs, &file, temp_file, LFS_O_RDWR | LFS_O_CREAT);
    if (result == 0) {
        lfs_file_close(&lfs, &file);
        lfs_remove(&lfs, temp_file);
        return 0;  // 成功
    }
    return -1;  // 失败
}

#if 0
    // read current count
    uint32_t boot_count = 0;
    lfs_file_open(&lfs, &file, "boot_count", LFS_O_RDWR | LFS_O_CREAT);
    lfs_file_read(&lfs, &file, &boot_count, sizeof(boot_count));

    // update boot count
    boot_count += 1;
    lfs_file_rewind(&lfs, &file);
    for (int i = 0; i < 100; i++)
    lfs_file_write(&lfs, &file, &boot_count, sizeof(boot_count));

    // remember the storage is not updated until the file is closed successfully
    lfs_file_close(&lfs, &file);

    // release any resources we were using
    lfs_unmount(&lfs);

    // print the boot count
    printf("boot_count: %d\n", boot_count);

#endif
