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


#ifndef ECXX_PLATFORM_HUB75_PIN_DDX_H
#define ECXX_PLATFORM_HUB75_PIN_DDX_H


namespace eCxx {


//
// A helper struct to instantiate the correct PinDDX for GenericHUB75Driver<T> control pins
//
template <bool ClockInverted, bool LatchInverted, bool OutputEnableInverted, uint8_t ClockDelayN0a, uint8_t ClockDelayN0b, uint8_t ClockDelayN1, typename PinIntegerSequenceT, uint8_t OffsetN>
struct HUB75ControlPin {
    static constexpr uint8_t PinCount = 3;

    static constexpr bool    ClockL   = ClockInverted        ? true  : false;
    static constexpr bool    ClockH   = ClockInverted        ? false : true;

    static constexpr bool    LatchL   = LatchInverted        ? true  : false;
    static constexpr bool    LatchH   = LatchInverted        ? false : true;

    static constexpr bool    OutEnL   = OutputEnableInverted ? true  : false;
    static constexpr bool    OutEnH   = OutputEnableInverted ? false : true;

    const PinDDX< eCxx::get<OffsetN + 0>( PinIntegerSequenceT{} ) > clk;
    const PinDDX< eCxx::get<OffsetN + 1>( PinIntegerSequenceT{} ) > le;
    const PinDDX< eCxx::get<OffsetN + 2>( PinIntegerSequenceT{} ) > oe;

    inline void begin() const __ECXX_ATTR_ALWAYS_INLINE
    {
        clk.pinModeOutput(); clk.digitalWrite(ClockL);
        le .pinModeOutput(); le .digitalWrite(LatchL);
        oe .pinModeOutput(); oe .digitalWrite(OutEnL);
    }

    inline void end() const __ECXX_ATTR_ALWAYS_INLINE
    {
        clk.pinModeInput(false);
        le .pinModeInput(false);
        oe .pinModeInput(false);
    }

    inline void pulseClock() const __ECXX_ATTR_ALWAYS_INLINE
    {
                                  asmNOP<ClockDelayN0a>();
        clk.digitalWrite(ClockH); asmNOP<ClockDelayN1 >();
        clk.digitalWrite(ClockL); asmNOP<ClockDelayN0b>();
    }
};


//
// A helper struct to instantiate the correct PinDDX for GenericHUB75Driver<T> data pin(s)
//
template <HUB75Config::DataPin DataPinE, typename PinIntegerSequenceT, uint8_t OffsetN>
struct HUB75DataPin;

template <typename PinIntegerSequenceT, uint8_t OffsetN>
struct HUB75DataPin<HUB75Config::DataPin::R0_R1_G0_G1_B0_B1, PinIntegerSequenceT, OffsetN> {
    static constexpr uint8_t PinCount = 6;

    const PinDDX< eCxx::get<OffsetN + 0>( PinIntegerSequenceT{} ) > r0;
    const PinDDX< eCxx::get<OffsetN + 1>( PinIntegerSequenceT{} ) > r1;
    const PinDDX< eCxx::get<OffsetN + 2>( PinIntegerSequenceT{} ) > g0;
    const PinDDX< eCxx::get<OffsetN + 3>( PinIntegerSequenceT{} ) > g1;
    const PinDDX< eCxx::get<OffsetN + 4>( PinIntegerSequenceT{} ) > b0;
    const PinDDX< eCxx::get<OffsetN + 5>( PinIntegerSequenceT{} ) > b1;

    inline void begin() const __ECXX_ATTR_ALWAYS_INLINE
    {
        r0.pinModeOutput(); r0.digitalWrite(false);
        r1.pinModeOutput(); r1.digitalWrite(false);
        g0.pinModeOutput(); g0.digitalWrite(false);
        g1.pinModeOutput(); g1.digitalWrite(false);
        b0.pinModeOutput(); b0.digitalWrite(false);
        b1.pinModeOutput(); b1.digitalWrite(false);
    }

    inline void end() const __ECXX_ATTR_ALWAYS_INLINE
    {
        r0.pinModeInput(false);
        r1.pinModeInput(false);
        g0.pinModeInput(false);
        g1.pinModeInput(false);
        b0.pinModeInput(false);
        b1.pinModeInput(false);
    }
};

template <typename PinIntegerSequenceT, uint8_t OffsetN>
struct HUB75DataPin<HUB75Config::DataPin::R01_G01_B01, PinIntegerSequenceT, OffsetN> {
    static constexpr uint8_t PinCount = 3;

