/*
 * Copyright (C) 2020-2021 Aloysius Indrayanto
 *
 * This file is part of the eCxx library, see LICENSE file for the license details.
 */


#ifndef ECXX_PLATFORM_ONE_WIRE_SOFT_UART_H
#define ECXX_PLATFORM_ONE_WIRE_SOFT_UART_H


#include <eCxx/Platform/Common/Application.h>


namespace eCxx {


//
// A simple one-wire software UART implementation
//
// # This class acts similar to the OWIMaster class. However, unlike the OWIMaster class, this class
//   can be used by both master and slave interfaces.
// # Like the one-wire-interface communication system (which is implemented by the OWIMaster class),
//   the data direction of the slave (receive vs transmit) in this one-wire software UART implementation
//   is also controlled by the master (using a pre-determined communication protocol).
// # Unlike the one-wire-interface communication system that utilizes variable pulse width to differentiate
//   between bit 0 and bit 1, this class use a normal UART data frame format.
// # Due to the possibility of transmission clashes, a series resistor between 470R to 4k7 should be put
//   between the master and slave interfaces to prevent short circuit.
// # It is possible to have several slaves by making the connection into a star form. In this case, for
//   every pin that connects to the star's central point, a series resistor should be added. Hence, for one
//   master with two slaves, three series resistors will be needed. Each resistor value should be reduced
//   in half to between 220R to 2k2.
// # Because it is a software UART implementation, higher baudrate would result in a much less reliable data
//   transmission (baudrate of 100000 or less is recommended).
//
class OneWireSoftUART : public Uncopyable {
    public:
        enum class DataFormat : uint8_t {
            //       PP.S..CC
            _8N1 = 0b00000011, // Format: no   parity ; one stop bit  ; 8-bit character size
            _8N2 = 0b00010011, // Format: no   parity ; two stop bits ; 8-bit character size
            _8E1 = 0b10000011, // Format: even parity ; one stop bit  ; 8-bit character size
            _8E2 = 0b10010011, // Format: even parity ; two stop bits ; 8-bit character size
            _8O1 = 0b01000011, // Format: odd  parity ; one stop bit  ; 8-bit character size
            _8O2 = 0b01010011  // Format: odd  parity ; two stop bits ; 8-bit character size
        };

    public:
        OneWireSoftUART();
        ~OneWireSoftUART();

    public:
        // Initialization
        bool begin(uint8_t pin, uint32_t baudrate, DataFormat dataFormat = DataFormat::_8N1);
        void end();

        // Immediate read functions
        // WARNING : Setting timeOutMS to 0 means no timeout!
        bool immRead(uint8_t& value, uint32_t timeOutMS) __ECXX_ATTR_OPTIMIZE_2;
        bool immRead(uint8_t* value, uint8_t count, uint32_t timeOutMS) __ECXX_ATTR_OPTIMIZE_2;

        // Immediate write functions
        void immWrite(uint8_t value) __ECXX_ATTR_OPTIMIZE_2;
        void immWrite(const uint8_t* value, uint8_t count) __ECXX_ATTR_OPTIMIZE_2;

        // Direct access to the pin
        inline Application::_PinFDX& pinFDX() noexcept;
        inline const Application::_PinFDX& pinFDX() const noexcept;

    private:
        static constexpr uint8_t InvalidParity = 255;

        static constexpr uint8_t ParityNone    =   0;
        static constexpr uint8_t ParityOdd     =   1;
        static constexpr uint8_t ParityEven    =   2;

        Application::_PinFDX _pinFDX;      // Fast-digital-read/write

        uint8_t              _parity;
        bool                 _twoStopBits;

        uint16_t             _txBitDelay;  // Delay for Tx bit
        uint16_t             _rxBitDelay;  // Delay for Rx bit
        uint16_t             _rxCtrDelay;  // Delay for centering Rx bit

    private:
        inline bool _calcParity(uint8_t value) __ECXX_ATTR_ALWAYS_INLINE __ECXX_ATTR_OPTIMIZE_2;

        inline bool _recvOneDataFrame(uint8_t& value) __ECXX_ATTR_ALWAYS_INLINE __ECXX_ATTR_OPTIMIZE_2;
        inline void _sendOneDataFrame(uint8_t value, bool parity) __ECXX_ATTR_ALWAYS_INLINE __ECXX_ATTR_OPTIMIZE_2;
};


inline Application::_PinFDX& OneWireSoftUART::pinFDX() noexcept
{ return _pinFDX; }


inline const Application::_PinFDX& OneWireSoftUART::pinFDX() const noexcept
{ return _pinFDX; }


} // namespace eCxx


#endif
