/************************************************************************************************
 *                                          Include                                              *
 ************************************************************************************************/
#include "iap.h"
#include "can_id_deal.h"
#include "flash.h"
#include "queue.h"
#include <stdint.h>
#include <string.h>

/************************************************************************************************
 *                                          Config                                                *
 ************************************************************************************************/
extern uint8_t jump_flag;
uint8_t        can_rx_flag;
// extern can_receive_message receive_iap_message;
static uint16_t jump_boot_time = 0;
uint8_t         send_buff[8]   = {0, 0, 0, 0, 0, 0, 0, 0};
unsigned char   recv_crc[2];
unsigned char   recvdata_ema[204];
unsigned char   recvdata_buffer[210];

#define SW_VER 0x2446

#define SW_VER_HI_ASCII ((('0' + ((uint8_t)((SW_VER >> 8) & 0x000F))) << 8) | \
                         ('0' + ((uint8_t)((SW_VER >> 12) & 0x000F))))

#define SW_VER_L0_ASCII ((('0' + ((uint8_t)(SW_VER & 0x000F))) << 8) | \
                         ('0' + ((uint8_t)((SW_VER >> 4) & 0x000F))))

#define PACK_TYPE       1
#define PACK_TYPE_ASCII ((('0' + PACK_TYPE) << 8) | ('0'))

uint16_t firm_ver[UPGRADE_APP_DATA_SIZE]       = {IAP_CMD_JUMP_OVER, SW_VER_HI_ASCII, SW_VER_L0_ASCII, 0xFFFF};
uint16_t firm_ver_buff[UPGRADE_BUFF_DATA_SIZE] = {0};
uint8_t  iap_disconnect_count                  = 0;
/************************************************************************************************
 *                                          Data structs                                         *
 ************************************************************************************************/
iap_type      g_iap;
upgrade_param upgrade;
/************************************************************************************************
 *                                          implements                                           *
 ************************************************************************************************/
void invert_uint8()
{
}

void iap_connect_clear(void)
{
    iap_disconnect_count = 0;
    send_buff[0]         = 0x00;
    send_buff[1]         = 0x00;
    send_buff[2]         = 0x00;
    send_buff[3]         = 0x00;
    send_buff[4]         = 0x00;
    send_buff[5]         = 0x00;
    send_buff[6]         = 0x00;
    send_buff[7]         = 0x00;
}

void iap_disconnect_check(void)
{
    if (++iap_disconnect_count >= 5)
    {
        iap_disconnect_count = 5;
    }
}

// iap相关参数初始化
void iap_param_init(void)
{
    g_iap.recv_len         = 0;
    g_iap.total_packages   = 0;
    g_iap.current_packages = 0;
    g_iap.crc16            = 0xFFFF;
    g_iap.total_bytes      = 0;
    g_iap.count_bytes      = 0;
    g_iap.count_packages   = 0;
    g_iap.firmware_crc16   = 0;
    g_iap.firmware_ver     = 0;
}

// 清除升级信息标志位
void set_firmver(void)
{
    fmc_clear_flag_star();
    fmc_read_n_half_word(UPGRADE_FLAG_START_ADDR, firm_ver_buff, UPGRADE_BUFF_DATA_SIZE);
    fmc_erase_pages(UPGRADE_FLAG_START_ADDR, 1);

    for (uint8_t i = 0; i < UPGRADE_APP_DATA_SIZE; i++)
    {
        firm_ver_buff[i + UPGRADE_FLAG_DATA_SIZE] = firm_ver[i];
    }
    fmc_write_n_half_word(UPGRADE_FLAG_START_ADDR, firm_ver_buff, UPGRADE_BUFF_DATA_SIZE);
    fmc_clear_flag_end();
}

