#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include "m24c16.h"

/**
 * @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];
    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 = M24C16_EEPROM_I2C_ADDRESS | (address >> 8);
    uint8_t buffer[M24C16_EEPROM_PAGE_SIZE + 1];

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

        if (bytes_to_write > length) 
        {
            bytes_to_write = length;
        }
        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(5);

        address += bytes_to_write;
        data += bytes_to_write;
        length -= bytes_to_write;
    }
    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;
    uint8_t sts = M24C16_FAIL;
    uint8_t slave_addr = M24C16_EEPROM_I2C_ADDRESS | (address >> 8);
    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, length) == 0) 
        {
            sts = M24C16_SCS;
        }
    }
    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
}
