/*! \file pigpio.h
 * \ingroup IO
 * \~\brief
 * \~english GPIO support.
 * \~russian Поддержка GPIO.
 */
/*
    PIP - Platform Independent Primitives
    GPIO
    Andrey Bychkov work.a.b@yandex.ru, Ivan Pelipenko peri4ko@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 PIGPIO_H
#define PIGPIO_H

#include "pithread.h"


//! \~\ingroup IO
//! \~\brief
//! \~english GPIO support library.
//! \~russian Библиотека поддержки GPIO.
//! \~\details
//! \~english The PIGPIO class provides functions for GPIO pin initialization, reading, writing, and monitoring.
//! \~russian Класс PIGPIO предоставляет функции для инициализации, чтения, записи и мониторинга GPIO пинов.
class PIP_EXPORT PIGPIO: public PIThread {
	PIOBJECT_SUBCLASS(PIGPIO, PIThread);

public:
	//! \~english Work mode for pin.
	//! \~russian Режим работы пина.
	//! \~\sa initPin(), pinSet()
	enum Direction {
		In /** \~english Input direction (read) \~russian Входной (чтение) */,
		Out /** \~english Output direction (write) \~russian Выходной (запись) */
	};

	//! \~english Returns singleton object of %PIGPIO.
	//! \~russian Возвращает синглтон объекта %PIGPIO.
	//! \~\sa instance()
	static PIGPIO * instance();

	//! \~english Initialize pin "gpio_num" for "dir" mode
	//! \~russian Инициализирует пин "gpio_num" для режима работы "dir"
	void initPin(int gpio_num, Direction dir = PIGPIO::In);

	//! \~english Set pin "gpio_num" value to "value"
	//! \~russian Устанавливает значение пина "gpio_num" в "value"
	void pinSet(int gpio_num, bool value);

	//! \~english Set pin "gpio_num" value to \b true.
	//! \~russian Устанавливает значение пина "gpio_num" в \b true.
	//! \~\sa pinSet()
	void pinHigh(int gpio_num) { pinSet(gpio_num, true); }

	//! \~english Set pin "gpio_num" value to \b false.
	//! \~russian Устанавливает значение пина "gpio_num" в \b false.
	//! \~\sa pinSet()
	void pinLow(int gpio_num) { pinSet(gpio_num, false); }

	//! \~english Returns pin "gpio_num" state
	//! \~russian Возвращает значение пина "gpio_num"
	bool pinState(int gpio_num);

	//! \~english Start watch for pin "gpio_num".
	//! \~russian Начинает наблюдение за пином "gpio_num".
	//! \~\details
	//! \~english This function doesn't affect thread state. Pins watching starts only with \a PIThread::start().
	//! \~russian Этот метод не меняет состояние потока наблюдения. Наблюдение за пинами начинается методом \a PIThread::start().
	void pinBeginWatch(int gpio_num);

	//! \~english End watch for pin "gpio_num".
	//! \~russian Заканчивает наблюдение за пином "gpio_num".
	//! \~\details
	//! \~english This function doesn't affect thread state. Pins watching starts only with \a PIThread::start().
	//! \~russian Этот метод не меняет состояние потока наблюдения. Наблюдение за пинами начинается методом \a PIThread::start().
	void pinEndWatch(int gpio_num);

	//! \~english End watch for all pins.
	//! \~russian Заканчивает наблюдение за всеми пинами.
	//! \~\details
	//! \~english This function doesn't affect thread state. Pins watching starts only with \a PIThread::start().
	//! \~russian Этот метод не меняет состояние потока наблюдения. Наблюдение за пинами начинается методом \a PIThread::start().
	void clearWatch();

	EVENT2(pinChanged, int, gpio_num, bool, new_value);

	//! \events
	//! \{

	//! \fn void pinChanged(int gpio_num, bool new_value)
	//! \~english Raised when pin "gpio_num" state changes to "new_value".
	//! \~russian Вызывается при смене состояния пина "gpio_num" на "new_value".
	//! \~\details
	//! \~\warning
	//! \~english This event is raised only when the thread is started.
	//! \~russian Это событие вызывается только при запущенном потоке.
	//! \~\note
	//! \~english Use \a PIThread::start() to begin watching pins.
	//! \~russian Используйте \a PIThread::start() для начала наблюдения за пинами.

	//! \}

private:
	PIGPIO();
	virtual ~PIGPIO();
	NO_COPY_CLASS(PIGPIO);

	struct PIP_EXPORT GPIOData {
		GPIOData() {
			dir = PIGPIO::In;
			num = fd = -1;
		}
		PIString name;
		int dir;
		int num;
		int fd;
	};
	void exportGPIO(int gpio_num);
	void openGPIO(GPIOData & g);
	bool getPinState(int gpio_num);
	void begin() override;
	void run() override;
	void end() override;

	static PIString GPIOName(int gpio_num);

	PIMap<int, GPIOData> gpio_;
	PIMap<int, bool> watch_state;
	PIMutex mutex;
};


#endif // PIDIR_H
