
/*
 * eeprom_m24.c
 */
#include "main.h"
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "eeprom_m24.h"

extern I2C_HandleTypeDef hi2c1;

m24c16_t hext_mem_eeprom_1 = {0};
m24c16_t hext_mem_eeprom_2 = {0};

/*!
 *  @brief I2C read hook function for MCP23008 module
 */
uint8_t m24c16_i2c_read(void* intf_hdl, uint16_t slv_addr, uint8_t *data, uint16_t len)
{
    uint8_t sts = M24C16_FAIL;
    I2C_HandleTypeDef *hi2c = (I2C_HandleTypeDef*)intf_hdl;
    if(HAL_I2C_Master_Receive(hi2c, (uint16_t)(slv_addr), data, len, 500) == HAL_OK)
    {
        sts = M24C16_SCS;
    }
    else
    {
        sts = M24C16_FAIL;
    }
    return sts;
}

/*!
 *  @brief I2C write hook function for MCP23008 module
 */
uint8_t m24c16_i2c_write(void* intf_hdl, uint16_t slv_addr, uint8_t *data, uint16_t len)
{
    uint8_t sts = M24C16_FAIL;
    I2C_HandleTypeDef *hi2c = (I2C_HandleTypeDef*)intf_hdl;
    if(HAL_I2C_Master_Transmit(hi2c, (uint16_t)(slv_addr), data, len, 500) == HAL_OK)
    {
        sts = M24C16_SCS;
    }
    return sts;
}

/*!
 *  @brief this function init eeprom
 */
void eeprom_m24c16_init(uint8_t ahw_id)
{
    switch(ahw_id)
    {
    case EXT_MEM_M24C16_EEPROM_1:
    {
        hext_mem_eeprom_1.intf_hdl = &hi2c1;
        hext_mem_eeprom_1.read = m24c16_i2c_read;
        hext_mem_eeprom_1.write = m24c16_i2c_write;
    }
    break;
    case EXT_MEM_M24C16_EEPROM_2:
    {
        hext_mem_eeprom_2.intf_hdl = &hi2c1;
        hext_mem_eeprom_2.read = m24c16_i2c_read;
        hext_mem_eeprom_2.write = m24c16_i2c_write;
    }
    break;
    }
}

/**
 * @brief Erase the entire EEPROM by writing 0xFF to all addresses.
 */
uint8_t m24c16_eeprom_erase_full_chip(m24c16_t* m24c16_hdl)
{
    uint8_t sts = M24C16_FAIL;
    uint8_t erase_data[M24C16_EEPROM_PAGE_SIZE] = {0x00};
    memset(erase_data, 0xFF, M24C16_EEPROM_PAGE_SIZE); // Fill the buffer with 0xFF

    for (uint16_t address = 0; address < M24C16_TOTAL_SIZE; address += M24C16_EEPROM_PAGE_SIZE)
    {
        if (m24c16_eeprom_write_buffer(m24c16_hdl, address, erase_data, M24C16_EEPROM_PAGE_SIZE) != M24C16_SCS)
        {
            return sts; // Return failure if any write operation fails
        }
    }
    sts = M24C16_SCS; // Set status to success if all operations succeed
    return sts;
}

/**
 * @brief Write a buffer of data to the EEPROM.
 */
uint8_t m24c16_eeprom_write_buffer(m24c16_t* m24c16_hdl, uint16_t address, uint8_t *data, uint16_t length)
{
    uint8_t sts = M24C16_FAIL;
    uint8_t slave_addr = 0;
    uint8_t buffer[M24C16_EEPROM_PAGE_SIZE + 1];
    uint16_t page_offset = 0;
    uint16_t bytes_to_write = 0;
    uint16_t address_temp = 0;

    uint16_t dbg_w_cntr = 0;

    while (length > 0)
    {
        page_offset = address % M24C16_EEPROM_PAGE_SIZE;
        bytes_to_write = M24C16_EEPROM_PAGE_SIZE - page_offset; // Calculate remaining space in current page

        if (bytes_to_write > length)
        {
            bytes_to_write = length;
        }
        address_temp = address >> 8 ;
        address_temp = address_temp << 1;
        slave_addr = M24C16_EEPROM_I2C_ADDRESS | address_temp;
        buffer[0] = (uint8_t)(address);    // Address MSB
        memcpy(&buffer[1], data, bytes_to_write);
        // m24c16_hdl->wc_ctrl(M24C16_WRITE_ENABLE);

        if (m24c16_hdl->write(m24c16_hdl->intf_hdl, slave_addr, buffer, bytes_to_write + 1) == 0)
        {
            sts = M24C16_SCS;
        }
        //m24c16_hdl->wc_ctrl(M24C16_WRITE_DISABLE);

        // Wait for the write cycle to complete
        m24c16_delay_ms(3);

        address += bytes_to_write;
        data += bytes_to_write;
        length -= bytes_to_write;
        dbg_w_cntr++;
    }
    return sts;
}

/**
 * @brief Read a buffer of data from the EEPROM.
 */
uint8_t m24c16_eeprom_read_buffer(m24c16_t* m24c16_hdl, uint16_t address, uint8_t *data, uint16_t length)
{
    uint8_t address_bytes = 0;
    uint8_t sts = M24C16_FAIL;
    uint8_t slave_addr = 0;
    uint16_t page_offset = 0;
    uint16_t bytes_to_read = 0;
    uint16_t dbg_r_cntr = 0;
    uint16_t address_temp = 0;

    while (length > 0)
    {
        page_offset = address % M24C16_EEPROM_PAGE_SIZE;
        bytes_to_read = M24C16_EEPROM_PAGE_SIZE - page_offset; // Calculate remaining space in current page

        if (bytes_to_read > length)
        {
            bytes_to_read = length;
        }
        address_temp = address >> 8 ;
        address_temp = address_temp << 1;
        slave_addr = M24C16_EEPROM_I2C_ADDRESS | address_temp;
        address_bytes = (uint8_t)address;
        if (m24c16_hdl->write(m24c16_hdl->intf_hdl, slave_addr, &address_bytes, 1) == 0)
        {
            if (m24c16_hdl->read(m24c16_hdl->intf_hdl, slave_addr, data, bytes_to_read) == 0)
            {
                sts = M24C16_SCS;
            }
        }

        // Wait for the write cycle to complete
        m24c16_delay_ms(3);

        address += bytes_to_read;
        data += bytes_to_read;
        length -= bytes_to_read;
        dbg_r_cntr++;
    }
    return sts;
}

/**
 * @brief Delay function (platform-specific).
 *
 * @param ms Number of milliseconds to delay
 */
__attribute__((weak)) void m24c16_delay_ms(uint16_t ms)
{
    // Implement platform-specific delay function
}