    const PinDDX< eCxx::get<OffsetN + 0>( PinIntegerSequenceT{} ) > r01;
    const PinDDX< eCxx::get<OffsetN + 1>( PinIntegerSequenceT{} ) > g01;
    const PinDDX< eCxx::get<OffsetN + 2>( PinIntegerSequenceT{} ) > b01;

    inline void begin() const __ECXX_ATTR_ALWAYS_INLINE
    {
        r01.pinModeOutput(); r01.digitalWrite(false);
        g01.pinModeOutput(); g01.digitalWrite(false);
        b01.pinModeOutput(); b01.digitalWrite(false);
    }

    inline void end() const __ECXX_ATTR_ALWAYS_INLINE
    {
        r01.pinModeInput(false);
        g01.pinModeInput(false);
        b01.pinModeInput(false);
    }
};

template <typename PinIntegerSequenceT, uint8_t OffsetN>
struct HUB75DataPin<HUB75Config::DataPin::RGB0_RGB1, PinIntegerSequenceT, OffsetN> {
    static constexpr uint8_t PinCount = 2;

    const PinDDX< eCxx::get<OffsetN + 0>( PinIntegerSequenceT{} ) > rgb0;
    const PinDDX< eCxx::get<OffsetN + 1>( PinIntegerSequenceT{} ) > rgb1;

    inline void begin() const __ECXX_ATTR_ALWAYS_INLINE
    {
        rgb0.pinModeOutput(); rgb0.digitalWrite(false);
        rgb1.pinModeOutput(); rgb1.digitalWrite(false);
    }

    inline void end() const __ECXX_ATTR_ALWAYS_INLINE
    {
        rgb0.pinModeInput(false);
        rgb1.pinModeInput(false);
    }
};

template <typename PinIntegerSequenceT, uint8_t OffsetN>
struct HUB75DataPin<HUB75Config::DataPin::RGB01, PinIntegerSequenceT, OffsetN> {
    static constexpr uint8_t PinCount = 1;

    const PinDDX< eCxx::get<OffsetN + 0>( PinIntegerSequenceT{} ) > rgb01;

    inline void begin() const __ECXX_ATTR_ALWAYS_INLINE
    { rgb01.pinModeOutput(); rgb01.digitalWrite(false); }

    inline void end() const __ECXX_ATTR_ALWAYS_INLINE
    { rgb01.pinModeInput(false); }
};


//
// A helper struct to instantiate the correct PinDDX for GenericHUB75Driver<T> address pin(s)
//
namespace internal {

    // Helper constants
    static constexpr uint8_t AddressMode_Direct          = 0;
    static constexpr uint8_t AddressMode_SimpleShifter   = 1;
    static constexpr uint8_t AddressMode_SelectorShifter = 2;

    // Helper functions
    static constexpr bool raIsSimpleShifter(HUB75Config::RowAddressing rowAddressing) noexcept
    {
        return (rowAddressing == HUB75Config::RowAddressing::Shift_Clk_Dat  ) ||
               (rowAddressing == HUB75Config::RowAddressing::Shift_nClk_Dat ) ||
               (rowAddressing == HUB75Config::RowAddressing::Shift_Clk_nDat ) ||
               (rowAddressing == HUB75Config::RowAddressing::Shift_nClk_nDat);
    }

    static constexpr bool raIsSelectorShifter(HUB75Config::RowAddressing rowAddressing) noexcept
    {
        return (rowAddressing == HUB75Config::RowAddressing::Shift_Clk_Dat_IEn_Sel  ) ||
               (rowAddressing == HUB75Config::RowAddressing::Shift_Clk_Dat_nIEn_Sel ) ||
               (rowAddressing == HUB75Config::RowAddressing::Shift_Clk_Dat_IEn_nSel ) ||
               (rowAddressing == HUB75Config::RowAddressing::Shift_Clk_Dat_nIEn_nSel);
    }

    static constexpr uint8_t raGetAddressMode(HUB75Config::RowAddressing rowAddressing) noexcept
    {
        return raIsSelectorShifter(rowAddressing) ? AddressMode_SelectorShifter
                                                  : ( raIsSimpleShifter(rowAddressing) ? AddressMode_SimpleShifter
                                                                                       : AddressMode_Direct
                                                    );
    }

