#include "kit_data.h"
#include "kit_debug.h"
//#include "bsp_head.h"
//#include "bsp_flash.h"

#include "bsp_eeprom.h"


#define BSP_EEPROM_HEAD_LEN             0x0C
#define BSP_EEPROM_DATA_EMPTY           0xFFFFFFFF


EEpromItem *_eeprom_item;

static bool eeprom_format(EEpromItem * item, AreaItem * area);
static bool eeprom_transfer(EEpromItem * item, AreaItem * area);
static bool eeprom_load_area(EEpromItem * item, AreaItem * area);
static bool eeprom_load_def_data(EEpromItem * item, AreaItem * area);
static bool eeprom_check_data(uint16_t idx, uint16_t value, EepromDataType type);
static bool eeprom_write_static_data(EEpromItem * item, uint16_t idx, uint32_t value);
static bool eeprom_is_remain_enough(AreaItem * area, uint32_t need_space);
static uint8_t eeprom_get_area_type(EEpromItem * item, uint16_t idx);

static uint8_t eeprom_get_area_type(EEpromItem * item, uint16_t idx)
{
    return (item->format[idx].property & BSP_EEPROM_AREA_BIT_MASK);
}


static bool eeprom_is_remain_enough(AreaItem * area, uint32_t need_space)
{
	KIT_ASSERT_PARAM(area != NULL);

	return ((area->write_addr + need_space) <= (area->main_addr + area->area_size));
}


static bool eeprom_format(EEpromItem * item, AreaItem * area)
{
    bool  res = false;

	KIT_ASSERT_PARAM((item != NULL) && (area != NULL));
    
    if((flash_series_erase_page(item->flash, area->main_addr, area->erase_page_num) == kKitResult_Ok) 
    && (flash_series_erase_page(item->flash, area->sub_addr, area->erase_page_num) == kKitResult_Ok)
    && (bsp_head_set_status(item->flash, area->main_addr, kHeadStatus_Start) == kKitResult_Ok)
    && (bsp_head_set_status(item->flash, area->main_addr, kHeadStatus_Finsih) == kKitResult_Ok))
    {
        area->write_addr = area->main_addr + BSP_EEPROM_HEAD_LEN;
        res = true;
    } 
    return res;
}

/***********************************************************************************
	*Case      full        move     read = write    res
	* 1         1           1           1           1
	* 2         1           1           0           0
	* 3         1           0           -           0
	* 4         0           -           1           1
	* 5         0           -           0           0
*************************************************************************************/
static bool eeprom_write_static_data(EEpromItem * item, uint16_t idx, uint32_t value)
{
    bool res;
	uint32_t write_cnt = 0;

	KIT_ASSERT_PARAM(item != NULL);

	value &= 0x0000FFFF;
	value |= ((uint32_t)idx << 16);
	while (write_cnt++ <= BSP_EEPROM_REWRITE_CNT)
	{
		res = flash_write_u32_with_check_in(item->flash, &item->static_area.write_addr, value);
        res &= flash_checken_write_u32(item->flash, item->static_area.write_addr + item->check_addr_offset - 4, ~value); //写校验值
		if(res == true)
			item->data_buf[idx] = value;

		if ((eeprom_is_remain_enough(&item->static_area, 4) == false) && (eeprom_transfer(item, &item->static_area) == false))
			return false;
		
		if (res == true)
			return true;
	}
	return false;
}


/***************************搬运时意外情况************************
1.写完head1后掉电或者未搬完掉电(valid receive)

*****************************************************************/

