#include "Int_EEPROM24xx.h"
#include "i2c.h"

static uint8_t g_inited = 0u;
static uint32_t g_last_hal_error = 0u;

static int32_t Int_EEPROM24xx_CheckRange(uint32_t mem_addr, uint16_t len)
{
    uint32_t end_addr;

    if (len == 0u)
    {
        return INT_EEPROM_ERR_ARG;
    }

    end_addr = mem_addr + (uint32_t)len;
    if (end_addr < mem_addr)
    {
        return INT_EEPROM_ERR_RANGE;
    }

    if (end_addr > INT_EEPROM24XX_MEM_SIZE_BYTES)
    {
        return INT_EEPROM_ERR_RANGE;
    }

    return INT_EEPROM_OK;
}

static uint16_t Int_EEPROM24xx_GetMemAddrSizeHal(void)
{
    return (INT_EEPROM24XX_MEM_ADDR_SIZE == 1u) ? I2C_MEMADD_SIZE_8BIT : I2C_MEMADD_SIZE_16BIT;
}

static int32_t Int_EEPROM24xx_WaitReady(void)
{
    if (HAL_I2C_IsDeviceReady(&INT_EEPROM24XX_I2C_HANDLE,
                              INT_EEPROM24XX_DEV_ADDR,
                              20u,
                              INT_EEPROM24XX_TIMEOUT_MS) == HAL_OK)
    {
        g_last_hal_error = 0u;
        return INT_EEPROM_OK;
    }

    g_last_hal_error = HAL_I2C_GetError(&INT_EEPROM24XX_I2C_HANDLE);
    return INT_EEPROM_ERR_IO;
}

int32_t Int_EEPROM24xx_Init(void)
{
    if ((INT_EEPROM24XX_PAGE_SIZE == 0u) || (INT_EEPROM24XX_MEM_SIZE_BYTES == 0u))
    {
        return INT_EEPROM_ERR_ARG;
    }

    if ((INT_EEPROM24XX_MEM_ADDR_SIZE != 1u) && (INT_EEPROM24XX_MEM_ADDR_SIZE != 2u))
    {
        return INT_EEPROM_ERR_ARG;
    }

    g_inited = 1u;
    g_last_hal_error = 0u;
    return INT_EEPROM_OK;
}

int32_t Int_EEPROM24xx_Read(uint32_t mem_addr, uint8_t *buf, uint16_t len)
{
    int32_t st;

    if (g_inited == 0u)
    {
        return INT_EEPROM_ERR_NOT_INIT;
    }

    if (buf == 0)
    {
        return INT_EEPROM_ERR_ARG;
    }

    st = Int_EEPROM24xx_CheckRange(mem_addr, len);
    if (st != INT_EEPROM_OK)
    {
        return st;
    }

    if (HAL_I2C_Mem_Read(&INT_EEPROM24XX_I2C_HANDLE,
                         INT_EEPROM24XX_DEV_ADDR,
                         (uint16_t)mem_addr,
                         Int_EEPROM24xx_GetMemAddrSizeHal(),
                         buf,
                         len,
                         INT_EEPROM24XX_TIMEOUT_MS) == HAL_OK)
    {
        g_last_hal_error = 0u;
        return INT_EEPROM_OK;
    }

    g_last_hal_error = HAL_I2C_GetError(&INT_EEPROM24XX_I2C_HANDLE);
    return INT_EEPROM_ERR_IO;
}

int32_t Int_EEPROM24xx_Write(uint32_t mem_addr, const uint8_t *buf, uint16_t len)
{
    uint16_t written = 0u;

    if (g_inited == 0u)
    {
        return INT_EEPROM_ERR_NOT_INIT;
    }

    if (buf == 0)
    {
        return INT_EEPROM_ERR_ARG;
    }

    if (Int_EEPROM24xx_CheckRange(mem_addr, len) != INT_EEPROM_OK)
    {
        return INT_EEPROM_ERR_RANGE;
    }

    while (written < len)
    {
        uint32_t cur_addr = mem_addr + (uint32_t)written;
        uint16_t page_off = (uint16_t)(cur_addr % INT_EEPROM24XX_PAGE_SIZE);
        uint16_t room = (uint16_t)(INT_EEPROM24XX_PAGE_SIZE - page_off);
        uint16_t remain = (uint16_t)(len - written);
        uint16_t chunk = (remain < room) ? remain : room;

        if (HAL_I2C_Mem_Write(&INT_EEPROM24XX_I2C_HANDLE,
                              INT_EEPROM24XX_DEV_ADDR,
                              (uint16_t)cur_addr,
                              Int_EEPROM24xx_GetMemAddrSizeHal(),
                              (uint8_t *)&buf[written],
                              chunk,
                              INT_EEPROM24XX_TIMEOUT_MS) != HAL_OK)
        {
            g_last_hal_error = HAL_I2C_GetError(&INT_EEPROM24XX_I2C_HANDLE);
            return INT_EEPROM_ERR_IO;
        }

        if (INT_EEPROM24XX_WRITE_CYCLE_MS > 0u)
        {
            HAL_Delay(INT_EEPROM24XX_WRITE_CYCLE_MS);
        }

        if (Int_EEPROM24xx_WaitReady() != INT_EEPROM_OK)
        {
            return INT_EEPROM_ERR_IO;
        }

        written = (uint16_t)(written + chunk);
    }

    return INT_EEPROM_OK;
}

uint32_t Int_EEPROM24xx_GetLastHalError(void)
{
    return g_last_hal_error;
}
