/**
 * @file i2c.hpp
 * @brief I2C wrapper component header - Wrapper for ESP-IDF I2C functionality
 * @author Mahmoud Elmohtady
 * @company Nabd solutions - ASF
 * @copyright Copyright (c) 2025
 */

#ifndef I2C_HPP
#define I2C_HPP

#include <cstdint>
#include "driver/i2c.h"
#include "esp_err.h"

/**
 * @brief I2C port enumeration
 */
enum class I2cPort
{
    PORT_0,
    PORT_1
};

/**
 * @brief I2C mode enumeration
 */
enum class I2cMode
{
    MASTER = I2C_MODE_MASTER,
    SLAVE = I2C_MODE_SLAVE
};

/**
 * @brief I2C configuration structure
 */
struct I2cConfig
{
    I2cMode mode;              ///< I2C mode (master/slave)
    uint32_t sdaPin;           ///< SDA pin number
    uint32_t sclPin;           ///< SCL pin number
    uint32_t clkSpeedHz;       ///< Clock speed in Hz (for master mode)
    bool sdaPullupEnable;      ///< Enable SDA pull-up resistor
    bool sclPullupEnable;      ///< Enable SCL pull-up resistor
    uint8_t slaveAddress;      ///< Slave address (for slave mode)
    uint32_t rxBufferLen;      ///< RX buffer length (for slave mode)
    uint32_t txBufferLen;      ///< TX buffer length (for slave mode)
};

/**
 * @brief I2C wrapper class
 * 
 * Provides a C++ wrapper for ESP-IDF I2C functionality.
 * This class encapsulates ESP-IDF I2C driver functions in an object-oriented interface.
 */
class I2c
{
public:
    /**
     * @brief Constructor
     * @details Initializes the I2C wrapper instance
     */
    I2c();

    /**
     * @brief Destructor
     * @details Cleans up resources and deinitializes all I2C ports
     */
    ~I2c();

    /**
     * @brief Initialize I2C bus
     * @param port I2C port number
     * @param config I2C configuration parameters
     * @return true if initialized successfully, false otherwise
     * @note This function configures pins, clock speed, and mode
     */
    bool initialize(I2cPort port, const I2cConfig& config);

    /**
     * @brief Deinitialize I2C bus
     * @param port I2C port number
     * @return true if deinitialized successfully, false otherwise
     */
    bool deinitialize(I2cPort port);

    /**
     * @brief Write data to I2C device
     * @param port I2C port number
     * @param deviceAddress 7-bit device address
     * @param data Pointer to data buffer
     * @param dataLen Number of bytes to write
     * @param timeoutMs Timeout in milliseconds (default: 1000ms)
     * @return Number of bytes written, or -1 on error
     */
    int32_t write(I2cPort port, uint8_t deviceAddress, const uint8_t* data, size_t dataLen, uint32_t timeoutMs = 1000);

    /**
     * @brief Read data from I2C device
     * @param port I2C port number
     * @param deviceAddress 7-bit device address
     * @param data Pointer to receive buffer
     * @param dataLen Number of bytes to read
     * @param timeoutMs Timeout in milliseconds (default: 1000ms)
     * @return Number of bytes read, or -1 on error
     */
    int32_t read(I2cPort port, uint8_t deviceAddress, uint8_t* data, size_t dataLen, uint32_t timeoutMs = 1000);

    /**
     * @brief Write to register and read from I2C device
     * @param port I2C port number
     * @param deviceAddress 7-bit device address
     * @param regAddress Register address to write
     * @param data Pointer to receive buffer
     * @param dataLen Number of bytes to read
     * @param timeoutMs Timeout in milliseconds (default: 1000ms)
     * @return Number of bytes read, or -1 on error
     */
    int32_t writeRead(I2cPort port, uint8_t deviceAddress, uint8_t regAddress, uint8_t* data, size_t dataLen, uint32_t timeoutMs = 1000);

    /**
     * @brief Write register value to I2C device
     * @param port I2C port number
     * @param deviceAddress 7-bit device address
     * @param regAddress Register address
     * @param regValue Register value
     * @param timeoutMs Timeout in milliseconds (default: 1000ms)
     * @return true if written successfully, false otherwise
     */
    bool writeRegister(I2cPort port, uint8_t deviceAddress, uint8_t regAddress, uint8_t regValue, uint32_t timeoutMs = 1000);

    /**
     * @brief Read register value from I2C device
     * @param port I2C port number
     * @param deviceAddress 7-bit device address
     * @param regAddress Register address
     * @param timeoutMs Timeout in milliseconds (default: 1000ms)
     * @return Register value, or -1 on error
     */
    int32_t readRegister(I2cPort port, uint8_t deviceAddress, uint8_t regAddress, uint32_t timeoutMs = 1000);

    /**
     * @brief Scan I2C bus for devices
     * @param port I2C port number
     * @param devices Array to store found device addresses
     * @param maxDevices Maximum number of devices to find
     * @return Number of devices found
     */
    int32_t scanBus(I2cPort port, uint8_t* devices, size_t maxDevices);

    /**
     * @brief Check if I2C port is initialized
     * @param port I2C port number
     * @return true if initialized, false otherwise
     */
    bool isInitialized(I2cPort port) const;

    /**
     * @brief Get default I2C configuration
     * @return Default I2C configuration structure
     */
    static I2cConfig getDefaultConfig();

private:
    bool m_isInitialized_[2];  ///< Initialization status for each port
    
    /**
     * @brief Convert I2cPort to ESP-IDF i2c_port_t
     * @param port I2C port
     * @return ESP-IDF i2c_port_t
     */
    i2c_port_t convertPort(I2cPort port);
    
    /**
     * @brief Convert I2cConfig to ESP-IDF i2c_config_t
     * @param config I2C configuration
     * @return ESP-IDF i2c_config_t
     */
    i2c_config_t convertConfig(const I2cConfig& config);
};

#endif // I2C_HPP

