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


#ifndef INCLUDE_ECXX_PLATFORM_SPECIFIC_UART_H
#error "this file cannot be included directly"
#endif
#undef INCLUDE_ECXX_PLATFORM_SPECIFIC_UART_H


#ifndef ECXX_PLATFORM_NODEMCU_UART_H
#define ECXX_PLATFORM_NODEMCU_UART_H


namespace eCxx {


//
// Uncomment this to discard the oldest Rx byte instead of the newest one
//
// #define ECXX_NODEMCU_UART_RX_DISCARD_OLDEST


//
// A simple NodeMCU UART wrapper
//
// WARNING : All functions derived from BasicOutputWriter<T> that accept parameters with data types
//           'PGMCharPointer' and 'PGMCharPointerFar' expect the pointers to come from program
//           memory exclusively!
//
// NOTE    : Whenever possible, use the versions that accept near program pointers because those that
//           accept the far program pointers may have more overhead and restrictions.
//
class UART : public BasicOutputWriter<UART> {
    public:
        enum class DataFormat : uint8_t {
            _8N1, // Format: no   parity ; one stop bit  ; 8-bit character size
            _8N2, // Format: no   parity ; two stop bits ; 8-bit character size
            _8E1, // Format: even parity ; one stop bit  ; 8-bit character size
            _8E2, // Format: even parity ; two stop bits ; 8-bit character size
            _8O1, // Format: odd  parity ; one stop bit  ; 8-bit character size
            _8O2  // Format: odd  parity ; two stop bits ; 8-bit character size
        };

    public:
        ~UART();

        void begin(uint32_t baudrate, DataFormat dataFormat = DataFormat::_8N1, bool invertUART0 = false, bool useUART0AltPin = false);
        void end();

        bool changeBaudrate(uint32_t baudrate);

        bool canRead() const;
        bool read(uint8_t& value);
        bool peek(uint8_t& value) const;

        bool canWrite() const;
        bool write(uint8_t value);
        void flush();

        bool rxOverrun() const;
        bool rxError() const;

        uint32_t detectBaudrate(uint16_t timeoutMS = 10000);

        void setDebug(uint8_t uartNo);

        inline uint8_t getDebug() const
        { return _currentUARTDebugNo; }

    private:
        static uint8_t _currentUARTDebugNo;

        uint8_t  _uartNo;
        uint8_t  _uartDebugNo;
        uint32_t _baudrate;
        bool     _uart0AltPin;
        bool     _initialized;

        mutable RingBuffer<uint8_t, 8UL * Application_Base::UARTBufferSize> _rxBuff;
        mutable bool                                                        _rxOverrun;
        mutable bool                                                        _rxError;

    private:
        static constexpr uint8_t UART0 =  0;
        static constexpr uint8_t UART1 =  1;
        static constexpr uint8_t UARTN = -1;

        // Construct an UART object using the given UART number
        UART(uint8_t uartNo);

    private:
        static void uartIgnoreChar(char c);
        static void uart0WriteChar(char c);
        static void uart1WriteChar(char c);

        static void rx_isr(void* arg);

        friend inline UART* uart0_instance();
        friend inline UART* uart1_instance();
};


} // namespace eCxx


#endif
