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


#include <eCxx/eCxx.h>

#include "../AVR_Common.h"


//
// Check the MCU
//
#ifdef __AVR_ARCH_X__
#error "this test application is meant for AVR platform only"
#endif

#ifndef ECXX_AVR_HAVE_HARDWARE_USB
#warning "the UART configuration in this test application is meant only for AVR MCU with hardware USB device module"
#endif


//
// The init function
//
void eCxx_user_init()
{
    // Initialize application
    initBasicAVRApp();

#ifdef ECXX_AVR_HAVE_HARDWARE_USB
    // Initialize UART
    // # On an AVR MCU with hardware USB device module, the Application::uart() function is actually
    //   a shortcut to the *Application::vuart0() function. Therefore, it returns an an instance
    //   of VirtualUART object (instead of the usual UART object).
    // # The virtual UART implementation ignores all UART settings, so here zero can be used as the
    //   baudrate.
    // # Both virtual UART0 and virtual UART1 are automatically initialized by the USB driver.
    //   The function call below is only to wait until the USB initialization process has completed
    //   and to discard possible garbage in the RX buffer.
    // # The implementation will reset the MCU if the host PC opens the virtual UART0 at the magic
    //   baudrate of 1200 bps; opening virtual UART1 will not reset the MCU.
    App::vuart0()->begin(0);
    App::vuart1()->begin(0);
#endif

#if 0
    // Test magic-baudrate callback
    App::vuart0MagicBaudrateCallback( +[]() {
        // Get the settings
        uint32_t baudrate;
        uint8_t  charSize, parity, stopBits;
        bool     rts, dtr;
        App::vuart0GetSettings(baudrate, charSize, parity, stopBits);
        App::vuart0GetRtsDtrState(rts, dtr);
        // Print the settings to the other VUART
        App::vuart0()->write      ('\n'             );
        App::vuart0()->writeString("V-UART #0 : br="); App::vuart0()->writeDecString(baudrate);
        App::vuart0()->writeString(" char-size="    ); App::vuart0()->writeDecString(charSize);
        App::vuart0()->writeString(" parity="       ); App::vuart0()->writeDecString(parity  );
        App::vuart0()->writeString(" stop-bits="    ); App::vuart0()->writeDecString(stopBits);
        App::vuart0()->writeString(" rts="          ); App::vuart0()->writeDecString(rts     );
        App::vuart0()->writeString(" dtr="          ); App::vuart0()->writeDecString(dtr     );
        App::vuart0()->write      ('\n'             );
    } );

    // Process event
    for(;;) App::processEvent();
#endif

#if 0
    // Test change-settings callbacks
    App::vuart0ChangeSettingsCallback( +[]() {
        // Get the settings
        uint32_t baudrate;
        uint8_t  charSize, parity, stopBits;
        bool     rts, dtr;
        App::vuart0GetSettings(baudrate, charSize, parity, stopBits);
        App::vuart0GetRtsDtrState(rts, dtr);
        // Print the settings to the other VUART
        App::vuart1()->write      ('\n'             );
        App::vuart1()->writeString("V-UART #0 : br="); App::vuart1()->writeDecString(baudrate);
        App::vuart1()->writeString(" char-size="    ); App::vuart1()->writeDecString(charSize);
        App::vuart1()->writeString(" parity="       ); App::vuart1()->writeDecString(parity  );
        App::vuart1()->writeString(" stop-bits="    ); App::vuart1()->writeDecString(stopBits);
        App::vuart1()->writeString(" rts="          ); App::vuart1()->writeDecString(rts     );
        App::vuart1()->writeString(" dtr="          ); App::vuart1()->writeDecString(dtr     );
        App::vuart1()->write      ('\n'             );
    } );

    App::vuart1ChangeSettingsCallback( +[]() {
        // Get the settings
        uint32_t baudrate;
        uint8_t  charSize, parity, stopBits;
        bool     rts, dtr;
        App::vuart1GetSettings(baudrate, charSize, parity, stopBits);
        App::vuart1GetRtsDtrState(rts, dtr);
        // Print the settings to the other VUART
        App::vuart0()->write      ('\n'             );
        App::vuart0()->writeString("V-UART #1 : br="); App::vuart0()->writeDecString(baudrate);
        App::vuart0()->writeString(" char-size="    ); App::vuart0()->writeDecString(charSize);
        App::vuart0()->writeString(" parity="       ); App::vuart0()->writeDecString(parity  );
        App::vuart0()->writeString(" stop-bits="    ); App::vuart0()->writeDecString(stopBits);
        App::vuart0()->writeString(" rts="          ); App::vuart0()->writeDecString(rts     );
        App::vuart0()->writeString(" dtr="          ); App::vuart0()->writeDecString(dtr     );
        App::vuart0()->write      ('\n'             );
    } );

    // Process event
    for(;;) App::processEvent();
#endif
}


//
// The task function
//
void eCxx_user_task()
{
    // Process event
    App::processEvent();

#ifdef ECXX_AVR_HAVE_HARDWARE_USB

    // Read from UART and write it back as it is
    auto& vuart0 = *App::vuart0();
    if( vuart0.canRead() ) {
        eCxx::uint8_t c;
        vuart0.read(c);
#ifndef ECXX_AVR_VUART_DUMP_SETTINGS
        vuart0.write(c);
#else
        vuart0._dumpSettings();
#endif
    }

    // Read from UART and write it back as a hexadecimal value
    auto& vuart1 = *App::vuart1();
    if( vuart1.canRead() ) {
        eCxx::uint8_t c;
        vuart1.read(c);
#ifndef ECXX_AVR_VUART_DUMP_SETTINGS
        vuart1.write('0');
        vuart1.write('x');
        vuart1.writeHexSeqString(c, true, false);
        vuart1.write('\n');
#else
        vuart1._dumpSettings();
#endif
    }

#endif
}
