#pragma once

#include "BKMPIDevice.h"
#include <condition_variable>
#include <thread>
#include <mutex>


constexpr auto TTY_RX_RDY  = (1 << 7);  // флаг состояния приёмника, бит 7 RX_CSR
constexpr auto TTY_RX_INT  = (1 << 6);  // разрешение прерывания, бит 6 RX_CSR
constexpr auto TTY_RX_BRK  = 1;         // бит 0 RX_CSR
constexpr auto TTY_RX_OVRFL = (1 << 12); // флаг ошибки переполнения, бит 12 RX_CSR
constexpr auto TTY_RX_PER  = (1 << 15); // флаг ошибки в посылке, бит 15 RX_CSR
constexpr auto TTY_TX_RDY  = (1 << 7);  // флаг состояния передатчика, бит 7 TX_CSR
constexpr auto TTY_TX_INT  = (1 << 6);  // разрешение прерывания, бит 6 TX_CSR
constexpr auto TTY_TX_BRK  = 1;         // разрыв линии, бит 0 TX_CSR
constexpr auto TTY_TX_LOOP = (1 << 2);  // проверка работы, бит 2 TX_CSR


class RCSR
{
		std::mutex m;
		uint16_t v{0};
	public:
		RCSR() = default;
		~RCSR() = default;
		void setValue(const uint16_t val)
		{
			std::lock_guard<std::mutex> lk(m);
			v = val;
		}

		uint16_t getValue()
		{
			std::lock_guard<std::mutex> lk(m);
			return v;
		}

		uint16_t getBit(const uint16_t mask)
		{
			std::lock_guard<std::mutex> lk(m);
			return v & mask;
		}

		void setBit(const uint16_t mask, const uint16_t bit)
		{
			std::lock_guard<std::mutex> lk(m);
			v &= ~mask;
			v |= (bit & mask);
		}

		void setBit(const uint16_t bit)
		{
			std::lock_guard<std::mutex> lk(m);
			v |= bit;
		}

		void clrBit(const uint16_t mask)
		{
			std::lock_guard<std::mutex> lk(m);
			v &= ~mask;
		}
};



class CIRPS : public CBKMPIDevice
{
	protected:
		uint16_t            RX_CSR{0}; // адрес регистра состояния приёмника (РСПР) доступ - чтение/запись
		uint16_t            RX_DAT{0}; // адрес буферного регистра приёмника (БРПР) доступ - чтение, запись - игнорируется, но ошибки не вызывает
		uint16_t            TX_CSR{0}; // адрес регистра состояния передатчика (РСПЕР) доступ - чтение/запись
		uint16_t            TX_DAT{0}; // адрес буферного регистра передатчика (БРПЕР) доступ - чтение/запись.
		uint16_t            RX_VEC{0}; // вектор прерывания приёмника
		uint16_t            TX_VEC{0}; // вектор прерывания передатчика

		// RX
		uint8_t             m_chRXData{ 0 };    // принятые данные
		RCSR                m_wRXcsr;           // регистр управления приёмником

		bool                m_bRX_ParityCheck{ true };  // true - проверять чётность, false - нет
		bool                m_bRX_PEV{ true };          // true - контроль чётных битов, false - контроль нечётных.

		// TX
		uint8_t             m_chTXData{ 0 };    // данные для передачи
		RCSR                m_wTXcsr;           // регистр управления передатчиком

		void               _int_ReceiveData(const uint8_t data);
		void                TXFunc();           // функция передачи символа

		// потоки ввода-вывода
		std::mutex          m_mutTXThreadWork;  // событие, означающее, что поток работает
		bool                m_bStopTXThread{ false };    // событие, что пора завершить работу потока
		bool                m_bTXTrigger{ false };       // флаг запуска потока
		std::thread         m_TXthr;            // поток выдачи данных из очереди
		void                TXThreadFunc();     // собственно функция
		std::condition_variable m_TXcv;         // условная переменная, запуск потока
		std::mutex          m_TXmutex;          // мутекс условной переменной

		std::mutex          m_mutRXThreadWork;  // событие, означающее, что поток работает
		bool                m_bStopRXThread{ false };    // событие, что пора завершить работу потока
		std::thread         m_RXthr;            // поток выдачи данных из очереди
		void                RXThreadFunc();     // собственно функция
		std::mutex          m_RXmutex;          // мутекс условной переменной
		std::mutex          m_mutSafeDelay;

		bool                StartThreads();
		void                StopThreads();

		HANDLE              m_hPort{ INVALID_HANDLE_VALUE };  // хэндл открытого порта, куда отображается ВП1-65
		HANDLE              OpenPort(const CString &strCOM);  // открытие порта
		bool                ByteIn(uint8_t *data) const;
		bool                ByteOut(const uint8_t data) const;
		void                ClosePort(HANDLE &Port);    // закрытие порта

		LARGE_INTEGER       m_liDelayMKS{ 0 };
		void                CalcDelay();
		void                MakeDelay();
		HANDLE              m_hWaitableTimer{ nullptr };
		bool                CreateTimer();
		void                DeleteTimer();

	public:
		CIRPS(CPlatform *p);
		virtual ~CIRPS() override;
		virtual bool        InitialSetDevice() override;

		virtual BK_DEV_MPI  GetBoardModel() const override
		{
			return BK_DEV_MPI::IRPS;
		}

		void                Configure(IRPS_MODE nMode);
		void                Reconfigure();
		// Virtual method called after reset command
		virtual void        OnReset() override;

		// Methods for Set/Get byte/word
		virtual bool        GetByteT(const uint16_t addr, uint8_t *pValue, int &nTC) override;
		virtual bool        GetWordT(const uint16_t addr, uint16_t *pValue, int &nTC) override;
		virtual bool        SetByteT(const uint16_t addr, uint8_t value, int &nTC) override;
		virtual bool        SetWordT(const uint16_t addr, uint16_t value, int &nTC) override;

		virtual bool        GetByteIndirect(const uint16_t addr, uint8_t *pValue) override;
		virtual bool        GetWordIndirect(const uint16_t addr, uint16_t *pValue) override;
		virtual bool        CheckReadWord(const uint16_t addr) override;
		virtual bool        CheckWriteWord(const uint16_t addr) override;

		void                ReceiveData(const uint8_t data);

};

