#include "hal_flash.h"

/*********************************************************************
*DEFINITION
*/
typedef struct zone_s
{
	uint8_t	 page_num;
	uint32_t zone_start_addr;
}zone_t;


/*********************************************************************
* LOCAL VARIABLES
*/
static int16_t 		PageCount = FLASH_PAGE_NUM;											//可用页数
static uint32_t		zone_used_index = 0;														//zone结构体下标
static zone_t 		zone[FLASH_PAGE_NUM];														//zone结构体

/*********************************************************************
 * LOCAL FUNCTIONS
 */
/**
 @brief 通过区域地址获取区域索引
 @param zone_addr[in] - 分配的区域地址
 @param zone_index[out] - 返回分配的区域索引
 @return 错误代码
*/ 
static uint32_t Zone_GetIndex(uint32_t zone_addr, uint32_t *zone_index)
{
	for(int i=0; i < zone_used_index; i++)
	{
		if( zone[i].zone_start_addr <= zone_addr && (zone[i].zone_start_addr + zone[i].page_num * FLASH_PAGE_SIZE) > zone_addr ){
			if(zone_index != NULL)*zone_index = i;
			return ZONE_OP_SUCCESS;
		}
	}
	return ZONE_ERROR_NO_INDEX;
}


/*********************************************************************
* PUBLIC FUNCTIONS
*/
/**
 @brief 初始化一个区域
 @param page_size[in] - 该区域使用了几个页
 @param zone_start_addr[out] - 返回分配的区域首地址
 @return 错误代码
*/ 
uint32_t Zone_Alloca(uint8_t page_num, uint32_t *zone_start_addr)
{
	if(page_num <= 0) return ZONE_ERROR_PARAM_PAGE_NUM;
	
	if(PageCount - page_num < 0)return ZONE_ERROR_NO_PAGE;
	
	zone[zone_used_index].page_num = page_num;
	if(zone_used_index == 0)zone[zone_used_index].zone_start_addr = FLASH_ZONE_ADDR_1;
	else zone[zone_used_index].zone_start_addr = zone[zone_used_index-1].zone_start_addr + FLASH_PAGE_SIZE * zone[zone_used_index-1].page_num;
	
	if(zone_start_addr != NULL)*zone_start_addr = zone[zone_used_index].zone_start_addr;
	
	PageCount -= page_num;
	zone_used_index++;

	
	return ZONE_OP_SUCCESS;
}

/**
 @brief 读取一个区域中指定位置的数据
 @param destination_addr[in] - 读取区域的地址
 @param pData[in] - 指向存储的buff
 @param dataLen[in] - 读取的长度，单位字节
 @return 错误代码
*/ 
uint32_t Zone_Read(uint32_t destination_addr, uint32_t *pData, uint32_t dataLen)
{
	uint32_t 	err_code;						//错误代码
	uint32_t  zone_index;					//是否有相对应的区域索引
	uint32_t  next_zone_addr;			//下个区域的首地址
	
	//验证是否申请了对应区域
	err_code = Zone_GetIndex(destination_addr, &zone_index);
	if(err_code != ZONE_OP_SUCCESS)return err_code;
	
	if(pData != NULL && dataLen != 0)
	{
		next_zone_addr = zone[zone_index].zone_start_addr + zone[zone_index].page_num * FLASH_PAGE_SIZE;
		//不能读到下一个区域
		if((destination_addr + dataLen) > next_zone_addr)return ZONE_ERROR_READ_FAIL;
		//读取
		err_code = Read_N_Byte_flash(destination_addr, pData, dataLen);
		if(err_code != FLASH_OP_SUCCESS)return ZONE_ERROR_READ_FAIL;
	}
	
	return ZONE_OP_SUCCESS;
	
}

/**
 @brief 往一个区域指定位置写入数据（支持跨页写）
 @param destination_addr[in] - 写入区域的地址
 @param pData[in] - 指向存储的buff
 @param dataLen[in] - 写入的长度
 @return 错误代码
*/ 
uint32_t Zone_Write(uint32_t destination_addr, uint32_t *pData, uint32_t dataLen)
{
	uint32_t 	err_code;						//错误代码
	uint32_t  zone_index;					//是否有相对应的区域索引
	uint32_t  next_zone_addr;			//下个区域的首地址

	//验证是否申请了对应区域
	err_code = Zone_GetIndex(destination_addr, &zone_index);
	if(err_code != ZONE_OP_SUCCESS)return err_code;
	
	if(pData != NULL && dataLen != 0)
	{
		next_zone_addr = zone[zone_index].zone_start_addr + zone[zone_index].page_num * FLASH_PAGE_SIZE;
		//不能写到下一个区域
		if((destination_addr + dataLen) > next_zone_addr)return ZONE_ERROR_WRITE_FAIL;
		//写入
		err_code = Write_N_4Byte_flash(destination_addr, pData, dataLen);
		if(err_code != FLASH_OP_SUCCESS)return ZONE_ERROR_WRITE_FAIL;
	}
	return ZONE_OP_SUCCESS;
}
/**
 @brief 擦除一个区域
 @param zone_start_addr[in] - 分配的区域首地址
 @return 错误代码
*/ 
uint32_t Zone_Erase(uint32_t zone_start_addr)
{
	uint32_t 	err_code;						//错误代码
	uint32_t  zone_index;					//是否有相对应的区域索引
	uint16_t 	PageNum;
	
	//验证是否申请了对应区域
	err_code = Zone_GetIndex(zone_start_addr, &zone_index);
	if(err_code != ZONE_OP_SUCCESS)return err_code;
	//一页一页擦除
	for(uint8_t i = 0; i < zone[zone_index].page_num; i++)
	{
		//获取输入地址对应的页号
		err_code = GetPageNum(zone[zone_index].zone_start_addr + i * FLASH_PAGE_SIZE, &PageNum);
		if(err_code != FLASH_OP_SUCCESS)return ZONE_ERROR_ERASE_FAIL;
		//根据页号擦除一整页
		err_code = Erase_OnePage(PageNum);
		if(err_code != FLASH_OP_SUCCESS)return ZONE_ERROR_ERASE_FAIL;
	}
	return ZONE_OP_SUCCESS;
}

/**
 @brief 获取一个区域的总字节大小
 @param zone_start_addr[in] - 分配的区域首地址
 @param bytes[out] - 返回区域的字节大小
 @return 错误代码
*/ 
uint32_t Zone_GetByteSize(uint32_t zone_start_addr, uint32_t *bytes)
{
	uint32_t 	err_code;						//错误代码
	uint32_t  zone_index;					//是否有相对应的区域索引
	
	//验证是否申请了对应区域
	err_code = Zone_GetIndex(zone_start_addr, &zone_index);
	if(err_code != ZONE_OP_SUCCESS)return err_code;
	
	if(bytes != NULL)*bytes = zone[zone_index].page_num * FLASH_PAGE_SIZE;
	
	return ZONE_OP_SUCCESS;
}