void iap_flag_release(void)
{
    fmc_read_n_half_word(UPGRADE_FLAG_START_ADDR, upgrade.infor_buf, UPGRADE_FLAG_DATA_SIZE);

    if (upgrade.param.sign == UPGRADE_SIGN)
    {
        fmc_clear_flag_star();
        fmc_erase_pages(UPGRADE_FLAG_START_ADDR, 1);
        fmc_write_n_half_word(UPGRADE_FLAG_START_ADDR + (UPGRADE_FLAG_DATA_SIZE * 2), firm_ver, UPGRADE_APP_DATA_SIZE);
        fmc_clear_flag_end();
        for (uint8_t i = 0; i < (UPGRADE_APP_DATA_SIZE * 2); i++)
        {
            send_buff[i] = fmc_read_byte(UPGRADE_FLAG_START_ADDR + (UPGRADE_FLAG_DATA_SIZE * 2) + i);
        }
        can_msg_tx(0x18000001, CAN_Id_Extended, send_buff, 8);
        CAN_ITConfig(CAN1, CAN_IT_TME, ENABLE);
    }
}

uint8_t erase_app_flash(uint32_t start_address, uint32_t end_addr)
{
    uint8_t  status   = 0;
    uint16_t page_len = 0;
    // page_len          = (end_addr - start_address) / FMC_PAGE_SIZE;
    if ((start_address == ADDR_FLASH_SECTOR_2))
    {
        status = fmc_erase_pages(start_address, 5);
    }
    else if ((start_address == ADDR_FLASH_SECTOR_8))
    {
        status = fmc_erase_pages(start_address, 3);
    }
    else
    {
        status = 1;
    }

    return status;
}

void jump_boot_time_ctrl(void)
{
    if (jump_boot_time > 0)
    {
        jump_boot_time--;
        if (jump_boot_time == 0)
        {
            // jump_flag = 1;
            __set_FAULTMASK(1); //  关闭所有中断
            NVIC_SystemReset(); //  复位
        }
    }
}

void write_app_flash(uint16_t *buf, uint16_t pack_len, uint16_t pack_num)
{
    static uint32_t w_start_addr = 0;

    w_start_addr = pack_num * 6 + APP_START_ADDR;
    fmc_write_n_half_word(w_start_addr, buf, pack_len);
}

void read_app_flash(uint16_t *buf, uint16_t pack_len, uint16_t pack_num)
{
    static uint32_t w_start_addr = 0;

    w_start_addr = pack_num * 6 + APP_START_ADDR;
    fmc_read_n_half_word(w_start_addr, buf, pack_len);
}

uint8_t write_download_flash(uint16_t *buf, uint16_t pack_len, uint16_t pack_num)
{
    uint8_t         status       = 0;
    static uint32_t w_start_addr = 0;

    w_start_addr = pack_num * 6 + DOWNLOAD_START_ADDR;
    status       = fmc_write_n_half_word(w_start_addr, buf, pack_len);
    return status;
}

void read_download_flash(uint16_t *buf, uint16_t pack_len, uint16_t pack_num)
{
    static uint32_t w_start_addr = 0;

    w_start_addr = pack_num * 6 + DOWNLOAD_START_ADDR;
    fmc_read_n_half_word(w_start_addr, buf, pack_len);
}

