//! \~\file pispi.h
//! \~\ingroup IO
//! \~\brief
//! \~english SPI device wrapper
//! \~russian Обертка над SPI-устройством
/*
    PIP - Platform Independent Primitives
    SPI
    Andrey Bychkov work.a.b@yandex.ru

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef PISPI_H
#define PISPI_H

#include "piiodevice.h"


//! \~\ingroup IO
//! \~\brief
//! \~english SPI device with configurable speed, word size and clock mode.
//! \~russian SPI-устройство с настраиваемыми скоростью, размером слова и режимом тактирования.
//! \~\details
//! \~english Data is exchanged through \a write(), and received bytes are accumulated for subsequent \a read() calls.
//! \~russian Обмен выполняется через \a write(), а принятые байты накапливаются для последующих вызовов \a read().
class PIP_EXPORT PISPI: public PIIODevice {
	PIIODEVICE(PISPI, "spi");

public:
	//! \~english Constructs an SPI device for "path" with transfer speed "speed_hz".
	//! \~russian Создает SPI-устройство для "path" со скоростью обмена "speed_hz".
	explicit PISPI(const PIString & path = PIString(), uint speed_hz = 1000000, PIIODevice::DeviceMode mode = PIIODevice::ReadWrite);

	//! \~english Destroys the SPI device.
	//! \~russian Уничтожает SPI-устройство.
	virtual ~PISPI();

	//! \~english SPI mode flags.
	//! \~russian Флаги режима SPI.
	enum Parameters {
		ClockInverse    = 0x1 /*! \~english Invert clock polarity \~russian Инвертировать полярность тактового сигнала */,
		ClockPhaseShift = 0x2 /*! \~english Shift sampling phase \~russian Сдвинуть фазу выборки */,
	};

	//! \~english Sets SPI clock speed in hertz.
	//! \~russian Устанавливает частоту SPI в герцах.
	//! \~\details
	//! \~english Configures the SPI clock frequency. The actual frequency may be rounded to the nearest supported value by the hardware.
	//! \~russian Настраивает частоту тактового сигнала SPI. Фактическая частота может быть округлена до ближайшего поддерживаемого значения
	//! оборудованием.
	//! \~\sa speed()
	void setSpeed(uint speed_hz);

	//! \~english Returns SPI clock speed in hertz.
	//! \~russian Возвращает частоту SPI в герцах.
	//! \~\sa setSpeed()
	uint speed() const { return spi_speed; }

	//! \~english Sets bits per transferred word.
	//! \~russian Устанавливает количество бит в передаваемом слове.
	//! \~\sa bits()
	void setBits(uchar bits = 8);

	//! \~english Returns bits per transferred word.
	//! \~russian Возвращает количество бит в передаваемом слове.
	uchar bits() const { return spi_bits; }

	//! \~english Replaces all SPI mode flags with "parameters_".
	//! \~russian Полностью заменяет набор флагов режима SPI на "parameters_".
	void setParameters(PIFlags<PISPI::Parameters> parameters_) { spi_mode = (int)parameters_; }

	//! \~english Enables or disables a single SPI mode flag.
	//! \~russian Включает или выключает отдельный флаг режима SPI.
	//! \~\sa isParameterSet()
	void setParameter(PISPI::Parameters parameter, bool on = true);

	//! \~english Returns whether SPI mode flag "parameter" is enabled.
	//! \~russian Возвращает, включен ли флаг режима SPI "parameter".
	//! \~\sa setParameter()
	bool isParameterSet(PISPI::Parameters parameter) const;

	//! \~english Returns current SPI mode flags.
	//! \~russian Возвращает текущие флаги режима SPI.
	//! \~\sa setParameters()
	PIFlags<PISPI::Parameters> parameters() const { return spi_mode; }

	//! \~english Returns how many received bytes are buffered for \a read().
	//! \~russian Возвращает количество принятых байт, буферизованных для \a read().
	ssize_t bytesAvailable() const override;

protected:
	bool openDevice() override;
	bool closeDevice() override;
	ssize_t readDevice(void * read_to, ssize_t max_size) override;
	ssize_t writeDevice(const void * data, ssize_t max_size) override;

	PIString constructFullPathDevice() const override;
	void configureFromFullPathDevice(const PIString & full_path) override;
	PIPropertyStorage constructVariantDevice() const override;
	void configureFromVariantDevice(const PIPropertyStorage & d) override;
	DeviceInfoFlags deviceInfoFlags() const override { return PIIODevice::Sequential; }

private:
	uint spi_speed;
	uchar spi_mode;
	uchar spi_bits;
	PIByteArray tx_buf, rx_buf;
	PIByteArray recv_buf;
	PRIVATE_DECLARATION(PIP_EXPORT)
};

#endif // PISPI_H
