/*
 * 自然写智能点阵笔嵌入式固件软件 V1.0
 * offline_storage.c - 离线Flash缓存存储
 *
 * 功能说明：
 * 1. 在BLE断连时将笔迹数据缓存到外部SPI Flash
 * 2. BLE重新连接后自动回传缓存数据
 * 3. 环形缓冲区管理Flash存储空间
 * 4. 掉电安全的写入机制（写入前擦除校验）
 * 5. 存储使用统计与容量告警
 */

#include <stdint.h>
#include <stdbool.h>
#include <string.h>

#include "hal_flash.h"

/* ========== Flash存储参数 ========== */

/* 外部SPI Flash总容量（4MB） */
#define FLASH_TOTAL_SIZE        (4 * 1024 * 1024)

/* Flash扇区大小（4KB，最小擦除单元） */
#define FLASH_SECTOR_SIZE       4096

/* Flash页大小（256字节，最小写入单元） */
#define FLASH_PAGE_SIZE         256

/* 离线存储起始地址（前64KB保留给系统配置） */
#define STORAGE_START_ADDR      (64 * 1024)

/* 离线存储可用大小 */
#define STORAGE_AVAILABLE       (FLASH_TOTAL_SIZE - STORAGE_START_ADDR)

/* 可用扇区数量 */
#define STORAGE_SECTOR_COUNT    (STORAGE_AVAILABLE / FLASH_SECTOR_SIZE)

/* 每条笔迹记录大小（固定长度，便于管理） */
#define RECORD_SIZE             16

/* 每个扇区能存储的记录数 */
#define RECORDS_PER_SECTOR      (FLASH_SECTOR_SIZE / RECORD_SIZE)

/* 记录头标识 */
#define RECORD_MAGIC            0xAB

/* ========== 数据结构 ========== */

/* 存储记录结构（16字节固定长度） */
typedef struct __attribute__((packed)) {
    uint8_t  magic;             /* 记录标识 0xAB */
    uint8_t  record_type;       /* 记录类型：0=坐标, 1=笔落下, 2=笔抬起 */
    uint32_t x;                 /* X坐标 */
    uint32_t y;                 /* Y坐标 */
    uint16_t pressure;          /* 压力值 */
    uint16_t timestamp_offset;  /* 时间偏移（相对于session开始） */
    uint8_t  checksum;          /* 校验和 */
} StorageRecord;

/* 存储管理状态 */
typedef struct {
    uint32_t write_sector;          /* 当前写入扇区索引 */
    uint16_t write_offset;          /* 当前扇区内写入偏移 */
    uint32_t read_sector;           /* 当前读出扇区索引 */
    uint16_t read_offset;           /* 当前扇区内读出偏移 */
    uint32_t total_records;         /* 缓存的总记录数 */
    uint32_t session_start_time;    /* 当前存储会话开始时间 */
    bool     is_full;               /* 存储是否已满 */
} StorageState;

/* ========== 静态变量 ========== */

/* 存储管理状态 */
static StorageState s_state;

/* 写入页缓冲区（攒满一页再写入Flash） */
static uint8_t s_page_buffer[FLASH_PAGE_SIZE];
static uint16_t s_page_buffer_offset = 0;

/* ========== 初始化 ========== */

/**
 * 初始化离线存储模块
 * 扫描Flash查找上次的写入位置（掉电恢复）
 */
void offline_storage_init(void) {
    memset(&s_state, 0, sizeof(s_state));
    memset(s_page_buffer, 0xFF, sizeof(s_page_buffer));
    s_page_buffer_offset = 0;

    /* 扫描Flash查找最后写入位置 */
    scan_storage_state();
}

/**
 * 扫描Flash存储区，恢复写入/读出位置
 * 通过检查每个扇区的第一个字节来判断是否已写入
 */
static void scan_storage_state(void) {
    uint32_t sector;
    uint8_t header;

    s_state.write_sector = 0;
    s_state.total_records = 0;

    for (sector = 0; sector < STORAGE_SECTOR_COUNT; sector++) {
        uint32_t addr = STORAGE_START_ADDR + sector * FLASH_SECTOR_SIZE;
        hal_flash_read(addr, &header, 1);

        if (header == 0xFF) {
            /* 空扇区，写入位置在此 */
            s_state.write_sector = sector;
            break;
        } else if (header == RECORD_MAGIC) {
            /* 已写入的扇区，继续扫描 */
            /* 统计有效记录数 */
            uint16_t offset;
            for (offset = 0; offset < FLASH_SECTOR_SIZE; offset += RECORD_SIZE) {
                uint8_t magic;
                hal_flash_read(addr + offset, &magic, 1);
                if (magic == RECORD_MAGIC) {
                    s_state.total_records++;
                } else {
                    break;
                }
            }
        }
    }

    /* 读出位置从最早的数据扇区开始 */
    s_state.read_sector = 0;
    s_state.read_offset = 0;
}

/* ========== 校验和计算 ========== */

/**
 * 计算记录校验和（简单异或校验）
 */
static uint8_t calculate_checksum(const StorageRecord *record) {
    const uint8_t *data = (const uint8_t *)record;
    uint8_t sum = 0;
    uint8_t i;

    /* 对除checksum字段外的所有字节异或 */
    for (i = 0; i < sizeof(StorageRecord) - 1; i++) {
        sum ^= data[i];
    }

    return sum;
}

/**
 * 验证记录校验和
 */
static bool verify_checksum(const StorageRecord *record) {
    return calculate_checksum(record) == record->checksum;
}

/* ========== 写入操作 ========== */

/**
 * 将一条笔迹记录写入离线缓存
 *
 * @param type       记录类型（0=坐标, 1=笔落下, 2=笔抬起）
 * @param x          X坐标
 * @param y          Y坐标
 * @param pressure   压力值
 * @param timestamp  时间戳
 * @return 0成功, -1存储已满, -2写入失败
 */
