#include "iap_upgrade.h"
#include "Callback.h"
#include "cmsis_os.h"	//osDelay
#include "uType.h"

//BootParams g_bootParams;
JsonDat_Root JsRoot;

static uint32_t upgrade_offset = 0;
uint16_t upgrade_mode_subcode = 0; // 记录使用的subcode（0x0100 或 0x0200）
uint32_t expected_file_size = 0;	// 升级文件大小值
static uint8_t expected_md5_0200[4];	// 接收的MD5码头4字节
static uint8_t expected_md5_0100[16];	// 接收的MD5码16字节
static uint32_t current_packet_index = 0;
static bool upgrade_in_progress = false; // 确保先收到起始帧再允许数据帧执行
bool erase_APP2 = false;
bool erase_APPdata_buffer = false;
bool save_JsRoot_0100_flag = false;

uint32_t data4001_last_data_time = 0;       // 上次收到数据帧的时间戳（单位：毫秒）

//static uint32_t upgrade_target_addr = IAPDataBuffer_ADDR; // 固定写入缓存区static uint32_t upgrade_target_addr = 0;

// 应答起始帧 状态码+固定6字节（00 00 01 00 00 00）
// （状态码：0=成功，其他为错误）
void send_upgrade_start_response(uint8_t func_code, uint16_t reg_addr, uint8_t status_code, uint16_t sliceCount)
{
    static uint8_t tx_buf[32] __attribute__((aligned(4)));
    uint16_t len = 0;
	
    tx_buf[len++] = JsRoot.addr;
    tx_buf[len++] = func_code;
    tx_buf[len++] = reg_addr >> 8;
    tx_buf[len++] = reg_addr & 0xFF;
    tx_buf[len++] = 0x41; // 自定义应答命令
    tx_buf[len++] = 0x00;
	
		tx_buf[len++] = status_code; // 状态码
		
    tx_buf[len++] = sliceCount >> 8;
    tx_buf[len++] = sliceCount & 0xFF;
		tx_buf[len++] = 0x01;	
		tx_buf[len++] = 0x00;
		tx_buf[len++] = 0x00;
		tx_buf[len++] = 0x00;
	
    uint16_t crc = modbus_crc16(tx_buf, len);
    tx_buf[len++] = crc & 0xFF;
    tx_buf[len++] = (crc >> 8) & 0xFF;

		
//	DEBUG_PRINTF("TX_BUF: ");
//    for (uint16_t i = 0; i < len; i++) {
//        DEBUG_PRINTF("%02X ", tx_buf[i]);
//    }
//    DEBUG_PRINTF("\r\n");
		
//    HAL_GPIO_WritePin(GPIOB, RS485_RE_Pin, GPIO_PIN_SET);
//    HAL_UART_Transmit_DMA(&huart1, tx_buf, len);
		uart485send(tx_buf, len);
		if(Systemmode != releasemode)
		taskprinttx(tx_buf,len);
}

// 应答数据帧  状态码 + 固定2字节（00 00）
// （状态码：0=成功，其他为错误）
void send_upgrade_data_response(uint8_t func_code, uint16_t reg_addr, uint8_t status_code, uint16_t sliceCount)
{
    static uint8_t tx_buf[32] __attribute__((aligned(4)));
    uint16_t len = 0;
	
    tx_buf[len++] = JsRoot.addr;
    tx_buf[len++] = func_code;
    tx_buf[len++] = reg_addr >> 8;
    tx_buf[len++] = reg_addr & 0xFF;
    tx_buf[len++] = 0x41; // 自定义应答命令
    tx_buf[len++] = 0x00;
	
    tx_buf[len++] = status_code; // 状态码

    tx_buf[len++] = sliceCount >> 8;
    tx_buf[len++] = sliceCount & 0xFF;
		tx_buf[len++] = 0x00;
		tx_buf[len++] = 0x00;
		tx_buf[len++] = 0x00;
		tx_buf[len++] = 0x00;

    uint16_t crc = modbus_crc16(tx_buf, len);
    tx_buf[len++] = crc & 0xFF;
    tx_buf[len++] = (crc >> 8) & 0xFF;

		
//		DEBUG_PRINTF("TX_BUF: ");
//    for (uint16_t i = 0; i < len; i++) {
//        DEBUG_PRINTF("%02X ", tx_buf[i]);
//    }
//    DEBUG_PRINTF("\r\n");
		
//    HAL_GPIO_WritePin(GPIOB, RS485_RE_Pin, GPIO_PIN_SET);
//    HAL_UART_Transmit_DMA(&huart1, tx_buf, len);
		uart485send(tx_buf, len);
		if(Systemmode != releasemode)
		taskprinttx(tx_buf,len);
}