    // Prototype template
    template <uint8_t AddressingModeN, uint8_t AddressLineN, bool ClockInverted, uint8_t ClockDelayN0a, uint8_t ClockDelayN0b, uint8_t ClockDelayN1, bool DataInverted, bool InputEnableInverted, bool SelectorInverted, typename PinIntegerSequenceT, uint8_t OffsetN>
    struct HUB75AddressPin;

    // Direct addressing mode
    template <bool ClockInverted, uint8_t ClockDelayN0a, uint8_t ClockDelayN0b, uint8_t ClockDelayN1, bool DataInverted, bool InputEnableInverted, bool SelectorInverted, typename PinIntegerSequenceT, uint8_t OffsetN>
    struct HUB75AddressPin<AddressMode_Direct, 1, ClockInverted, ClockDelayN0a, ClockDelayN0b, ClockDelayN1, DataInverted, InputEnableInverted, SelectorInverted, PinIntegerSequenceT, OffsetN> {
        static constexpr uint8_t PinCount = 1;

        static constexpr bool    AddressL = DataInverted ? true  : false;
        static constexpr bool    AddressH = DataInverted ? false : true;

        const PinDDX< eCxx::get<OffsetN + 0>( PinIntegerSequenceT{} ) > a;

        inline void begin() const __ECXX_ATTR_ALWAYS_INLINE
        { a.pinModeOutput(); a.digitalWrite(AddressL); }

        inline void end() const __ECXX_ATTR_ALWAYS_INLINE
        { a.pinModeInput(false); }

        inline void writeAddress(uint8_t addr) const __ECXX_ATTR_ALWAYS_INLINE
        {
            a.digitalWrite( (addr & 0b00000001) ? AddressH : AddressL );

            asmNOP<ClockDelayN1>();
        }
    };

    template <bool ClockInverted, uint8_t ClockDelayN0a, uint8_t ClockDelayN0b, uint8_t ClockDelayN1, bool DataInverted, bool InputEnableInverted, bool SelectorInverted, typename PinIntegerSequenceT, uint8_t OffsetN>
    struct HUB75AddressPin<AddressMode_Direct, 2, ClockInverted, ClockDelayN0a, ClockDelayN0b, ClockDelayN1, DataInverted, InputEnableInverted, SelectorInverted, PinIntegerSequenceT, OffsetN> {
        static constexpr uint8_t PinCount = 2;

        static constexpr bool    AddressL = DataInverted ? true  : false;
        static constexpr bool    AddressH = DataInverted ? false : true;

        const PinDDX< eCxx::get<OffsetN + 0>( PinIntegerSequenceT{} ) > a;
        const PinDDX< eCxx::get<OffsetN + 1>( PinIntegerSequenceT{} ) > b;

        inline void begin() const __ECXX_ATTR_ALWAYS_INLINE
        {
            a.pinModeOutput(); a.digitalWrite(AddressL);
            b.pinModeOutput(); b.digitalWrite(AddressL);
        }

        inline void end() const __ECXX_ATTR_ALWAYS_INLINE
        {
            a.pinModeInput(false);
            b.pinModeInput(false);
        }

        inline void writeAddress(uint8_t addr) const __ECXX_ATTR_ALWAYS_INLINE
        {
            a.digitalWrite( (addr & 0b00000001) ? AddressH : AddressL );
            b.digitalWrite( (addr & 0b00000010) ? AddressH : AddressL );

            asmNOP<ClockDelayN1>();
        }
    };

    template <bool ClockInverted, uint8_t ClockDelayN0a, uint8_t ClockDelayN0b, uint8_t ClockDelayN1, bool DataInverted, bool InputEnableInverted, bool SelectorInverted, typename PinIntegerSequenceT, uint8_t OffsetN>
    struct HUB75AddressPin<AddressMode_Direct, 3, ClockInverted, ClockDelayN0a, ClockDelayN0b, ClockDelayN1, DataInverted, InputEnableInverted, SelectorInverted, PinIntegerSequenceT, OffsetN> {
        static constexpr uint8_t PinCount = 3;

        static constexpr bool    AddressL = DataInverted ? true  : false;
        static constexpr bool    AddressH = DataInverted ? false : true;

        const PinDDX< eCxx::get<OffsetN + 0>( PinIntegerSequenceT{} ) > a;
        const PinDDX< eCxx::get<OffsetN + 1>( PinIntegerSequenceT{} ) > b;
        const PinDDX< eCxx::get<OffsetN + 2>( PinIntegerSequenceT{} ) > c;

        inline void begin() const __ECXX_ATTR_ALWAYS_INLINE
        {
            a.pinModeOutput(); a.digitalWrite(AddressL);
            b.pinModeOutput(); b.digitalWrite(AddressL);
            c.pinModeOutput(); c.digitalWrite(AddressL);
        }