static bool eeprom_transfer(EEpromItem * item, AreaItem * area)
{
	uint32_t idx, tmp, addr, write_cnt = 0;

	KIT_ASSERT_PARAM((item != NULL) && (area != NULL));

	if (bsp_head_get_status(item->flash, area->sub_addr) != kHeadStatus_None)
	{
		if (flash_series_erase_page(item->flash, area->sub_addr, area->erase_page_num) != kKitResult_Ok)
			return false;
	}
    
	if (bsp_head_set_status(item->flash, area->sub_addr, kHeadStatus_Start) != kKitResult_Ok)
		return false;

	addr = area->sub_addr + BSP_EEPROM_HEAD_LEN;
	for (idx = 0; idx < item->total_data_cnt; idx++)
	{
		write_cnt = 0;
		if (eeprom_get_area_type(item, idx) == area->type)
		{
			tmp = ((uint32_t)idx << 16) | item->data_buf[idx];
			while (write_cnt++ < BSP_EEPROM_REWRITE_CNT)
			{
				if (flash_write_u32_with_check_in(item->flash, &addr, tmp) == true)
                {
                    if((area->type == BSP_EEPROM_AREA_STATIC)
                    && (flash_checken_write_u32(item->flash, addr + item->check_addr_offset - 4, ~tmp) == false))
                    {
                        continue;
                    }
                    break;
                }			
			}
			if(write_cnt >= BSP_EEPROM_REWRITE_CNT)
				return false;
		}
	}
    //搬完数据后立即写标志后再擦除
	if ((flash_series_erase_page(item->flash, area->main_addr, area->erase_page_num) == kKitResult_Ok)
     && (bsp_head_set_status(item->flash, area->sub_addr, kHeadStatus_Finsih) == kKitResult_Ok)) 
     {
        tmp = area->main_addr;
        area->main_addr = area->sub_addr;
        area->sub_addr = tmp;
		area->write_addr = addr;
     }
     return true;
}
//Tan 20230909 优化eeprom 初始化标志位
#define EEPROM_INIT_FLAG_NUM    40
static bool eeprom_load_data(EEpromItem * item, AreaItem * area)
{
    bool res = true;
    uint32_t is_data_load[EEPROM_INIT_FLAG_NUM];
	uint32_t tmp = 0, ntmp = 0, idx, read_addr, end_addr;

	KIT_ASSERT_PARAM((item != NULL) && (area != NULL));

	read_addr = area->main_addr + BSP_EEPROM_HEAD_LEN;
	end_addr = area->main_addr + area->area_size;
    kit_set_buf(is_data_load, EEPROM_INIT_FLAG_NUM * 4, 0);    
    
	while (read_addr < end_addr)
	{
		flash_read_u32(item->flash, read_addr, &tmp);
        if(area->type == BSP_EEPROM_AREA_STATIC)
            flash_read_u32(item->flash, read_addr + item->check_addr_offset, &ntmp);
        else
            ntmp = ~tmp;
        
		if (tmp == BSP_EEPROM_DATA_EMPTY)
			break;
        
        read_addr += 4;
        
        if(tmp + ntmp == 0xFFFFFFFF)
        {
            idx = (uint16_t)(tmp >> 16);
            if ((idx < item->total_data_cnt) && (eeprom_get_area_type(item, idx) == area->type))
            {
                KIT_SET_32B_ARRAY_BIT(is_data_load, idx);
                item->data_buf[idx] = (uint16_t)tmp; 
            }
        }
	}
	area->write_addr = read_addr;

    if(area->type == BSP_EEPROM_AREA_STATIC)
    {
        for (idx = 0; idx < item->total_data_cnt; idx++)
        {
            //TODO 初始化满,且正好新加了一个参数
            if ((KIT_GET_32B_ARRAY_BIT(is_data_load, idx) == 0) 
            && (eeprom_get_area_type(item, idx) == BSP_EEPROM_AREA_STATIC))
            {
                tmp = item->format[idx].config_default;
                res &= eeprom_write_static_data(item, idx, tmp);
            }           
        }
    }
	return res;
}

