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

#ifndef SPI_HPP
#define SPI_HPP

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

/**
 * @brief SPI host enumeration
 */
enum class SpiHost
{
    SPI1_HOST,
    SPI2_HOST,
    SPI3_HOST
};

/**
 * @brief SPI mode enumeration
 */
enum class SpiMode
{
    MODE_0,
    MODE_1,
    MODE_2,
    MODE_3
};

/**
 * @brief SPI bus configuration structure
 */
struct SpiBusConfig
{
    uint32_t mosiPin;          ///< MOSI pin number
    uint32_t misoPin;          ///< MISO pin number
    uint32_t sclkPin;          ///< SCLK pin number
    uint32_t quadwpPin;        ///< Quad SPI write protect pin (optional)
    uint32_t quadhdPin;        ///< Quad SPI hold pin (optional)
    uint32_t maxTransferSize;  ///< Maximum transfer size in bytes
};

/**
 * @brief SPI device configuration structure
 */
struct SpiDeviceConfig
{
    uint32_t csPin;            ///< Chip select pin number
    uint32_t clockSpeedHz;     ///< Clock speed in Hz
    SpiMode mode;              ///< SPI mode (0-3)
    uint32_t queueSize;        ///< Transaction queue size
    uint32_t commandBits;      ///< Command phase bits
    uint32_t addressBits;      ///< Address phase bits
    uint32_t dummyBits;        ///< Dummy phase bits
    bool csEnaPretrans;        ///< CS setup time
    bool csEnaPosttrans;       ///< CS hold time
};

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

    /**
     * @brief Destructor
     * @details Cleans up resources and deinitializes all SPI hosts
     */
    ~Spi();

    /**
     * @brief Initialize SPI bus
     * @param host SPI host number
     * @param config SPI bus configuration
     * @return true if initialized successfully, false otherwise
     * @note This function configures the SPI bus pins and parameters
     */
    bool initializeBus(SpiHost host, const SpiBusConfig& config);

    /**
     * @brief Deinitialize SPI bus
     * @param host SPI host number
     * @return true if deinitialized successfully, false otherwise
     */
    bool deinitializeBus(SpiHost host);

    /**
     * @brief Add SPI device to bus
     * @param host SPI host number
     * @param config SPI device configuration
     * @param deviceHandle Pointer to store device handle
     * @return true if device added successfully, false otherwise
     */
    bool addDevice(SpiHost host, const SpiDeviceConfig& config, spi_device_handle_t* deviceHandle);

    /**
     * @brief Remove SPI device from bus
     * @param deviceHandle Device handle to remove
     * @return true if device removed successfully, false otherwise
     */
    bool removeDevice(spi_device_handle_t deviceHandle);

    /**
     * @brief Transmit and receive data
     * @param deviceHandle Device handle
     * @param txData Pointer to transmit data
     * @param rxData Pointer to receive buffer
     * @param length Number of bytes to transfer
     * @return Number of bytes transferred, or -1 on error
     */
    int32_t transmit(spi_device_handle_t deviceHandle, const uint8_t* txData, uint8_t* rxData, size_t length);

    /**
     * @brief Transmit data only
     * @param deviceHandle Device handle
     * @param txData Pointer to transmit data
     * @param length Number of bytes to transmit
     * @return Number of bytes transmitted, or -1 on error
     */
    int32_t transmitOnly(spi_device_handle_t deviceHandle, const uint8_t* txData, size_t length);

    /**
     * @brief Receive data only
     * @param deviceHandle Device handle
     * @param rxData Pointer to receive buffer
     * @param length Number of bytes to receive
     * @return Number of bytes received, or -1 on error
     */
    int32_t receiveOnly(spi_device_handle_t deviceHandle, uint8_t* rxData, size_t length);

    /**
     * @brief Transmit with command and address phases
     * @param deviceHandle Device handle
     * @param command Command to send
     * @param address Address to send
     * @param txData Pointer to transmit data
     * @param rxData Pointer to receive buffer
     * @param length Number of bytes to transfer
     * @return Number of bytes transferred, or -1 on error
     */
    int32_t transmitWithCmdAddr(spi_device_handle_t deviceHandle, uint32_t command, uint32_t address,
                                const uint8_t* txData, uint8_t* rxData, size_t length);

    /**
     * @brief Check if SPI host is initialized
     * @param host SPI host number
     * @return true if initialized, false otherwise
     */
    bool isInitialized(SpiHost host) const;

    /**
     * @brief Get default bus configuration
     * @return Default SPI bus configuration structure
     */
    static SpiBusConfig getDefaultBusConfig();

    /**
     * @brief Get default device configuration
     * @return Default SPI device configuration structure
     */
    static SpiDeviceConfig getDefaultDeviceConfig();

private:
    bool m_isInitialized_[3];  ///< Initialization status for each host
    
    /**
     * @brief Convert SpiHost to ESP-IDF spi_host_device_t
     * @param host SPI host
     * @return ESP-IDF spi_host_device_t
     */
    spi_host_device_t convertHost(SpiHost host);
    
    /**
     * @brief Convert SpiBusConfig to ESP-IDF spi_bus_config_t
     * @param config SPI bus configuration
     * @return ESP-IDF spi_bus_config_t
     */
    spi_bus_config_t convertBusConfig(const SpiBusConfig& config);
    
    /**
     * @brief Convert SpiDeviceConfig to ESP-IDF spi_device_interface_config_t
     * @param config SPI device configuration
     * @return ESP-IDF spi_device_interface_config_t
     */
    spi_device_interface_config_t convertDeviceConfig(const SpiDeviceConfig& config);
    
    /**
     * @brief Convert SpiMode to ESP-IDF mode flags
     * @param mode SPI mode
     * @return ESP-IDF mode flags
     */
    uint32_t convertMode(SpiMode mode);
};

#endif // SPI_HPP

