#include "flash_ring.h"


uint8_t current_page = 0;  // текущая страница для записи


// Вычисление CRC16 (полином 0x8005)
static uint16_t calc_crc16(const uint8_t *data, uint32_t len)
{
    uint16_t crc = 0xFFFF;
    for (uint32_t i = 0; i < len; i++)
    {
        crc ^= data[i];
        for (int j = 0; j < 8; j++)
        {
            if (crc & 1) crc = (crc >> 1) ^ 0xA001;
            else crc >>= 1;
        }
    }
    return crc;
}

// Проверка заголовка страницы
static bool is_page_valid(uint32_t addr)
{
    PageHeader_t *hdr = (PageHeader_t *)addr;
    return (hdr->valid_marker == 0xDEADBEEF);
}

// Инициализация: поиск актуальной страницы или очистка
bool FlashRing_Init(void)
{
    for (uint8_t i = 0; i < PAGE_COUNT; i++)
    {
        uint32_t addr = FLASH_BASE_USER + i * FLASH_PAGE_SIZE_USER;
        if (is_page_valid(addr))
        {
            current_page = i;
            return true;
        }
    }
    // Если ни одна страница не актуальна — стираем обе
    if (FlashRing_EraseAll()!= 1) {
        return false;  // Ошибка стирания
    }
		  

		
    current_page = 0;
    return true;
}

uint16_t CalculateCRC16(const uint8_t *data, uint32_t len) {
    uint16_t crc = 0xFFFF;  // Начальное значение
    for (uint32_t i = 0; i < len; i++) {
        crc ^= data[i];
        for (int j = 0; j < 8; j++) {
            if (crc & 1) {
                crc = (crc >> 1) ^ 0xA001;  // 0xA001 = обратный полином 0x8005
            } else {
                crc >>= 1;
            }
        }
    }
    return crc;
}


// Стирание всех страниц
int FlashRing_EraseAll(void)
{
    HAL_FLASH_Unlock();
    FLASH_EraseInitTypeDef EraseInit =
    {
        .TypeErase = FLASH_TYPEERASE_PAGES,
        .PageAddress = FLASH_BASE_USER,
        .NbPages = PAGE_COUNT
    };
    uint32_t page_error;
    HAL_FLASHEx_Erase(&EraseInit, &page_error);
    //  HAL_FLASHLock();
    HAL_FLASH_Lock();
		return 1;
}

// Запись уставки
bool FlashRing_Write(const Setpoint_t *sp)
{
    uint32_t page_addr = FLASH_BASE_USER + current_page * FLASH_PAGE_SIZE_USER;
    PageHeader_t *hdr = (PageHeader_t *)page_addr;

    // Если страница не инициализирована или заполнена — переключаемся
    if (!is_page_valid(page_addr) || hdr->count >= (FLASH_PAGE_SIZE_USER - sizeof(PageHeader_t)) / sizeof(Setpoint_t))
    {
        // Стираем следующую страницу
        uint8_t next_page = (current_page + 1) % PAGE_COUNT;
        uint32_t next_addr = FLASH_BASE_USER + next_page * FLASH_PAGE_SIZE_USER;

        HAL_FLASH_Unlock();
        FLASH_EraseInitTypeDef EraseInit =
        {
            .TypeErase = FLASH_TYPEERASE_PAGES,
            .PageAddress = next_addr,
            .NbPages = 1
        };
        uint32_t page_error;
        HAL_FLASHEx_Erase(&EraseInit, &page_error);
      
        current_page = next_page;
        page_addr = next_addr;
       
		HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, (uint32_t)&hdr->valid_marker, 0xDEADBEEF);
HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD, (uint32_t)&hdr->count, 0);				
				HAL_FLASH_Lock();

    }

    // Вычисляем смещение для новой уставки
    uint8_t *data_start = (uint8_t *)page_addr + sizeof(PageHeader_t);
    uint16_t idx = hdr->count;
    Setpoint_t *dst = (Setpoint_t *)(data_start + idx * sizeof(Setpoint_t));

    *dst = *sp;
    hdr->count++;

    // Обновляем CRC
    hdr->crc = calc_crc16(data_start, hdr->count * sizeof(Setpoint_t));

    return true;
}

// Чтение уставки по индексу
bool FlashRing_Read(uint16_t index, Setpoint_t *sp)
{
    uint32_t page_addr = FLASH_BASE_USER + current_page * FLASH_PAGE_SIZE_USER;
    if (!is_page_valid(page_addr)) return false;


    PageHeader_t *hdr = (PageHeader_t *)page_addr;
    if (index >= hdr->count) return false;

    uint8_t *data_start = (uint8_t *)page_addr + sizeof(PageHeader_t);
    *sp = *(Setpoint_t *)(data_start + index * sizeof(Setpoint_t));
    return true;
}

// Получение числа записанных уставок
uint16_t FlashRing_GetCount(void)
{
    uint32_t page_addr = FLASH_BASE_USER + current_page * FLASH_PAGE_SIZE_USER;
    if (!is_page_valid(page_addr)) return 0;
    PageHeader_t *hdr = (PageHeader_t *)page_addr;
    return hdr->count;
}
