#include "drv_misc.h"

#include "bsp_boot.h"
#include "bsp_head.h"
#include "bsp_flash.h"

#include "kit_data.h"
#include "kit_time.h"
#include "kit_debug.h"




/********************************************************************************
*具备强刷功能，时间可配
*检验固件大小
*连续n次复位异常，停留在boot中
*升级模式3中：1.不升级，直接跳到应用程序 
              2.app升级，不写备份区，直接写应用区
              3.OTA模式需要写备份区
*********************************************************************************/

/***********备份区起始开辟32字节空间**********
    +-------+-------+----------+----------+--------+
    | head1 | head2 | 固件长度 | 升级时间 |  预留  |
    |  4B   |  4B   |    4B    |    4B    |  16B   |
    +-------+-------+----------+----------+--------+
**********************************************/
#define APP_CFG_BOOT_FW_LENGTH_OFFSET           8u
#define APP_CFG_BOOT_FW_UPDATE_TIME_OFFSET      12u
#define APP_CFG_BOOT_FW_DATA_OFFSET             32u

static kit_ret_e boot_write_data(BootItem *item);

static kit_ret_e boot_write_data(BootItem *item)
{
    uint8_t  tmp;
    uint16_t data_len;
    uint32_t start_addr = 0;
	kit_ret_e res = kKit_Ret_Ok;	
	
    //校验不过重新请求改包，不算错误
	if((item != NULL) && (kit_check_crc16(item->buf_addr, item->buf_len) == 0))
    {
        //校验第一包数据的产品编号和地址
        if(item->pack_idx == 0)
        {
            if((*((uint32_t *)item->buf_addr) != item->app_start_addr)
            || (*((uint16_t *)&item->buf_addr[2 + 4 + item->product_num_offset]) != item->product_num))
            {
                return kKit_Ret_Error;
            }            
        }
        data_len = item->buf_len - 4 - 2 - 2; // 减去地址 数据长度 CRC
        //数据先写入备份区
        start_addr = item->back_up_addr + APP_CFG_BOOT_FW_DATA_OFFSET + item->rcv_firmware_len;
        
        item->rcv_firmware_len += data_len;
        //每一包数据必须4字节对齐,TODO必须是最后一包
        tmp = data_len % 4;
        if(tmp != 0)
        {
            data_len += (4 - tmp);
        }
        
        res = bsp_flash_write_data_u32(item->flash_item, start_addr, item->buf_addr + 6, data_len);
        //校验写入flash数据是否正确
        if((res == kKit_Ret_Ok) && (kit_is_buf_same((void *)start_addr, (void *)(item->buf_addr + 6), data_len) == false))
        {
            //如果写入失败,擦除从新开始升级
            res = bsp_flash_erase_page(item->flash_item, item->back_up_addr, item->firmware_len + APP_CFG_BOOT_FW_DATA_OFFSET);
            item->pack_idx = 0;
            
        }
        else
        {
            item->pack_idx++;
        }
    }
    
    return res;
}

uint16_t bsp_boot_get_pack_idx(BootItem *item)
{
    uint16_t res = 0;
    if(item != NULL)
    {
        res = item->pack_idx;
    }
    return res;
}

void bsp_boot_set_cmd(BootItem *item, BootCmd cmd, uint8_t* buf, uint16_t len)
{
	KIT_ASSERT_PARAM(cmd < kBootCmd_End);
    if((item != NULL) && (cmd < kBootCmd_End))
	{
		item->cmd = cmd;

        if(item->cmd == kBootCmd_Erase)
        {
            item->firmware_len = READ_LT_INT32U_BY_CONST_POS(buf, 0);
        }
        else if(item->cmd == kBootCmd_SndFrame)
        {
            item->buf_len = len;
            item->buf_addr = buf;
        }
        else if(item->cmd == kBootCmd_UpdateRes)
        {
            item->update_time = READ_LT_INT32U_BY_CONST_POS(buf, 0);
        }
	}	
}