static bool eeprom_load_def_data(EEpromItem * item, AreaItem * area)
{
    bool res = true;
	uint32_t idx;

	KIT_ASSERT_PARAM((item != NULL) && (area != NULL));

	//只加载静态区默认值
	if (area->type == BSP_EEPROM_AREA_STATIC)
	{
		for (idx = 0; idx < item->total_data_cnt; idx++)
		{
			if (eeprom_get_area_type(item, idx) == area->type)
			{
				item->data_buf[idx] = item->format[idx].config_default;
				res &= eeprom_write_static_data(item, idx, item->data_buf[idx]);
			}
		}
	}
	return res;
}
/*******************************************************************************
    B1    B2    MB      情况
1.  V     E     B1      正常模式
2.  V     RI    B1      换页中掉电
3.  V     RD    B2      换完页后掉电
4.  O     RD    B2      头部未擦除完全
4.  E     RD    B2      擦除页时掉电
4.  E     V     B2      完成换页
*******************************************************************************/
static bool eeprom_load_area(EEpromItem * item, AreaItem * area)
{
	bool res = false;
	HeadStatus head_part1_st, head_part2_st;
    
    uint32_t tmp;

	KIT_ASSERT_PARAM((item != NULL) && (area != NULL));

	head_part1_st = bsp_head_get_status(item->flash, area->main_addr);
	head_part2_st = bsp_head_get_status(item->flash, area->sub_addr);
    
	if (head_part2_st == kHeadStatus_Finsih)
	{  
		tmp = area->main_addr;
		area->main_addr = area->sub_addr;
		area->sub_addr = tmp;
	}
    /***********************************************************************************
	*Case        block1         block2              mark
	* 1          empty(sub)     valid(main)        normal
	* 2          valid(main)    empty(sub)         normal
	***********************************************************************************/
    if(((head_part1_st == kHeadStatus_Finsih) && (head_part2_st == kHeadStatus_None))
     ||((head_part1_st == kHeadStatus_None) && (head_part2_st == kHeadStatus_Finsih)))  
	{
		if (eeprom_load_data(item, area) == true)
        {
			if (area->type == BSP_EEPROM_AREA_DYNAMIC)
            {
				tmp = area->data_cnt << 3;
            }
			else
            {
                //Tan 20230920 静态区预留40字节
				tmp = 4 * 10;
            }
            //可能在需要换页擦除时掉电,上电需要检测是否需要换页
            if((eeprom_is_remain_enough(area, tmp) == false)
            && (eeprom_transfer(item, area) == false))
                return false;
            
            res = true;    
        }
	}  
	/*************************************************************************************
	*Case        block1             block2              mark
	* 1          valid(main)        receiving(sub)      interrupt while transfer(should continue the transfer)
	* 2          receiving(sub)     valid(main)         interrupt while transfer(should continue the transfer)
	**************************************************************************************/
	else if(((head_part1_st == kHeadStatus_Finsih) && (head_part2_st == kHeadStatus_Start))
     ||((head_part1_st == kHeadStatus_Start) && (head_part2_st == kHeadStatus_Finsih))) 
	{
		if((flash_series_erase_page(item->flash, area->sub_addr, area->erase_page_num) == kKitResult_Ok) 
        && (eeprom_load_data(item, area) == true) 
        && (eeprom_transfer(item, area) == true))
            res = true;
	}
	/***********************************************************************************
	*Case        Page0             Page1                mark
	* 1          empty(sub)        received(main)       interrupt while modify the flag or interrupt while erase(transfer finish)
	* 2          received(main)    empty(sub)           interrupt while modify the flag or interrupt while erase(transfer finish)
	***********************************************************************************/
//	else if((head_joint & kBspEepromHeadSt_Received) == kBspEepromHeadSt_Received)
//	{
//		if((flash_series_erase_page(item->flash, area->sub_addr, area->page_num * area->type) == kKitResult_Ok) 
//        && (eeprom_set_head_status(area->main_addr, BSP_EEPROM_HEAD_PART3) == true) 
//        && (eeprom_load_data(item, area) == true))
//            res = true;   
//	}
	/***********************************************************************************
	*Case        Page0        Page1        mark
	* 1          empty       empty         maybe first to use or exception
	* 2          valid       valid         exception
	* 3          receive     receive       exception
	* 4          other       other         page0 or page1 or both not the set state
	************************************************************************************/
	else
	{
        if((eeprom_format(item, area) == true) 
        && (eeprom_load_def_data(item, area) == true))
            res = true;
	}
	return res;
}
/*********************************************************************************************
1.eeprom数据结构:备份区可选,主区和校验区可放置在1个page中,主区和副区会相互转换
      +-------+-------+-------+-------+-------+-------+-------+-------+-------+
      |       |       |       |       |       |       |       |       |       |
      | 备份区| S主区 |校验区 | S副区 |  校验 | D主区 | D副区 |       |       |
      |       |       |       |       |  副区 |       |       |       |       |
      |       |       |       |       |       |       |       |       |       |
      +-------+-------+-------+-------+-------+-------+-------+-------+-------+
**********************************************************************************************/