// 应答结束帧  状态码 + 固定2字节（00 00）
// （状态码：0=成功，其他为错误）
void send_upgrade_end_response(uint8_t func_code, uint16_t reg_addr, uint8_t status_code, uint16_t sliceCount)
{
    static uint8_t tx_buf[32] __attribute__((aligned(4)));
    uint16_t len = 0;
	
    tx_buf[len++] = JsRoot.addr;
    tx_buf[len++] = func_code;
    tx_buf[len++] = reg_addr >> 8;
    tx_buf[len++] = reg_addr & 0xFF;
    tx_buf[len++] = 0x41; // 自定义应答命令
    tx_buf[len++] = 0x00;
	
    tx_buf[len++] = status_code; // 状态码

    tx_buf[len++] = sliceCount >> 8;
    tx_buf[len++] = sliceCount & 0xFF;
		tx_buf[len++] = 0x00;
		tx_buf[len++] = 0x00;
		tx_buf[len++] = 0x00;
		tx_buf[len++] = 0x00;

    uint16_t crc = modbus_crc16(tx_buf, len);
    tx_buf[len++] = crc & 0xFF;
    tx_buf[len++] = (crc >> 8) & 0xFF;

		
//		DEBUG_PRINTF("TX_BUF: ");
//    for (uint16_t i = 0; i < len; i++) {
//        DEBUG_PRINTF("%02X ", tx_buf[i]);
//    }
//    DEBUG_PRINTF("\r\n");
		
//    HAL_GPIO_WritePin(GPIOB, RS485_RE_Pin, GPIO_PIN_SET);
//    HAL_UART_Transmit_DMA(&huart1, tx_buf, len);
		uart485send(tx_buf, len);
		if(Systemmode != releasemode)
		taskprinttx(tx_buf,len);
}


/*
状态码值：
		00：正常
		01：busy
		02：升级文件大小超限
		03：单帧切片大小超限
		04：切片索引错误
		05：校验错误
		06：参数错误
		07：命令码错误
		08：执行错误（如无起始帧却先收到了数据帧）
*/

// 处理起始帧 (0x4000)
void handle_upgrade_start_frame(uint8_t *data)
{	
		DEBUG_PRINTF("\r\n============================\r\n");
		DEBUG_PRINTF("=== Upgrade start ===\r\n");
		DEBUG_PRINTF("0x4000 start\r\n");
		
    uint16_t subcode = (data[6] << 8 | data[7]);
		upgrade_mode_subcode = subcode;  // 记录本次升级的 subcode
	
		data4001_last_data_time = HAL_GetTick();  // 收到起始帧数据更新时间戳
	
		if (upgrade_mode_subcode == 0x0100) { // md5校验码16个字节签名在bin文件最后
				DEBUG_PRINTF("subcode: 0x0100\r\n");
        expected_file_size = (data[23] << 24) | (data[24] << 16) | (data[25] << 8) | data[26];
				DEBUG_PRINTF("expected_file_size[0x%04x]\r\n",expected_file_size);
			
        if (expected_file_size > APP_SIZE) {
            send_upgrade_start_response(0x42, 0xAABB, 0x02,0x0000); // 文件过大
            return;
        }
				erase_APPdata_buffer = true; // 擦除APP2缓存区

        upgrade_offset = 0;
        current_packet_index = 0;
				
        upgrade_in_progress = true; // 证明收到起始帧，数据帧允许接收
				
				save_JsRoot_0100_flag = true; // 写入升级subcode以及文件大小
				
				
				send_upgrade_start_response(0x42, 0xAABB, 0x00,0x0000); // 特殊起始帧应答
				
				Systemmode = IAPbootloader;
				DEBUG_PRINTF("0x4000 end\r\n");
				DEBUG_PRINTF("============================\r\n");
    } else if (upgrade_mode_subcode == 0x0200) { // 无签名，md5校验码直接在起始帧负载里
			
				DEBUG_PRINTF("subcode: 0x0200\r\n");
        expected_file_size = (data[8] << 24) | (data[9] << 16) | (data[10] << 8) | data[11];
				memcpy(expected_md5_0200, &data[12], 4); // 支持MD5的头4字节
					
				save_JsRoot_0100_flag = true; // 写入升级subcode以及文件大小
			
				DEBUG_PRINTF("expected_file_size[0x%04x]\r\n",expected_file_size); 
				DEBUG_PRINTF("expected_md5_0200:");
				for (int i = 0; i < 4; i++)
				{
						DEBUG_PRINTF(" %02X", expected_md5_0200[i]);
						if ((i + 1) % 4 == 0) 
						{
								DEBUG_PRINTF("\n");
						}
				}
				DEBUG_PRINTF("\n");
				DEBUG_PRINTF("============================\r\n");
				
				
        send_upgrade_start_response(0x42, 0xAABB, 0x00,0x0000); // 特殊起始帧应答
				Flash_ErasePages(IAPDataBuffer_ADDR, APP_SIZE / 4); // 擦除APP缓存区数据等待写入
				Systemmode = IAPbootloader;
				
    }else{
				send_upgrade_start_response(0x42, 0xAABB, 0x06, 0x0000);	// 参数错误
				
		}
}

