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


//
// Instantiate the data-stream sampler
//
static eCxx::StreamSampler rbs;


//
// Helper functions
//
static void initAndRunStreamSampler()
{
    // For convenience
    App::uart().begin(9600);
    auto& uart = App::uart();

    // Initialize the data-stream sampler
    eCxx::StreamSampler::TimingSpecification tspec;

#if 1
    tspec.endBlockTimeoutMS =   30;
    tspec.noiseTimeUS       =  100;

    tspec.minLTimeUS0       =  453;
    tspec.minLTimeUS1       =  679;
    tspec.minHTimeUS0       = 1549;
    tspec.minHTimeUS1       = 2323;

    tspec.maxLTimeUS0       = 1579;
    tspec.maxLTimeUS1       = 2367;
    tspec.maxHTimeUS0       = 2074;
    tspec.maxHTimeUS1       = 3110;

    tspec.minLTimeBits      =    1;
    tspec.minHTimeBits      =    3;
    tspec.maxLTimeBits      =    3;
    tspec.maxHTimeBits      =    4;

    tspec.maxBitCount       =   48;
#endif

    rbs.begin(RECV, tspec);

    // Perform calibration because if the data-stream sampler does not have a valid timing specification
    //
    // # Calibration performed in different platform (AVR vs NodeMCU) would result in different
    //   timing specification.
    // # It is recommended to do the calibration using platform with the lowest clock speed (AVR)
    //   so that the resulting timing specification would produce the same result in all platforms.
    //
    if( !rbs.timingSpecification().valid() ) {
        eCxx::StreamSampler::CalibrationSetting cs;

        uart.write('\n');
        uart.writeString( PGM_STR("Waiting for calibration ... ") );

        if( !rbs.calibrate(cs) ) {
            uart.writeStringln( PGM_STR("FAILED\nHalting here!\n" ) );
            App::halt();
        }
        else {
            uart.writeStringln( PGM_STR("SUCCESS\n") );
        }
    }

    // Print the timing specification
    const eCxx::StreamSampler::TimingSpecification& ts = rbs.timingSpecification();

    uart.writeStringln( PGM_STR("--------------------") );
    uart.writeStringln( PGM_STR("Timing Specification") );
    uart.writeStringln( PGM_STR("--------------------") );
    uart.writeString  ( PGM_STR("endBlockTimeoutMS : ") ); uart.writeDecStringln(ts.endBlockTimeoutMS);
    uart.writeString  ( PGM_STR("noiseTimeUS       : ") ); uart.writeDecStringln(ts.noiseTimeUS);
    uart.write        ('\n');
    uart.writeString  ( PGM_STR("minLTimeUS0       : ") ); uart.writeDecStringln(ts.minLTimeUS0);
    uart.writeString  ( PGM_STR("minLTimeUS1       : ") ); uart.writeDecStringln(ts.minLTimeUS1);
    uart.writeString  ( PGM_STR("minHTimeUS0       : ") ); uart.writeDecStringln(ts.minHTimeUS0);
    uart.writeString  ( PGM_STR("minHTimeUS1       : ") ); uart.writeDecStringln(ts.minHTimeUS1);
    uart.write        ('\n');
    App::yield();
    uart.writeString  ( PGM_STR("maxLTimeUS0       : ") ); uart.writeDecStringln(ts.maxLTimeUS0);
    uart.writeString  ( PGM_STR("maxLTimeUS1       : ") ); uart.writeDecStringln(ts.maxLTimeUS1);
    uart.writeString  ( PGM_STR("maxHTimeUS0       : ") ); uart.writeDecStringln(ts.maxHTimeUS0);
    uart.writeString  ( PGM_STR("maxHTimeUS1       : ") ); uart.writeDecStringln(ts.maxHTimeUS1);
    uart.write        ('\n');
    uart.writeString  ( PGM_STR("minLTimeBits      : ") ); uart.writeDecStringln(ts.minLTimeBits);
    uart.writeString  ( PGM_STR("minHTimeBits      : ") ); uart.writeDecStringln(ts.minHTimeBits);
    uart.writeString  ( PGM_STR("maxLTimeBits      : ") ); uart.writeDecStringln(ts.maxLTimeBits);
    uart.writeString  ( PGM_STR("maxHTimeBits      : ") ); uart.writeDecStringln(ts.maxHTimeBits);
    uart.write        ('\n');
    uart.writeString  ( PGM_STR("maxBitCount       : ") ); uart.writeDecStringln(ts.maxBitCount);
    uart.write        ('\n');
    App::yield();

    uart.writeStringln( PGM_STR("Ready for data-stream sampling ...\n") );

#if 1
    // Receive data-stream sample
    //
    // # With the same timing specification, this should produce the same result both in AVR and NodeMCU.
    // # Examples of samples received from an IR remote for an RGB LED controller/driver:
    //       Red     ==> 48 : 00001000 10001000 01000100 00100010 00100010 00100000
    //       Green   ==> 48 : 00001000 10001000 10001000 01000100 01000100 01000000
    //       Blue    ==> 48 : 00010001 00010001 00001000 10001000 10001000 10001000
    //       White   ==> 48 : 00010001 00010001 00010001 00010001 00010001 00010000
    //      *Repeat* ==> 08 : 00010000
    //
    rbs.receive(
        [](const eCxx::uint8_t* buff, eCxx::uint8_t bitCount)
        {
            App::uart().writeDecString(
                bitCount,
                eCxx::Convert::NumSign::None_Minus,
                2,
                eCxx::Convert::NumPad::Zero,
                false
            );
            App::uart().write(' ');
            App::uart().write(':');
            App::uart().write(' ');

            for(eCxx::uint8_t i = 0; i < (bitCount / 8); ++i) {
                App::uart().writeBinString(
                    *buff++,
                    eCxx::Convert::NumSign::None_Minus,
                    8,
                    eCxx::Convert::NumPad::Zero,
                    eCxx::Convert::NumFormat::Uppercase
                );
                App::uart().write(' ');
            }

            App::uart().write('\n');
        },
        eCxx::ActiveState::Low
    );
#endif
}
