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


#include <eCxx/eCxx_InputDevice.h>

#include "AVR_Common.h"


//
// Pin numbers
//
static constexpr eCxx::uint8_t ROW_1  =  5;
static constexpr eCxx::uint8_t ROW_2  =  6;
static constexpr eCxx::uint8_t ROW_3  =  7;
static constexpr eCxx::uint8_t ROW_4  =  8;

static constexpr eCxx::uint8_t COL_1  = 19; // Cannot use pin 1 because it is USART_TX
static constexpr eCxx::uint8_t COL_2  =  2;
static constexpr eCxx::uint8_t COL_3  =  3;
static constexpr eCxx::uint8_t COL_4  =  4;

static constexpr eCxx::uint8_t MUX    =  9;

static constexpr eCxx::uint8_t DS     =  5;
static constexpr eCxx::uint8_t SHCP   =  6;
static constexpr eCxx::uint8_t STCP   =  7;


//
// Instantiate the keypad scanner
//
#define KEYPAD_SCANNER_MODE 1

#if KEYPAD_SCANNER_MODE == 1
    static eCxx::KeypadScanner<4, 4, 4> keypadScanner; // Simple keypad
#elif KEYPAD_SCANNER_MODE == 2
    static eCxx::KeypadScanner<2, 2, 4> keypadScanner; // Keypad with decoder & multiplexer
#elif KEYPAD_SCANNER_MODE == 3
    static eCxx::KeypadScanner<2, 2, 4> keypadScanner; // Keypad with SIPO, decoder & multiplexer
#else
    #error "invalid KEYPAD_SCANNER_MODE"
#endif


//
// Define the modifier key and instantiate the keypad debouncer
//
static constexpr eCxx::uint8_t                                    printMask = 0b01110111;
static constexpr eCxx::uint8_t                                    modKey0   = 0x00 | ~printMask;
static constexpr eCxx::uint8_t                                    modKey3   = 0x03 | ~printMask;

static           eCxx::DebounceKeypad<decltype(keypadScanner), 2> dbkp(eCxx::makeArray<eCxx::uint8_t, modKey0, modKey3>());


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

    // Initialize UART
    App::uart().begin(9600);

    // Initialize keypad scanner
#if KEYPAD_SCANNER_MODE == 1
    // Simple keypad
    keypadScanner.begin(
        eCxx::ActiveState::Low,
        eCxx::makeArray<eCxx::uint8_t, ROW_1, ROW_2, ROW_3, ROW_4>(),
        eCxx::makeArray<eCxx::uint8_t, COL_1, COL_2, COL_3, COL_4>(),
        true
    );
#elif KEYPAD_SCANNER_MODE == 2
    // Keypad with decoder & multiplexer
    keypadScanner.begin(
        eCxx::ActiveState::Low,
        eCxx::makeArray<eCxx::uint8_t, ROW_1, ROW_2>(),
        eCxx::makeArray<eCxx::uint8_t, COL_1, COL_2>(),
        MUX
    );
#elif KEYPAD_SCANNER_MODE == 3
    // Keypad with SIPO, decoder & multiplexer
    keypadScanner.begin(
        eCxx::ActiveState::Low,
        DS,
        SHCP,
        STCP,
        MUX
    );
#endif
}


//
// The task function
//
void eCxx_user_task()
{
    // Process event and delay
    static eCxx::uint32_t prvMillis = 0;
           eCxx::uint32_t curMillis = App::processEvent();
    if(curMillis - prvMillis < 10) return;
    else                           prvMillis = curMillis;

    // Perform keypad scanning and debouncing
    static eCxx::uint8_t keys[ decltype(keypadScanner)::NumberOfMultiKeys ];
           eCxx::uint8_t kcnt = keypadScanner.scan(keys);
    dbkp.debounce(keys, kcnt);

    static eCxx::uint8_t stdk[ decltype(dbkp)::NumberOfStdKeys ];
    kcnt = dbkp.getStdKeyPressed(stdk);

    const bool modKey0Pressed = dbkp.modKeyPressed(modKey0);
    const bool modKey3Pressed = dbkp.modKeyPressed(modKey3);

    // Write the pressed key(s) to UART
    if(kcnt) {
        // Write the preamble text
        auto& uart = App::uart();
        uart.writeString( PGM_STR("Pressed: ") );
        uart.flush();
        // Print marker(s) if the modifier key(s) is/are pressed
        if(modKey0Pressed) uart.writeString( PGM_STR("M0+ ") );
        if(modKey3Pressed) uart.writeString( PGM_STR("M3+ ") );
        uart.flush();
        // Print all the pressed standard keys
        for(eCxx::uint8_t i = 0; i < kcnt; ++i) {
            uart.writeHexString(
                static_cast<eCxx::uint8_t>(stdk[i] & printMask),
                eCxx::Convert::NumSign::None_Minus,
                2,
                eCxx::Convert::NumPad::Zero,
                eCxx::Convert::NumFormat::Uppercase
            );
            uart.write(' ');
        }
        uart.write( '\n' );
    }
}
