#include "app_config.h"
#include "typedef.h"
#include "fs.h"
/* #include "norflash.h" */
/* #include "spi/nor_fs.h" */
#include "rec_nor/nor_interface.h"
#include "update_loader_download.h"
#include "update.h"
#include "ftl_api.h"

typedef struct _nand_update_info {
    u32 src_file_addr;
    u32 src_file_len;
} nand_update_info_parm;

static nand_cfg_parm nand_cfg;
static nand_update_info_parm nand_update_info;
static void *update_fp = NULL;

//=============================================================//
//                  NandFlash升级V2版本                     //
/* APP NandFlash 升级模型(APP_NANDFLASH_UFW_UPDATA)
  ________                    _________                    ________
 |        |                  |         |                  |        |
 |        |       (1)        |         |      (2)         |        |
 |   APP  | ----------->     |NandFlash| ------------>    |  CHIP  |
 |        | nand_update.ufw  |         | nand_update.ufw  |        |
 |________|                  |_________|                  |________|

NOTE:
 关于流程(1): 该流程为从远端获取升级文件(nand_update.ufw)写到外置flash某个位置, nand_update.ufw文件在外置flash中需要保证在物理上连续存储, 注意该流程为用户在自己定义的ota中完成, 不在本文件流程中, 需要用户自己开发;
 关于流程(2): 该流程为本文件实现的升级流程, 改动较少(主要改动为跟读取外置flash的硬件相关接口), 在用户完成流程(1)后(nand_update.ufw已存在外置flash), 可随时调用nandflash_update_ufw_init函数启动升级;

 */
//============================================================//

#define NANDFLASH_UFW_UPDATE_VERIFY_ALL_FILE 		1 //1: 进升级前校验用户写到外置flash的nand_update.ufw文件; 0: 不校验

extern const int support_nandflash_ufw_update_en;
extern u32 get_nand_flash_info(u8 *buf[]);
extern u32 ftl_get_nand_id();
extern void update_param_ext_fill(UPDATA_PARM *p, u8 ext_type, u8 *ext_data, u8 ext_len);

//=========================================================//
// nandflash 操作接口
//=========================================================//

static u32 user_remote_base_addr = 0; //
static u32 user_remote_cur_offset = 0;
static void *dev_ptr = NULL;
/*----------------------------------------------------------------------------*/
/**@brief  获取升级文件在外flash存放位置和长度
   @param  buf, 结构为nand_update_info
   @return void
   @note
*/
/*----------------------------------------------------------------------------*/
static void get_nand_update_info_param(void *buf)
{
    if (buf) {
        printf("src_file_addr = 0x%x", nand_update_info.src_file_addr); //外挂flash存放flash.bin文件物理地址
        printf("src_file_len = 0x%x", nand_update_info.src_file_len);   //外挂flash存放flash.bin文件物理地址
        memcpy(buf, (u8 *)&nand_update_info, sizeof(nand_update_info));
    }
}



static u32 user_remote_device_get_cur_addr(void)
{
    return (user_remote_base_addr + user_remote_cur_offset);
}
/*----------------------------------------------------------------------------*/
/**@brief   打开外置flash升级文件(初始化外置flash读接口)
   @param   void
   @return  0: 打开错误;
   		   非0: 打开成功
   @note
*/
/*----------------------------------------------------------------------------*/
static u16 nandflash_ufw_update_f_open(void)
{
    printf("%s", __func__);
    return 1;
}

/*----------------------------------------------------------------------------*/
/**@brief   读取外置flash升级文件内容
   @param   fp: NULL, 保留
   @param   buff: 读取数据buf
   @param   len: 读取数据长度
   @return  len: 读取成功
            (-1): 读取出错
   @note
*/
/*----------------------------------------------------------------------------*/
static u16 nandflash_ufw_update_f_read(void *fp, u8 *buff, u16 len)
{
    //TODO:
    /* int rlen = 0; */
    enum ftl_error_t error;
    /* if (dev_ptr) { */
    if (1) {
        wdt_clear();
        /* rlen = ftl_read_bytes(user_remote_device_get_cur_addr(), buff, len, &error); */
        ftl_read_bytes(user_remote_device_get_cur_addr(), buff, len, &error);
        //printf("%s: addr = 0x%x, rlen = %d, dev_ptr = 0x%x", __func__, user_remote_device_get_cur_addr(), rlen, dev_ptr);
        user_remote_cur_offset += len;
        //put_buf(buff, 128);
    }

    return len;
}