// 处理数据帧 (0x4001)
void handle_upgrade_data_frame(uint8_t *data)
{
		
    if (!upgrade_in_progress) {
        send_upgrade_data_response(0x42, 0xAABB, 0x08, 0x0000); // 执行错误08（无起始帧却先收到数据帧）
        return;
    }
    if ((data[4] << 8 | data[5]) != 0x4001) return;
    
		data4001_last_data_time = HAL_GetTick();  // 每次收到数据更新时间戳
    uint16_t index = data[6] << 8 | data[7];
    if (index != current_packet_index) {
        send_upgrade_data_response(0x42, 0xAABB, 0x04, 0x0000 );
        reset_upgrade_state();
        return;
    }
    Flash_Write64(IAPDataBuffer_ADDR + upgrade_offset, (uint64_t*)&data[8], PACKET_SIZE / 8);
    upgrade_offset += PACKET_SIZE;
    current_packet_index++;
    send_upgrade_data_response(0x42, 0xAABB, 0x00, current_packet_index);
}

// 处理结束帧 (0x4002)
void handle_upgrade_end_frame(void)
{
    uint8_t result_md5[16];
    MD5_CTX context;
	
		send_upgrade_end_response(0x42, 0xAABB, 0x00, 0x0000); // 应答表示收到结束帧
		
		bool md5_pass = false;
		
		if(upgrade_mode_subcode == 0x0100)
		{
				MD5Init(&context);
				MD5Update(&context, (uint8_t*)IAPDataBuffer_ADDR, expected_file_size - 16); // 0100 -16
				MD5Final(result_md5, &context); // 计算收到的整体数据的MD5
			
				// 从 IAPDataBuffer 最后 16 字节读取 MD5 签名
				Flash_ReadBytes(IAPDataBuffer_ADDR + expected_file_size - 16, expected_md5_0100, 16); // 从 IAPDataBuffer 最后16字节读取 MD5 校验码
				
				DEBUG_PRINTF("result_md5:"); 
				for (int i = 0; i < 16; i++)
				{
						DEBUG_PRINTF(" %02X", result_md5[i]);
						if ((i + 1) % 16 == 0) // 
						{
								DEBUG_PRINTF("\n");
						}
				}
				DEBUG_PRINTF("\n");
				
				DEBUG_PRINTF("expected_md5_0100:"); // 收到MD5码16字节
				for (uint16_t i = 0; i < 16; i++)
				{
						DEBUG_PRINTF(" %02X", expected_md5_0100[i]);
						if ((i + 1) % 16 == 0) // 每行打印16字节
						{
								DEBUG_PRINTF("\n");
						}
				}
				DEBUG_PRINTF("\n");
				
				md5_pass = (memcmp(result_md5, expected_md5_0100, 16) == 0); 
				// 比较APP数据缓存区的自计算的MD5和收到的MD5码是否一致，一致则表示APP缓存区接收和写入的没问题。
		} else if(upgrade_mode_subcode == 0x0200){

				MD5Init(&context);
				MD5Update(&context, (uint8_t*)IAPDataBuffer_ADDR, expected_file_size); // 0200 -0
				MD5Final(result_md5, &context); // 计算APP缓存区的MD5
			
				DEBUG_PRINTF("expected_md5_0200: "); // 收到的MD5码
        for (uint16_t i = 0; i < 4; i++) {
            DEBUG_PRINTF(" %02X", expected_md5_0200[i]);
        }
        DEBUG_PRINTF("\n");
				DEBUG_PRINTF("result_md5 (first 4 bytes): "); // 计算的MD5码
        for (uint16_t i = 0; i < 4; i++) {
            DEBUG_PRINTF(" %02X", result_md5[i]);
        }
        DEBUG_PRINTF("\n");
				md5_pass = (memcmp(result_md5, expected_md5_0200, 4) == 0); // 因为收到的只有4字节MD5码，所以只验证前四字节对不对
		}
		
    if (md5_pass) //如果验证APP数据缓存区的MD5码通过
		{
			DEBUG_PRINTF(" MD5 check passed \r\n");       

			JsRoot.iapLoadStatus = 300;					
			DB_SaveUInt(DB_ID_SNAPSHOT, KV_KEY_IAP_LOAD , JsRoot.iapLoadStatus ); // 存入到SNAPSHOT数据库
			
			// 将每个字节转换为两位16进制字符
			for(int i = 0; i < 4; i++) {
					sprintf(&JsRoot.iapMd5[i*2], "%02X", result_md5[i]);
			}
			JsRoot.iapMd5[8] = '\0';
			DB_SaveChar(DB_ID_SNAPSHOT, KV_KEY_IAP_MD5 , JsRoot.iapMd5 ); // 存入MD5到SNAPSHOT数据库
			
			osDelay(500);
			NVIC_SystemReset();
			
    } else {
			// 收到的APP数据缓存区的数据不完整，就擦除掉缓存区的数据
				DEBUG_PRINTF (" MD5 check fails, Erase IAPDataBuffer_ADDR Flash \r\n");
				Flash_ErasePages(IAPDataBuffer_ADDR, APP_SIZE / 4);  // 擦除整个 IAPDataBuffer 区域（80KB）擦除 40 页（2048字节/页）
				send_upgrade_end_response(0x42, 0xAABB, 0x05, 0x0000); //MD5校验错误
    }

    reset_upgrade_state(); // 清除相关升级状态标志
// 4.09.06
}
// 清除升级状态（在失败或完成后调用）
void reset_upgrade_state(void)
{
    upgrade_offset = 0;
    expected_file_size = 0;
    memset(expected_md5_0100, 0, sizeof(expected_md5_0100));
		memset(expected_md5_0200, 0, sizeof(expected_md5_0200));
    current_packet_index = 0;
    upgrade_in_progress = false;
}
void check_upgrade_data4001_timeout(void)
{
    if (upgrade_in_progress)
    {
        if (HAL_GetTick() - data4001_last_data_time > UPGRADE_TIMEOUT_MS)
        {
            DEBUG_PRINTF("Upgrade timeout\r\n");
            reset_upgrade_state();       // 清除升级状态
        }
    }
}