bool bsp_eeprom_init(EEpromItem *item, uint8_t backup_page_num, uint8_t static_page_num, uint8_t dynamic_page_num, uint32_t start_addr)
{
	bool     res = true;
	uint32_t idx, tmp, static_num = 0, dynamic_num = 0;

	KIT_ASSERT_PARAM(item != NULL);

	if (item != NULL)
	{
        _eeprom_item = item;
        item->backup_area_addr = start_addr;
        item->backup_area_page_num = backup_page_num;
        //计算静态区和动态区数据个数
        for (idx = 0; idx < item->total_data_cnt; idx++)
        {
            if (eeprom_get_area_type(item, idx) == BSP_EEPROM_AREA_STATIC)
                static_num++;
            else if (eeprom_get_area_type(item, idx) == BSP_EEPROM_AREA_DYNAMIC)
                dynamic_num++;
        }
        //407 小容量页较少,将主区和校验区放在一个页中,即将一个页分为两个区用
	#if defined(STM32F40_41xxx) || defined(STM32F429_439xx)
        tmp = flash_get_page_size(item->flash) >> 1;
	#else
		tmp = flash_get_page_size(item->flash);
	#endif	
        if ((((static_num + 4) << 2) >= tmp * static_page_num)
            || (((dynamic_num + 4) << 2) >= tmp * dynamic_page_num))
            return false;
         
        item->check_addr_offset = tmp * static_page_num;
        item->static_area.data_cnt = static_num;
        item->dynamic_area.data_cnt = dynamic_num;

        item->static_area.type = BSP_EEPROM_AREA_STATIC;
        item->static_area.area_size = tmp * static_page_num;
        item->static_area.main_addr = (start_addr + backup_page_num * tmp);
		item->static_area.sub_addr = (item->static_area.main_addr + static_page_num * tmp *2);
		
        item->dynamic_area.type = BSP_EEPROM_AREA_DYNAMIC;
        item->dynamic_area.area_size = tmp * static_page_num;
		item->dynamic_area.main_addr = (item->static_area.sub_addr + static_page_num * tmp * 2);
	#if defined(STM32F40_41xxx) || defined(STM32F429_439xx)
        item->dynamic_area.erase_page_num = item->static_area.erase_page_num = 1;
        item->dynamic_area.sub_addr = (item->dynamic_area.main_addr + dynamic_page_num * tmp * 2);
	#else
        item->static_area.erase_page_num = static_page_num * BSP_EEPROM_AREA_STATIC;
        item->dynamic_area.erase_page_num = dynamic_page_num * BSP_EEPROM_AREA_DYNAMIC;
		item->dynamic_area.sub_addr = (item->dynamic_area.main_addr + dynamic_page_num * tmp);
	#endif
	
    #if defined(STM32F429_439xx) //20231005 by songman 防止配置恢复默认配置
        if((item->static_area.main_addr == start_addr) 
        && ((ARM_READ_INT32U(0x08006010) == 0xFFFFFFFF) && (ARM_READ_INT32U(0x08006014) == 0xFFFFFFFF)))
        {
            flash_series_write_u32(item->flash, 0x08006000, (uint8_t *)0x08008000, 0x2000);
            flash_series_erase_page(item->flash, 0x08008000, 1);
        }
    #endif
	
        if (static_num != 0)
            res &= eeprom_load_area(item, &item->static_area);

        if (dynamic_num != 0)
            res &= eeprom_load_area(item, &item->dynamic_area);
    }
    
	return res;
}

static bool eeprom_check_data(uint16_t idx, uint16_t value, EepromDataType type)
{
    bool res = false;
    uint32_t max, min;
    
    if (((_eeprom_item->format[idx].property) & BSP_EEPROM_CHECK_MAX_MIN_BIT_MASK) == BSP_EEPROM_CHECK_MAX_MIN_ON)
    {
        max = _eeprom_item->format[idx].config_max;
        min = _eeprom_item->format[idx].config_min;
        if (((type == kEepromDataType_Full) && (value >= min) && (value <= max ))
         || ((type == kEepromDataType_High) && (value >= (min >> 8)) && (value <= (max >> 8)))
         || ((type == kEepromDataType_Low)  && (value >= (uint8_t)min) && (value <= (uint8_t)max)))
        {
             res = true;
        }
    }
    else
    {
        res = true;
    }
    return res;
}

bool bsp_eeprom_set_data(uint16_t idx, uint32_t value, EepromDataType type)
{
    bool  res = false;
    uint16_t write_value;
    
    KIT_ASSERT_PARAM((idx < _eeprom_item->total_data_cnt) && (type < kEepromDataType_End));
    
    if ((idx < _eeprom_item->total_data_cnt) 
     && (type < kEepromDataType_End) 
     && (eeprom_check_data(idx, value, type) == true))
    {
		res = true;
        write_value = _eeprom_item->data_buf[idx];
        if (type == kEepromDataType_High)
            write_value = (write_value & 0x00FF) | (value << 8);
        else if (type == kEepromDataType_Low)
            write_value = (write_value & 0xFF00) | (value & 0x00FF);
        else if (type == kEepromDataType_Full)
            write_value = value;
        else
        {
            //如果4字节,先写低16位,再写高16位
			if(_eeprom_item->data_buf[idx] != (uint16_t)value)
            {
                _eeprom_item->data_buf[idx] = (uint16_t)value;
            }
			write_value = value >>16;
            idx++;
        }
		if (_eeprom_item->data_buf[idx] != write_value)
        {
            _eeprom_item->data_buf[idx] = (uint16_t)write_value;
        }
    }
    return res;
}