int offline_storage_write(uint8_t type, uint32_t x, uint32_t y,
                           uint16_t pressure, uint32_t timestamp) {
    if (s_state.is_full) {
        return -1;
    }

    /* 构建记录 */
    StorageRecord record;
    record.magic = RECORD_MAGIC;
    record.record_type = type;
    record.x = x;
    record.y = y;
    record.pressure = pressure;
    record.timestamp_offset = (uint16_t)(timestamp - s_state.session_start_time);
    record.checksum = calculate_checksum(&record);

    /* 将记录复制到页缓冲区 */
    memcpy(&s_page_buffer[s_page_buffer_offset], &record, RECORD_SIZE);
    s_page_buffer_offset += RECORD_SIZE;

    /* 页缓冲区满，写入Flash */
    if (s_page_buffer_offset >= FLASH_PAGE_SIZE) {
        int ret = flush_page_buffer();
        if (ret != 0) {
            return -2;
        }
    }

    s_state.total_records++;
    return 0;
}

/**
 * 将页缓冲区内容写入Flash
 * 写入前检查目标扇区是否需要擦除
 */
static int flush_page_buffer(void) {
    uint32_t sector_addr = STORAGE_START_ADDR
                           + s_state.write_sector * FLASH_SECTOR_SIZE;
    uint32_t page_addr = sector_addr + s_state.write_offset;

    /* 如果是扇区的起始位置，先擦除扇区 */
    if (s_state.write_offset == 0) {
        hal_flash_erase_sector(sector_addr);
    }

    /* 写入一页数据 */
    hal_flash_write(page_addr, s_page_buffer, FLASH_PAGE_SIZE);

    /* 读回验证（写入校验） */
    uint8_t verify_buf[FLASH_PAGE_SIZE];
    hal_flash_read(page_addr, verify_buf, FLASH_PAGE_SIZE);

    if (memcmp(s_page_buffer, verify_buf, FLASH_PAGE_SIZE) != 0) {
        /* 写入验证失败 */
        return -1;
    }

    /* 更新写入位置 */
    s_state.write_offset += FLASH_PAGE_SIZE;
    if (s_state.write_offset >= FLASH_SECTOR_SIZE) {
        s_state.write_offset = 0;
        s_state.write_sector++;

        if (s_state.write_sector >= STORAGE_SECTOR_COUNT) {
            /* 回绕到起始位置（环形缓冲） */
            s_state.write_sector = 0;
            s_state.is_full = true;
        }
    }

    /* 清空页缓冲区 */
    memset(s_page_buffer, 0xFF, sizeof(s_page_buffer));
    s_page_buffer_offset = 0;

    return 0;
}

/* ========== 读取操作 ========== */

/**
 * 从离线缓存读取一条记录
 *
 * @param record  输出记录指针
 * @return 0成功并返回记录, 1无更多数据, -1读取错误
 */
int offline_storage_read(StorageRecord *record) {
    if (s_state.total_records == 0) {
        return 1;
    }

    uint32_t addr = STORAGE_START_ADDR
                    + s_state.read_sector * FLASH_SECTOR_SIZE
                    + s_state.read_offset;

    /* 从Flash读取记录 */
    hal_flash_read(addr, (uint8_t *)record, RECORD_SIZE);

    /* 验证记录有效性 */
    if (record->magic != RECORD_MAGIC) {
        return 1;   /* 无更多有效数据 */
    }

    if (!verify_checksum(record)) {
        /* 校验和错误，跳过损坏的记录 */
        s_state.read_offset += RECORD_SIZE;
        return -1;
    }

    /* 更新读出位置 */
    s_state.read_offset += RECORD_SIZE;
    if (s_state.read_offset >= FLASH_SECTOR_SIZE) {
        s_state.read_offset = 0;
        s_state.read_sector++;
        if (s_state.read_sector >= STORAGE_SECTOR_COUNT) {
            s_state.read_sector = 0;
        }
    }

    s_state.total_records--;
    return 0;
}

/* ========== 缓冲区刷新 ========== */

/**
 * 强制将页缓冲区中的数据写入Flash
 * 在进入深度睡眠前调用，确保数据不丢失
 */
void offline_storage_flush(void) {
    if (s_page_buffer_offset > 0) {
        flush_page_buffer();
    }
}

/* ========== 存储状态查询 ========== */

/**
 * 获取缓存的记录数量
 */
uint32_t offline_storage_get_count(void) {
    return s_state.total_records;
}

/**
 * 获取存储使用百分比
 */
uint8_t offline_storage_get_usage_percent(void) {
    uint32_t max_records = STORAGE_SECTOR_COUNT * RECORDS_PER_SECTOR;
    if (max_records == 0) return 0;
    return (uint8_t)((uint64_t)s_state.total_records * 100 / max_records);
}

/**
 * 清空所有离线缓存数据
 * 通过批量擦除Flash实现
 */
void offline_storage_clear(void) {
    uint32_t sector;
    for (sector = 0; sector < STORAGE_SECTOR_COUNT; sector++) {
        uint32_t addr = STORAGE_START_ADDR + sector * FLASH_SECTOR_SIZE;
        hal_flash_erase_sector(addr);
    }

    /* 重置管理状态 */
    memset(&s_state, 0, sizeof(s_state));
    memset(s_page_buffer, 0xFF, sizeof(s_page_buffer));
    s_page_buffer_offset = 0;
}

/**
 * 开始新的离线存储会话
 * @param start_time  会话开始时间戳
 */
void offline_storage_start_session(uint32_t start_time) {
    s_state.session_start_time = start_time;
}