/*----------------------------------------------------------------------------*/
/**@brief   偏移升级文件地址
   @param   fp: NULL, 保留
   @param   type: SEEK_SET, SEEK_CUR, SEEK_END
   @param   offset: 偏移长度
   @return  0: 操作成功
            非0: 操作出错
   @note
*/
/*----------------------------------------------------------------------------*/
static int nandflash_ufw_update_f_seek(void *fp, u8 type, u32 offset)
{
    //TODO:
    //printf("%s", __func__);
    if (type == SEEK_SET) {
        user_remote_cur_offset = offset;
    } else {
        ASSERT(0, "ONLY SUPPORT SEEK_SET");
    }
    return 0;
}

/*----------------------------------------------------------------------------*/
/**@brief   关闭外置flash读操作
   @param   err: 传入升级状态
   @return  0: 操作成功
            非0: 操作出错
   @note
*/
/*----------------------------------------------------------------------------*/
static u16 nandflash_ufw_update_f_stop(u8 err)
{
    //TODO:
    printf("%s, err = 0x%x", __func__, err);

    return 0;
}

/*----------------------------------------------------------------------------*/
/**@brief   通知升级文件大小
   @param   priv: NULL, 保留
   @param   size: 待升级文件大小
   @return  0: 操作成功
            非0: 操作出错
   @note
*/
/*----------------------------------------------------------------------------*/
static int nandflash_ufw_update_notify_update_content_size(void *priv, u32 size)
{
    //TODO:
    printf("%s: size: 0x%x", __func__, size);

    return 0;
}


static const update_op_api_t nandflash_ufw_update_op_api = {
    .ch_init = NULL,
    .f_open = nandflash_ufw_update_f_open,
    .f_read = nandflash_ufw_update_f_read,
    .f_seek = nandflash_ufw_update_f_seek,
    .f_stop = nandflash_ufw_update_f_stop,
    .notify_update_content_size = nandflash_ufw_update_notify_update_content_size,
};

/*----------------------------------------------------------------------------*/
/**@brief   外置flash升级文件校验完成, 会调用该函数
   @param   priv: 回调传入参数
   @param   type: 升级模式
   @param   cmd: 1) UPDATE_LOADER_OK 外置flash升级文件校验成功, cpu复位, 启动内置flash固件升级流程
   				 2) UPDATE_LOADER_ERR 外置flash升级文件校验失败
   @return
   @note
*/
/*----------------------------------------------------------------------------*/
static void nandflash_ufw_update_end_callback(void *priv, int type, u8 cmd)
{
    //TODO:
    printf("%s: type: 0x%x, cmd = 0x%x", __func__, type, cmd);

    if (type == USER_NANDFLASH_UFW_UPDATA) {
        if (cmd == UPDATE_LOADER_OK) {
            printf("soft reset to update >>>");
            cpu_reset(); //复位让主控进入升级内置flash
        }
    }
}