        inline void end() const __ECXX_ATTR_ALWAYS_INLINE
        {
            a.pinModeInput(false);
            b.pinModeInput(false);
            c.pinModeInput(false);
        }

        inline void writeAddress(uint8_t addr) const __ECXX_ATTR_ALWAYS_INLINE
        {
            a.digitalWrite( (addr & 0b00000001) ? AddressH : AddressL );
            b.digitalWrite( (addr & 0b00000010) ? AddressH : AddressL );
            c.digitalWrite( (addr & 0b00000100) ? AddressH : AddressL );

            asmNOP<ClockDelayN1>();
        }
    };

    template <bool ClockInverted, uint8_t ClockDelayN0a, uint8_t ClockDelayN0b, uint8_t ClockDelayN1, bool DataInverted, bool InputEnableInverted, bool SelectorInverted, typename PinIntegerSequenceT, uint8_t OffsetN>
    struct HUB75AddressPin<AddressMode_Direct, 4, ClockInverted, ClockDelayN0a, ClockDelayN0b, ClockDelayN1, DataInverted, InputEnableInverted, SelectorInverted, PinIntegerSequenceT, OffsetN> {
        static constexpr uint8_t PinCount = 4;

        static constexpr bool    AddressL = DataInverted ? true  : false;
        static constexpr bool    AddressH = DataInverted ? false : true;

        const PinDDX< eCxx::get<OffsetN + 0>( PinIntegerSequenceT{} ) > a;
        const PinDDX< eCxx::get<OffsetN + 1>( PinIntegerSequenceT{} ) > b;
        const PinDDX< eCxx::get<OffsetN + 2>( PinIntegerSequenceT{} ) > c;
        const PinDDX< eCxx::get<OffsetN + 3>( PinIntegerSequenceT{} ) > d;

        inline void begin() const __ECXX_ATTR_ALWAYS_INLINE
        {
            a.pinModeOutput(); a.digitalWrite(AddressL);
            b.pinModeOutput(); b.digitalWrite(AddressL);
            c.pinModeOutput(); c.digitalWrite(AddressL);
            d.pinModeOutput(); d.digitalWrite(AddressL);
        }

        inline void end() const __ECXX_ATTR_ALWAYS_INLINE
        {
            a.pinModeInput(false);
            b.pinModeInput(false);
            c.pinModeInput(false);
            d.pinModeInput(false);
        }

        inline void writeAddress(uint8_t addr) const __ECXX_ATTR_ALWAYS_INLINE
        {
            a.digitalWrite( (addr & 0b00000001) ? AddressH : AddressL );
            b.digitalWrite( (addr & 0b00000010) ? AddressH : AddressL );
            c.digitalWrite( (addr & 0b00000100) ? AddressH : AddressL );
            d.digitalWrite( (addr & 0b00001000) ? AddressH : AddressL );

            asmNOP<ClockDelayN1>();
        }
    };

    template <bool ClockInverted, uint8_t ClockDelayN0a, uint8_t ClockDelayN0b, uint8_t ClockDelayN1, bool DataInverted, bool InputEnableInverted, bool SelectorInverted, typename PinIntegerSequenceT, uint8_t OffsetN>
    struct HUB75AddressPin<AddressMode_Direct, 5, ClockInverted, ClockDelayN0a, ClockDelayN0b, ClockDelayN1, DataInverted, InputEnableInverted, SelectorInverted, PinIntegerSequenceT, OffsetN> {
        static constexpr uint8_t PinCount = 5;

        static constexpr bool    AddressL = DataInverted ? true  : false;
        static constexpr bool    AddressH = DataInverted ? false : true;

        const PinDDX< eCxx::get<OffsetN + 0>( PinIntegerSequenceT{} ) > a;
        const PinDDX< eCxx::get<OffsetN + 1>( PinIntegerSequenceT{} ) > b;
        const PinDDX< eCxx::get<OffsetN + 2>( PinIntegerSequenceT{} ) > c;
        const PinDDX< eCxx::get<OffsetN + 3>( PinIntegerSequenceT{} ) > d;
        const PinDDX< eCxx::get<OffsetN + 4>( PinIntegerSequenceT{} ) > e;