void handle_rollback_request(void) // 回退
{
    JsRoot.iapLoadStatus = 0;
    JsRoot.ubootback = 1;
		DB_SaveUInt(DB_ID_SNAPSHOT, KV_KEY_IAP_LOAD , JsRoot.iapLoadStatus ); // 存入到SNAPSHOT数据库
		DB_SaveUInt(DB_ID_SNAPSHOT, KV_KEY_UBOOTBACK , JsRoot.ubootback ); // 存入到SNAPSHOT数据库
		osDelay(300);
    NVIC_SystemReset();
}
void app_jump_check_and_jump(void) // 初始化完成之后，检查状态是否跳转 
{

	if (__HAL_RCC_GET_FLAG(RCC_FLAG_IWDGRST)){ // 是否有看门狗导致复位
			printf ("\r\n IWDG_RST flag");
			// 增加计数
		}else{
			printf ("\r\n No IWDG_RST flag");
		}

    if (JsRoot.iapLoadStatus == 2) {
				printf ("\r\n valid APP2. Run APP2\r\n");
				if (!JumpToApp(APP2_ADDR)) {

						printf("\r\n Jump failed. Run APP1" );
        } 
					
    } else if(JsRoot.iapLoadStatus == 0){
      // 运行当前 APP1 程序
			printf ("\r\n No valid APP2. Run APP1\r\n");
    }
}

bool JumpToApp(uint32_t addr) 
{
		// APP1: sp = 0x100037A8 ; pc = 0x08014281;
		// APP2: sp = 0x100037A8 ; pc = 0x08000281;
    uint32_t sp = *(volatile uint32_t*)addr;// *(volatile uint32_t*)addr;// 0x100037A8;// 
    uint32_t pc = *(volatile uint32_t*)(addr + 4);// *(volatile uint32_t*)(addr + 4);// 0x08014281;// 
				
		if( sp ==0x100037A8 && pc == 0x08014281) // 跳转到APP2区域
		{
			DEBUG_PRINTF("\r\n Jumping app_address. SP=0x%08X, PC=0x%08X \r\n", sp, pc);
			DEBUG_PRINTF("\r\n Jumping app_address success");
			HAL_RCC_DeInit();		// 复位时钟
			HAL_DeInit();				// 复位外设
			SCB->VTOR = addr;	  // 设置向量表基地址		
			__set_MSP(sp);		  // 设置主堆栈指针
			__disable_irq();    // 关闭总中断
			void (*jump)(void) = (void (*)(void))pc; 
			jump(); // 跳转到APP
			return true;
		}else{
			printf("\r\n Jumping app_address. SP=0x%08X, PC=0x%08X \r\n", sp, pc);
			printf("\r\n Jumping app_address fail");
			return false;
		}
    
}