void bsp_boot_poll(BootItem *item)
{
	kit_ret_e res;

    if((item != NULL) && (item->ans_call != NULL))
	{
        if(item->cmd == kBootCmd_Reboot)
        {
            item->buf_len = 0;
            item->pack_idx = 0;
            item->is_token = false;
            item->rcv_firmware_len = 0;
            item->cmd = kBootCmd_None;

            item->step = kBootStep_WaitErase;
            item->ans_call(item, kBootCmd_Reboot, kKit_Ret_Ok);
        }
        else if(item->cmd == kBootCmd_Quit)
        {
            item->step = kBootStep_None;
            item->ans_call(item, kBootCmd_Quit, kKit_Ret_Ok);
        }
        else if(item->cmd == kBootCmd_Token)
        {
            item->is_token = true;
            item->ans_call(item, kBootCmd_Token, kKit_Ret_Ok);
        } 

        switch (item->step)
        {
        case kBootStep_None:
                
                break;
        case kBootStep_WaitErase:
            if(item->cmd == kBootCmd_Erase)
            {
                res = bsp_flash_erase_page(item->flash_item, item->back_up_addr, item->firmware_len + APP_CFG_BOOT_FW_DATA_OFFSET);
                if (res == kKit_Ret_Ok)
                {
                    item->step = kBootStep_RvdData;
                }
                if(item->is_token == true)
                {
                    item->ans_call(item, kBootCmd_Erase, res);
                }
            }
            break;
        case kBootStep_RvdData:
            if(item->cmd == kBootCmd_SndFrame)
            {		    
                res = boot_write_data(item);
                if(item->is_token == true)
                {
                    kit_time_dly_ms(10);
                    item->ans_call(item, kBootCmd_SndFrame, res);
                }
            }
            else if(item->cmd == kBootCmd_UpdateRes)
            {
                 if((item->rcv_firmware_len == item->firmware_len) 
                && (kit_check_crc16((uint8_t *)(item->back_up_addr + APP_CFG_BOOT_FW_DATA_OFFSET), item->rcv_firmware_len) == 0)) 
                {
                    res = kKit_Ret_Ok;
                    drv_flash_write_u32(item->flash_item, item->back_up_addr + APP_CFG_BOOT_FW_LENGTH_OFFSET, item->rcv_firmware_len);
                    drv_flash_write_u32(item->flash_item, item->back_up_addr + APP_CFG_BOOT_FW_UPDATE_TIME_OFFSET, item->update_time);
                    bsp_head_set_status(item->flash_item, item->back_up_addr, kHeadStatus_Start);
                    item->step = kBootStep_UpdateEnd;
                }
                else
                {
                    res = kKit_Ret_CheckErr;
                }
                kit_time_dly_ms(10);
                item->ans_call(item, kBootCmd_UpdateRes, res);

            }
            break;
            
        case kBootStep_UpdateEnd:
            if(item->cmd == kBootCmd_UpdateEnd)
            {
                //必须复位，直接跳转后使用的堆栈是PSP，导致软件复位
                drv_misc_reset_mcu();
            }
            break;

        default:
            break;
        }        
        item->cmd = kBootCmd_None;
    }
}


//固件长度> BOOT_SPACE_SIZE - 16 异常
//固件长度为0xFFFFFFFF表示未下载完成断电，无需搬运直接跳到应用程序
//固件长度不为0xFFFFFFFF，表示固件已经下载到备份区，可能未搬运、可能搬运未完成
bool bsp_boot_move_firmware(BootItem *item, uint32_t start_addr, uint32_t max_len)
{
    bool res = false;
    uint16_t crc;
    uint32_t tmp, data = 0xFFFFFFFF;

    if(item != NULL)
	{
        drv_flash_read_u32(item->flash_item, item->back_up_addr + APP_CFG_BOOT_FW_LENGTH_OFFSET, &data);   
        KIT_ASSERT_PARAM(data < (max_len - APP_CFG_BOOT_FW_DATA_OFFSET));
        KIT_DEBUG_PRINTF("GUIDE:need update\r\n");
        if((data < (max_len - APP_CFG_BOOT_FW_DATA_OFFSET)) && (kit_check_crc16((uint8_t *)(item->back_up_addr + APP_CFG_BOOT_FW_DATA_OFFSET), data) == 0))
        {
            bsp_flash_erase_page(item->flash_item, start_addr, data);
            tmp = data;
            if(data % 4 != 0)
            {
                tmp += 4 - (data % 4);
            }
            bsp_flash_write_data_u32(item->flash_item, start_addr, (uint8_t *)(item->back_up_addr + APP_CFG_BOOT_FW_DATA_OFFSET), tmp);
            
            crc = kit_check_crc16((uint8_t *)start_addr, data);
            KIT_ASSERT_PARAM(crc == 0);
            //todo
            if(crc == 0)
            {
                bsp_head_set_status(item->flash_item, item->back_up_addr, kHeadStatus_Finsih);
                res = true;
            }
            else
            {
                KIT_DEBUG_PRINTF("GUIDE:crc err\r\n");
            }
        }
    }
    return res;
}    

uint32_t bsp_boot_get_update_time(BootItem *item)
{
    uint32_t tmp = 0;

    if((drv_flash_read_u32(item->flash_item, item->back_up_addr + APP_CFG_BOOT_FW_UPDATE_TIME_OFFSET, &tmp) != kKit_Ret_Ok) 
    || (tmp == 0xFFFFFFFF))
    {
        tmp = 0;
    }
    return tmp;
}