void iap_rec_handler(pdu_tag rec_msg)
{
    uint8_t erase_flag = 0x01;

    g_can_ota_id = rec_msg.id.r;
    if (rec_msg.data.u8_buf[0] != IAP_CMD_VERSION)
        iap_connect_clear();

    if (rec_msg.data.u8_buf[0] >> 7)
    {
        if (rec_msg.id.b.sa == 0xE1)
        {
            g_iap.recv_len         = rec_msg.reg.dlc - 2;
            g_iap.current_packages = ((uint16_t)(rec_msg.data.u8_buf[0] & 0x7F) << 8) | rec_msg.data.u8_buf[1];
            memcpy(g_iap.flash_data.u8_buf, &rec_msg.data.u8_buf[2], g_iap.recv_len);

            if ((g_iap.count_packages == g_iap.current_packages) && (g_iap.current_packages < g_iap.total_packages))
            {
                g_iap.count_bytes += g_iap.recv_len;

                if ((g_iap.recv_len % 2) != 0)
                {
                    g_iap.flash_data.u8_buf[g_iap.recv_len] = 0xFF;
                    g_iap.recv_len++;
                }

                fmc_clear_flag_star();
                erase_flag = write_download_flash(g_iap.flash_data.u16_buf, g_iap.recv_len / 2, g_iap.count_packages);
                fmc_clear_flag_end();

                g_iap.crc16 = firmware_crc16_ccitt_false(g_iap.crc16, g_iap.flash_data.u8_buf, g_iap.recv_len);

                send_buff[0] = IAP_CMD_DATA;
                send_buff[1] = !erase_flag;
                g_iap.count_packages++;
            }
            else
            {
                send_buff[0] = IAP_CMD_DATA;
                send_buff[1] = 0x06;
            }

            send_buff[2] = rec_msg.data.u8_buf[0] & 0x7F;
            send_buff[3] = rec_msg.data.u8_buf[1];

            send_buff[4] = g_iap.count_packages >> 8;
            send_buff[5] = g_iap.count_packages & 0xFF;
        }
    }
    else
    {
        switch (rec_msg.data.u8_buf[0])
        {
        case IAP_CMD_VERSION:
            iap_param_init();
            for (uint8_t i = 0; i < (UPGRADE_APP_DATA_SIZE * 2); i++)
            {
                send_buff[i] = fmc_read_byte(UPGRADE_FLAG_START_ADDR + (UPGRADE_FLAG_DATA_SIZE * 2) + i);
            }

            send_buff[0] = IAP_CMD_VERSION;
            send_buff[1] = 0x01;
            break;
        case IAP_CMD_INFO:
            g_iap.total_bytes = (uint16_t)rec_msg.data.u8_buf[1] << 8 | rec_msg.data.u8_buf[2];
            g_iap.total_bytes <<= 16;
            g_iap.total_bytes |= (uint16_t)rec_msg.data.u8_buf[3] << 8 | rec_msg.data.u8_buf[4];
            g_iap.total_packages = (uint16_t)rec_msg.data.u8_buf[5] << 8 | rec_msg.data.u8_buf[6];
            send_buff[0]         = IAP_CMD_INFO;
            send_buff[1]         = 0x01;
            break;
        case IAP_CMD_ERASE:
            g_iap.firmware_crc16 = (uint16_t)rec_msg.data.u8_buf[2] << 8 | rec_msg.data.u8_buf[3];
            if (rec_msg.data.u8_buf[1])
            {
                // 喂狗
                fmc_clear_flag_star();
                erase_flag = erase_app_flash(DOWNLOAD_START_ADDR, DOWNLOAD_END_ADDR);
                fmc_clear_flag_end();
                // 喂狗
                g_iap.count_bytes    = 0;
                g_iap.count_packages = 0;
            }
            send_buff[0] = IAP_CMD_ERASE;
            send_buff[1] = !erase_flag;
            break;
        case IAP_CMD_CRC:
            send_buff[0] = IAP_CMD_CRC;
            send_buff[1] = (g_iap.crc16 == g_iap.firmware_crc16 ? 1 : 0);
            break;
        case IAP_CMD_JUMP:
            if (g_iap.crc16 != g_iap.firmware_crc16)
            {
                send_buff[1] = 0x00;
            }
            else
            {
                fmc_clear_flag_star();
                fmc_erase_pages(UPGRADE_FLAG_START_ADDR, 1);
                upgrade.param.sign        = UPGRADE_SIGN;
                upgrade.param.crc16       = g_iap.crc16;
                upgrade.param.moving_size = g_iap.total_bytes;
                fmc_write_n_half_word(UPGRADE_FLAG_START_ADDR, upgrade.infor_buf, UPGRADE_FLAG_DATA_SIZE);
                fmc_clear_flag_end();
                jump_boot_time = 10;
                send_buff[1]   = 0x01;
            }
            send_buff[0] = IAP_CMD_JUMP;
            break;
        case IAP_CMD_MODEL_TYPE:
            send_buff[0] = IAP_CMD_MODEL_TYPE;
            send_buff[1] = 0x01;

            send_buff[2] = DEVICE_ID >> 8;
            send_buff[3] = DEVICE_ID & 0xFF;
        }
    }

    push_can_message_to_queue(get_ota_id(CAN_IAP), 8, send_buff);
}