/*
 * @Author: mypx
 * @Email: mypx_coder@163.com
 * @Date: 2025-06-19 15:35:35
 * @LastEditors: mypx mypx_coder@163.com
 * @Description: 
 */
#include <stdio.h>
#include <stdint.h>
#include "at24cx.h"

// Log tag
static const char *TAG = "at24c";

// Debug macro for logging messages
#define AT24_DBG(fmt, ...)                    \
    if (device && device->log)                \
    {                                         \
        device->log(TAG, fmt, ##__VA_ARGS__); \
    }

// Define the chip information table
static const at24cx_chip_info_t chip_info_table[] = {
    {0x0000, 0x007F,  8}, // AT24C01
    {0x0000, 0x00FF,  8}, // AT24C02
    {0x0000, 0x01FF,  9}, // AT24C04
    {0x0000, 0x03FF, 10}, // AT24C08
    {0x0000, 0x3FFF, 16}  // AT24C128
};

// Check if the address is within the valid range
static int check_address_range(at24cx_dev_t *device, uint16_t addr, uint16_t num)
{
    const at24cx_chip_info_t *info = &chip_info_table[device->chip_model];
    if (addr < info->min_addr || addr + num - 1 > info->max_addr)
    {
        AT24_DBG("Address out of range: min = %04X, max = %04X, requested = %04X - %04X",
                 info->min_addr, info->max_addr, addr, addr + num - 1);
        return -1;
    }
    return 0;
}

// Write a single byte to the specified address
int at24cx_write_byte(at24cx_dev_t *device, uint16_t addr, uint8_t data)
{
    if (check_address_range(device, addr, 1) != 0)
    {
        return -1;
    }
    if (device != NULL && device->write_bytes)
    {
        return device->write_bytes((uint16_t)device->dev_addr, addr, &data, 1);
    }
    return -1;
}

// Write multiple bytes to the device, supporting cross-page writing
int at24cx_write(at24cx_dev_t *device, uint16_t start_addr, uint8_t *data_buf, uint16_t write_num)
{
    if (check_address_range(device, start_addr, write_num) != 0)
    {
        return -1;
    }
    uint16_t bytes_written = 0;
    uint16_t current_addr  = start_addr;
    uint8_t *current_data  = data_buf;

    // Loop until all data is written
    while (bytes_written < write_num)
    {
        // Calculate the number of bytes that can be written to the current page
        uint16_t bytes_to_write = AT24_MAX_PAGE_SIZE - (current_addr % AT24_MAX_PAGE_SIZE);
        if (bytes_to_write > write_num - bytes_written)
        {
            bytes_to_write = write_num - bytes_written;
        }

        if (device != NULL && device->write_bytes)
        {
            int ret = device->write_bytes(device->dev_addr, current_addr, current_data, bytes_to_write);
            if (ret != 0)
            {
                AT24_DBG("Failed to write %d bytes at address %04X", bytes_to_write, current_addr);
                return -1;
            }
        }
        else
        {
            return -1;
        }

        // Update the number of written bytes, current address, and data pointer
        bytes_written += bytes_to_write;
        current_addr  += bytes_to_write;
        current_data  += bytes_to_write;
    }

    return 0;
}

// Read a single byte from the specified register
int at24cx_read_byte(at24cx_dev_t *device, uint16_t reg, uint8_t *data)
{
    if (check_address_range(device, reg, 1) != 0)
    {
        return -1;
    }
    if (device != NULL && device->read_bytes)
    {
        int ret = device->read_bytes(device->dev_addr, reg, data, 1);
        if (ret == 0)
        {
            return 0;
        }
        AT24_DBG("Failed to read byte at register %02X", reg);
    }
    return -1;
}

// Read multiple bytes from the device
int at24cx_read(at24cx_dev_t *device, uint16_t start_addr, uint8_t *data_buf, uint16_t read_num)
{
    if (check_address_range(device, start_addr, read_num) != 0)
    {
        return -1;
    }
    if (device != NULL && device->read_bytes)
    {
        int ret = device->read_bytes(device->dev_addr, start_addr, data_buf, read_num);
        if (ret == 0)
        {
            return 0;
        }
        AT24_DBG("Failed to read %d bytes from register %02X", read_num, start_addr);
    }
    return -1;
}
