#ifndef LINUX_SPI_SPICOMIF_H_
#define LINUX_SPI_SPICOMIF_H_

#include <fsfw/devicehandlers/DeviceCommunicationIF.h>
#include <fsfw/objectmanager/SystemObject.h>
#include <linux/gpio/GpioIF.h>
#include <linux/spi/spiDefinitions.h>
#include <returnvalues/classIds.h>

#include <vector>
#include <unordered_map>

/**
 * @brief   Encapsulates access to linux SPI driver for FSFW objects
 * @details
 * Right now, only full-duplex SPI is supported.
 * @author  R. Mueller
 */
class SpiComIF: public DeviceCommunicationIF, public SystemObject {
public:
    static constexpr uint8_t spiRetvalId = CLASS_ID::LINUX_SPI_COM_IF;
    static constexpr ReturnValue_t OPENING_FILE_FAILED =
            HasReturnvaluesIF::makeReturnCode(spiRetvalId, 0);
    /* Full duplex (ioctl) transfer failure */
    static constexpr ReturnValue_t FULL_DUPLEX_TRANSFER_FAILED =
            HasReturnvaluesIF::makeReturnCode(spiRetvalId, 1);
    /* Half duplex (read/write) transfer failure */
    static constexpr ReturnValue_t HALF_DUPLEX_TRANSFER_FAILED =
            HasReturnvaluesIF::makeReturnCode(spiRetvalId, 2);

    SpiComIF(object_id_t objectId, GpioIF* gpioComIF);

    ReturnValue_t initializeInterface(CookieIF * cookie) override;
    ReturnValue_t sendMessage(CookieIF *cookie,const uint8_t *sendData,
            size_t sendLen) override;
    ReturnValue_t getSendSuccess(CookieIF *cookie) override;
    ReturnValue_t requestReceiveMessage(CookieIF *cookie,
            size_t requestLen) override;
    ReturnValue_t readReceivedMessage(CookieIF *cookie, uint8_t **buffer,
            size_t *size) override;
private:

    struct SpiInstance {
        std::vector<uint8_t> replyBuffer;
    };

    GpioIF* gpioComIF = nullptr;

    MutexIF* spiMutex = nullptr;
    MutexIF::TimeoutType timeoutType = MutexIF::TimeoutType::WAITING;
    uint32_t timeoutMs = 20;

    using SpiDeviceMap = std::unordered_map<address_t, SpiInstance>;
    using SpiDeviceMapIter = SpiDeviceMap::iterator;

    SpiDeviceMap spiDeviceMap;


    ReturnValue_t getReadBuffer(address_t spiAddress, uint8_t** buffer);
    void setSpiSpeedAndMode(int spiFd, spi::SpiModes mode, uint32_t speed);
};

#endif /* LINUX_SPI_SPICOMIF_H_ */