        inline void begin() const __ECXX_ATTR_ALWAYS_INLINE
        {
            a.pinModeOutput(); a.digitalWrite(AddressL);
            b.pinModeOutput(); b.digitalWrite(AddressL);
            c.pinModeOutput(); c.digitalWrite(AddressL);
            d.pinModeOutput(); d.digitalWrite(AddressL);
            e.pinModeOutput(); e.digitalWrite(AddressL);
        }

        inline void end() const __ECXX_ATTR_ALWAYS_INLINE
        {
            a.pinModeInput(false);
            b.pinModeInput(false);
            c.pinModeInput(false);
            d.pinModeInput(false);
            e.pinModeInput(false);
        }

        inline void writeAddress(uint8_t addr) const __ECXX_ATTR_ALWAYS_INLINE
        {
            a.digitalWrite( (addr & 0b00000001) ? AddressH : AddressL );
            b.digitalWrite( (addr & 0b00000010) ? AddressH : AddressL );
            c.digitalWrite( (addr & 0b00000100) ? AddressH : AddressL );
            d.digitalWrite( (addr & 0b00001000) ? AddressH : AddressL );
            e.digitalWrite( (addr & 0b00010000) ? AddressH : AddressL );

            asmNOP<ClockDelayN1>();
        }
    };

    // Simple-shifter addressing mode
    template <uint8_t AddressLineN, bool ClockInverted, uint8_t ClockDelayN0a, uint8_t ClockDelayN0b, uint8_t ClockDelayN1, bool DataInverted, bool InputEnableInverted, bool SelectorInverted, typename PinIntegerSequenceT, uint8_t OffsetN>
    struct HUB75AddressPin<AddressMode_SimpleShifter, AddressLineN, ClockInverted, ClockDelayN0a, ClockDelayN0b, ClockDelayN1, DataInverted, InputEnableInverted, SelectorInverted, PinIntegerSequenceT, OffsetN> {
        static constexpr uint8_t PinCount = 2;

        static constexpr bool    ClockL   = ClockInverted ? true  : false;
        static constexpr bool    ClockH   = ClockInverted ? false : true;
        static constexpr bool    AddressL = DataInverted  ? true  : false;
        static constexpr bool    AddressH = DataInverted  ? false : true;

        const PinDDX< eCxx::get<OffsetN + 0>( PinIntegerSequenceT{} ) > aclk;
        const PinDDX< eCxx::get<OffsetN + 1>( PinIntegerSequenceT{} ) > aout;

        inline void begin() const __ECXX_ATTR_ALWAYS_INLINE
        {
            aclk.pinModeOutput(); aclk.digitalWrite(ClockL  );
            aout.pinModeOutput(); aout.digitalWrite(AddressL);
        }

        inline void end() const __ECXX_ATTR_ALWAYS_INLINE
        {
            aclk.pinModeInput(false);
            aout.pinModeInput(false);
        }

        inline void writeAddress(uint8_t addr) const __ECXX_ATTR_ALWAYS_INLINE
        {
            constexpr uint8_t RowCount = eCxx::pow(2, AddressLineN);

            addr = RowCount - 1 - addr;

            for(uint8_t row = 0; row < RowCount; ++row) {
                // Write the address bit
                aout.digitalWrite( (row == addr) ? AddressH : AddressL );
                // Pulse the clock pin
                                                                          asmNOP<ClockDelayN0a>();
                aclk.digitalWrite( ClockH                              ); asmNOP<ClockDelayN1 >();
                aclk.digitalWrite( ClockL                              ); asmNOP<ClockDelayN0b>();
            }
        }
    };

    // Selector-shifter addressing mode
    template <uint8_t AddressLineN, bool ClockInverted, uint8_t ClockDelayN0a, uint8_t ClockDelayN0b, uint8_t ClockDelayN1, bool DataInverted, bool InputEnableInverted, bool SelectorInverted, typename PinIntegerSequenceT, uint8_t OffsetN>
    struct HUB75AddressPin<AddressMode_SelectorShifter, AddressLineN, ClockInverted, ClockDelayN0a, ClockDelayN0b, ClockDelayN1, DataInverted, InputEnableInverted, SelectorInverted, PinIntegerSequenceT, OffsetN> {
        static constexpr uint8_t PinCount = 5;

        static constexpr bool    ClockL    = ClockInverted        ? true  : false;
        static constexpr bool    ClockH    = ClockInverted        ? false : true;
        static constexpr bool    AddressL  = DataInverted         ? true  : false;
        static constexpr bool    AddressH  = DataInverted         ? false : true;
        static constexpr bool    InputEnL  = InputEnableInverted  ? true  : false;
        static constexpr bool    InputEnH  = InputEnableInverted  ? false : true;
        static constexpr bool    SelectorL = SelectorInverted     ? true  : false;
        static constexpr bool    SelectorH = SelectorInverted     ? false : true;

