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


#include <eCxx/eCxx_DDX.h>

#include "AVR_Common.h"
#include "HUB75PinNumber.h"


//
// Instantiate the pin objects
//
const eCxx::PinDDX<PIN_CLK> clk;
const eCxx::PinDDX<PIN_LE > le;
const eCxx::PinDDX<PIN_OE > oe;

const eCxx::PinDDX<PIN_R0 > r0;
const eCxx::PinDDX<PIN_G0 > g0;
const eCxx::PinDDX<PIN_B0 > b0;

const eCxx::PinDDX<PIN_R1 > r1;
const eCxx::PinDDX<PIN_G1 > g1;
const eCxx::PinDDX<PIN_B1 > b1;

const eCxx::PinDDX<PIN_A  > a;
const eCxx::PinDDX<PIN_B  > b;
const eCxx::PinDDX<PIN_C  > c;
const eCxx::PinDDX<PIN_D  > d;


//
// LED panel configuration
//
static constexpr uint8_t ScanMode   = 8; // 16 -> 1/16 ; 8 -> 1/8 ; 4 -> 1/4 ; 2 -> 1/2

static constexpr uint8_t PixelX     = 32;
static constexpr uint8_t PixelY     = 16;

static constexpr uint8_t PixelXMult = PixelY / 2 / ScanMode;


//
// Tracing-related variables
//
static uint8_t curX = 0;
static uint8_t curY = 0;


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

    // Initialize pins
    clk.pinModeOutput(); clk.digitalWrite(false);
    le .pinModeOutput(); le .digitalWrite(false);
    oe .pinModeOutput(); oe .digitalWrite(true ); // This pin is usually active low

    r0 .pinModeOutput(); r0 .digitalWrite(false);
    g0 .pinModeOutput(); g0 .digitalWrite(false);
    b0 .pinModeOutput(); b0 .digitalWrite(false);

    r1 .pinModeOutput(); r1 .digitalWrite(false);
    g1 .pinModeOutput(); g1 .digitalWrite(false);
    b1 .pinModeOutput(); b1 .digitalWrite(false);

                                   a.pinModeOutput(); a.digitalWrite(false);
    __if_constexpr(ScanMode > 2) { b.pinModeOutput(); b.digitalWrite(false); }
    __if_constexpr(ScanMode > 4) { c.pinModeOutput(); c.digitalWrite(false); }
    __if_constexpr(ScanMode > 8) { d.pinModeOutput(); d.digitalWrite(false); }
}


//
// 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 < (1000 / 5) ) return;
    else                                     prvMillis = curMillis;

    // Turn off the LEDs
    oe.digitalWrite(true); // This pin is usually active low

    // Send the address
    // NOTE: It only works
                                 a.digitalWrite(curY & 0b00000001);
    __if_constexpr(ScanMode > 2) b.digitalWrite(curY & 0b00000010);
    __if_constexpr(ScanMode > 4) c.digitalWrite(curY & 0b00000100);
    __if_constexpr(ScanMode > 8) d.digitalWrite(curY & 0b00001000);

    // Send the data bits for all the pixels
    for(uint8_t x = 0; x < (PixelX * PixelXMult); ++x) {
        // Send the data bits
        if(x == curX) {
            r0.digitalWrite(true ); g0.digitalWrite(true ); b0.digitalWrite(false); // Yellow
            r1.digitalWrite(true ); g1.digitalWrite(false); b1.digitalWrite(true ); // Magenta
        }
        else {
            r0.digitalWrite(false); g0.digitalWrite(false); b0.digitalWrite(false);
            r1.digitalWrite(false); g1.digitalWrite(false); b1.digitalWrite(false);
        }
        // Pulse the clock pin
                                 eCxx::asmNOP<1>();
        clk.digitalWrite(true ); eCxx::asmNOP<2>();
        clk.digitalWrite(false); eCxx::asmNOP<1>();
    }

    // Pulse the latch pin
                            eCxx::asmNOP<1>();
    le.digitalWrite(true ); eCxx::asmNOP<2>();
    le.digitalWrite(false); eCxx::asmNOP<1>();

    // Update the coordinates
    ++curX;
    if( curX >= (PixelX * PixelXMult) ) {
        curX = 0;
        ++curY;
        if(curY >= ScanMode) curY = 0;
    }

    // Turn on the LEDs
    oe.digitalWrite(false); // This pin is usually active low
}