bool bsp_eeprom_save_data(uint16_t idx, uint32_t value, EepromDataType type)
{
    bool  res = false;
    uint16_t write_value;
    
    KIT_ASSERT_PARAM((idx < _eeprom_item->total_data_cnt) && (type < kEepromDataType_End));
    
    if ((idx < _eeprom_item->total_data_cnt) && (type < kEepromDataType_End) && (eeprom_check_data(idx, value, type) == true))
    {
		res = true;
        write_value = _eeprom_item->data_buf[idx];
        if (type == kEepromDataType_High)
            write_value = (write_value & 0x00FF) | (value << 8);
        else if (type == kEepromDataType_Low)
            write_value = (write_value & 0xFF00) | (value & 0x00FF);
        else if (type == kEepromDataType_Full)
            write_value = value;
        else
        {
			if((_eeprom_item->data_buf[idx] != (uint16_t)value)
            || ((_eeprom_item->format[idx].property & BSP_EEPROM_CHECK_EQUAL_BIT_MASK) == false))
            {
				res &= eeprom_write_static_data(_eeprom_item, idx, (uint16_t)value);
            }
            
			write_value = value >>16;
            idx++;
        }
		if((_eeprom_item->data_buf[idx] != (uint16_t)write_value)
        || ((_eeprom_item->format[idx].property & BSP_EEPROM_CHECK_EQUAL_BIT_MASK) == false))
        {
			res &= eeprom_write_static_data(_eeprom_item, idx, write_value);
        }
    }
    return res;
}

uint32_t bsp_eeprom_get_data(uint16_t idx, EepromDataType type)
{
    uint32_t value = 0;
    
    KIT_ASSERT_PARAM((idx < _eeprom_item->total_data_cnt) && (type < kEepromDataType_End));
    
    if ((idx < _eeprom_item->total_data_cnt) && (type < kEepromDataType_End))
    {
		value = _eeprom_item->data_buf[idx];

		if (type == kEepromDataType_High)
			value >>= 8;
		else if (type == kEepromDataType_Low)
			value &= 0x00FF;
		else if (type == kEepromDataType_Double)
			value |= (uint32_t)_eeprom_item->data_buf[idx + 1] << 16;
    }
    return value;
}


bool bsp_eeprom_backup_static_area(void)
{
	uint32_t i, write, addr = _eeprom_item->backup_area_addr;

	if ((_eeprom_item->backup_area_page_num != 0) && (flash_series_erase_page(_eeprom_item->flash, addr, _eeprom_item->static_area.erase_page_num) == kKitResult_Ok))
	{
		for (i = 0; i < _eeprom_item->total_data_cnt; i++)
		{
			write = (uint32_t)_eeprom_item->data_buf[i] | (i << 16);
			if (flash_write_u32_with_check_in(_eeprom_item->flash, &addr, write) == false)
				return false;
		}
	}
	return true;
}

bool bsp_eeprom_recovery_static_area(EEpromItem *item, uint16_t except_idx)
{
    bool res = false;
    
    uint16_t data = bsp_eeprom_get_data(except_idx, kEepromDataType_Full);
    
    if((eeprom_format(item, &item->static_area) == true) 
    && (eeprom_load_def_data(item, &item->static_area) == true))
    {
        res = true;
        bsp_eeprom_save_data(except_idx, data, kEepromDataType_Full);
    }
    return res;
}

void bsp_eeprom_power_off_save_data(EepromPoData *data, uint32_t len)
{
    uint32_t i, idx, write_cnt;
    //静态区页满情况:在上电换页时掉电,此时静态区数据不用存
    if((data != NULL) && (_eeprom_item != NULL) && (len <= _eeprom_item->dynamic_area.data_cnt)
    && (eeprom_is_remain_enough(&_eeprom_item->dynamic_area, _eeprom_item->dynamic_area.data_cnt << 3) == true))
    {
		for (i = 0; i < len; i++)
        {
            write_cnt = 0;
            idx = data[i].item.idx;
            if ((idx < _eeprom_item->total_data_cnt) && (_eeprom_item->data_buf[idx] != data[i].item.value)
             && (eeprom_get_area_type(_eeprom_item, idx) == BSP_EEPROM_AREA_DYNAMIC))
            {
                while ((flash_write_u32_with_check_in(_eeprom_item->flash, &_eeprom_item->dynamic_area.write_addr, data[i].value) == false)
					&& (write_cnt++ < BSP_EEPROM_REWRITE_CNT))
                { 
				}
            }
        }
    }    
}