        const PinDDX< eCxx::get<OffsetN + 0>( PinIntegerSequenceT{} ) > aclk;
        const PinDDX< eCxx::get<OffsetN + 1>( PinIntegerSequenceT{} ) > aout;
        const PinDDX< eCxx::get<OffsetN + 2>( PinIntegerSequenceT{} ) > aien;
        const PinDDX< eCxx::get<OffsetN + 3>( PinIntegerSequenceT{} ) > asl0;
        const PinDDX< eCxx::get<OffsetN + 4>( PinIntegerSequenceT{} ) > asl1;

        inline void begin() const __ECXX_ATTR_ALWAYS_INLINE
        {
            aclk.pinModeOutput(); aclk.digitalWrite(ClockL   );
            aout.pinModeOutput(); aout.digitalWrite(AddressL );
            aien.pinModeOutput(); aien.digitalWrite(InputEnL );
            asl0.pinModeOutput(); asl0.digitalWrite(SelectorL);
            asl1.pinModeOutput(); asl1.digitalWrite(SelectorL);
        }

        inline void end() const __ECXX_ATTR_ALWAYS_INLINE
        {
            aclk.pinModeInput(false);
            aout.pinModeInput(false);
            aien.pinModeInput(false);
            asl0.pinModeInput(false);
            asl1.pinModeInput(false);
        }

        inline void writeAddress(uint8_t addr) const __ECXX_ATTR_ALWAYS_INLINE
        {
            // Input enable
            aien.digitalWrite(InputEnH);

            // Write the address bits
            for(uint8_t row = 0; row < 8; ++row) {
                // Write the address bit
                aout.digitalWrite( ( (7 - row) == (addr % 8) ) ? AddressH : AddressL );
                // Pulse the clock pin
                                                                          asmNOP<ClockDelayN0a>();
                aclk.digitalWrite( ClockH                              ); asmNOP<ClockDelayN1 >();
                aclk.digitalWrite( ClockL                              ); asmNOP<ClockDelayN0b>();
            }

            // Input disable
            aien.digitalWrite(InputEnL);

            // Select the active shifter
            asl0.digitalWrite( (addr & 0b00001000) ? SelectorH : SelectorL );
            asl1.digitalWrite( (addr & 0b00010000) ? SelectorH : SelectorL );
        }
    };

} // namespace internal

// Derive the public interface of HUB75AddressPin<T> from the internal version above
template <HUB75Config::RowAddressing RowAddressingE, uint8_t AddressLineN, uint8_t ClockDelayN0a, uint8_t ClockDelayN0b, uint8_t ClockDelayN1, typename PinIntegerSequenceT, uint8_t OffsetN>
using HUB75AddressPin = internal::HUB75AddressPin<
    // Addressing mode
    internal::raGetAddressMode(RowAddressingE),
    // The number of address line(s)
    AddressLineN,
    // Inverted flag - clock
    (
        (RowAddressingE == HUB75Config::RowAddressing::Shift_nClk_Dat ) ||
        (RowAddressingE == HUB75Config::RowAddressing::Shift_nClk_nDat)
    ),
    // Clock delay factors
    ClockDelayN0a,
    ClockDelayN0b,
    ClockDelayN1,
    // Inverted flag - data
    (
        (RowAddressingE == HUB75Config::RowAddressing::nDirect        ) ||
        (RowAddressingE == HUB75Config::RowAddressing::Shift_Clk_nDat ) ||
        (RowAddressingE == HUB75Config::RowAddressing::Shift_nClk_nDat)
    ),
    // Inverted flag - input enable
    (
        (RowAddressingE == HUB75Config::RowAddressing::Shift_Clk_Dat_nIEn_Sel ) ||
        (RowAddressingE == HUB75Config::RowAddressing::Shift_Clk_Dat_nIEn_nSel)
    ),
    // Inverted flag - selector
    (
        (RowAddressingE == HUB75Config::RowAddressing::Shift_Clk_Dat_IEn_nSel ) ||
        (RowAddressingE == HUB75Config::RowAddressing::Shift_Clk_Dat_nIEn_nSel)
    ),
    // List of pin numbers
    PinIntegerSequenceT,
    // Offset to the above list of pin numbers
    OffsetN
>;


} // namespace eCxx


#endif