/*----------------------------------------------------------------------------*/
/**@brief   填充升级结构体私有参数
   @param   p: 升级结构体指针(UPDATA_PARM)
   @return  void
   @note
*/
/*----------------------------------------------------------------------------*/
static void nandflash_ufw_update_private_param_fill(UPDATA_PARM *p)
{
    u8 *nand_flash = NULL;
    u32 param_len = 0;
    get_nand_update_info_param(p->parm_priv);
    param_len = get_nand_flash_info(&nand_flash);
#if (2 == TCFG_NANDFLASH_FTL_VERSION)
    if (nand_cfg.data) {
#else
    if (0) {
#endif
        update_param_ext_fill(p, EXT_NAND_PARM, nand_cfg.data, nand_cfg.data_len);
    } else if (param_len && param_len <= sizeof(p->nand_param)) {
        memcpy(p->nand_param, nand_flash, param_len);
    } else {
        printf("nandflash param err\n");
        update_param_ext_fill(p, EXT_NAND_PARM, nand_flash, param_len);
    }
}

/*----------------------------------------------------------------------------*/
/**@brief   固件升级校验流程完成, cpu reset跳转升级新的固件
   @param   type: 升级类型
   @return  void
   @note
*/
/*----------------------------------------------------------------------------*/
static void nandflash_ufw_update_before_jump_handle(int type)
{
    printf("soft reset to update >>>");
    cpu_reset(); //复位让主控进入升级内置flash
}

/*----------------------------------------------------------------------------*/
/**@brief   外置nandflash升级流程状态处理
   @param   type: 升级类型
   @param   state: 当前升级状态
   @param   priv: 跟状态相关的私有参数指针
   @return  void
   @note
*/
/*----------------------------------------------------------------------------*/
static void nandflash_ufw_update_state_cbk(int type, u32 state, void *priv)
{
    update_ret_code_t *ret_code = (update_ret_code_t *)priv;

    if (ret_code) {
        printf("state:%x err:%x\n", ret_code->stu, ret_code->err_code);
    }

    switch (state) {
    case UPDATE_CH_EXIT:
        if ((0 == ret_code->stu) && (0 == ret_code->err_code)) {
            //update_mode_api(BT_UPDATA);
            update_mode_api_v2(USER_NANDFLASH_UFW_UPDATA,
                               nandflash_ufw_update_private_param_fill,
                               nandflash_ufw_update_before_jump_handle);
        } else {
#if (2 == TCFG_NANDFLASH_FTL_VERSION)
            if (nand_cfg.data) {
                free(nand_cfg.data);
                nand_cfg.data = NULL;
                memset(&nand_cfg, 0, sizeof(nand_cfg_parm));
            }
#endif
        }

        break;
#if (2 == TCFG_NANDFLASH_FTL_VERSION)
    case UPDATE_CH_INIT:
        if (nand_cfg.data) {
            free(nand_cfg.data);
            nand_cfg.data = NULL;
            memset(&nand_cfg, 0, sizeof(nand_cfg_parm));
        }
        break;
    case UPDATE_CH_NAND_INFO:
        nand_cfg.id = ftl_get_nand_id();
        nand_cfg_parm **nand_cfg_p = (nand_cfg_parm **) priv;
        *nand_cfg_p = &nand_cfg;
        break;
#endif
    default:
        break;
    }
}

/*----------------------------------------------------------------------------*/
/**@brief  通过地址读取升级文件内容
   @param  buf, 读取数据缓存
   @param  addr, 读取外置flash数据的地址
   @param  len, 读取外置flash数据的长度
   @return u16 读取长度
   @note
*/
/*----------------------------------------------------------------------------*/
static u16 nandflash_update_file_read(void *buf, u32 addr, u32 len)
{
    int rlen = 0;
    enum ftl_error_t error;

    /* if (dev_ptr) { */
    if (1) {
        wdt_clear();
        /* printf("%s: addr = 0x%x, rlen = 0x%x", __func__, addr, len); */
        rlen = ftl_read_bytes(addr, buf, len, &error);
        /* put_buf(buf, 128); */
    }

    return rlen;
}

/*----------------------------------------------------------------------------*/
/**@brief  启动外置flash升级接口, 该接口为用户在保证将nand_update.ufw烧写到外置flash之后, 调用该接口进行内置flash升级
   @param  nandflash_addr: 升级文件在外置flash的物理地址
   @return  void
   @note
*/
/*----------------------------------------------------------------------------*/
//使用ufw文件格式
void nandflash_update_ufw_init(u32 nandflash_addr)
{
    if (support_nandflash_ufw_update_en == 0) {
        return;
    }
    user_remote_base_addr = nandflash_addr;
    nand_update_info.src_file_addr = nandflash_addr;
#if NANDFLASH_UFW_UPDATE_VERIFY_ALL_FILE
    if (nandflash_ufw_update_f_open()) {
        if (update_file_verify(nandflash_addr, nandflash_update_file_read) != 0) {
            //校验失败
            return;
        }
    }
#endif /* #if NANDFLASH_UFW_UPDATE_VERIFY_ALL_FILE */

    update_mode_info_t nandflash_ufw_update_info = {
        .type = USER_NANDFLASH_UFW_UPDATA,
        .state_cbk = nandflash_ufw_update_state_cbk,
        .p_op_api = &nandflash_ufw_update_op_api,
        .task_en = 1,
    };

    app_active_update_task_init(&nandflash_ufw_update_info);
}

