/*
 * 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_AVR_UART_H
#define ECXX_PLATFORM_AVR_UART_H


namespace eCxx {


//
// A simple AVR 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 {
            //       ..PPSCC.
            _8N1 = 0b00000110, // Format: no   parity ; one stop bit  ; 8-bit character size
            _8N2 = 0b00001110, // Format: no   parity ; two stop bits ; 8-bit character size
            _8E1 = 0b00100110, // Format: even parity ; one stop bit  ; 8-bit character size
            _8E2 = 0b00101110, // Format: even parity ; two stop bits ; 8-bit character size
            _8O1 = 0b00110110, // Format: odd  parity ; one stop bit  ; 8-bit character size
            _8O2 = 0b00111110  // Format: odd  parity ; two stop bits ; 8-bit character size
        };

    public:
        inline ~UART() = default;

    public:
        void begin(uint32_t baudrate, DataFormat dataFormat = DataFormat::_8N1);
        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();

    private:
        volatile uint8_t* const _UBRRH;
        volatile uint8_t* const _UBRRL;
        volatile uint8_t* const _UCSRA;
        volatile uint8_t* const _UCSRB;
        volatile uint8_t* const _UCSRC;
        volatile uint8_t* const _UDR;

        RingBuffer<uint8_t, Application_Base::UARTBufferSize> _rxBuff;
        RingBuffer<uint8_t, Application_Base::UARTBufferSize> _txBuff;
        bool                                                  _txAtLeastOnce;

    private:
        // Construct an UART object using the given SFRs
        inline UART(
            volatile uint8_t* UBRRH_, volatile uint8_t* UBRRL_,
            volatile uint8_t* UCSRA_, volatile uint8_t* UCSRB_,
            volatile uint8_t* UCSRC_, volatile uint8_t* UDR_
        );

        // Interrupt handlers
        inline void rxComplete_isr();
        inline void udrEmpty_isr();

    private:
        friend inline UART* uart0_instance();
        friend inline void uart0_rxComplete_isr();
        friend inline void uart0_udrEmpty_isr();

        friend inline UART* uart1_instance();
        friend inline void uart1_rxComplete_isr();
        friend inline void uart1_udrEmpty_isr();

        friend inline UART* uart2_instance();
        friend inline void uart2_rxComplete_isr();
        friend inline void uart2_udrEmpty_isr();

        friend inline UART* uart3_instance();
        friend inline void uart3_rxComplete_isr();
        friend inline void uart3_udrEmpty_isr();
};


} // namespace eCxx


#endif
