/*
 * 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_CONFIG_H
#define ECXX_PLATFORM_HUB75_CONFIG_H


namespace eCxx {


//
// A class to hold GenericHUB75Driver<T> configuration specifiers
//
class HUB75Config : public Uninstantiable {
    public:
        //
        // Data pin mode
        // NOTE : Some LED panels will be labeled with R1/R2, G1/G2, and B1/B2
        //        instead of R0/R1, G0/G1, and B0/B1
        //
        enum class DataPin : uint8_t {
            R0_R1_G0_G1_B0_B1 = 0, // 6 pins (individual : R0 R1 G0 G1 B0 B1)
                                   // LED panel input pins         : R0, R1, G0, G1, B0, and B1
                                   // LED panel special connection : none

            R0R1_G0G1_B0B1    = 1, // 3 pins (chained    : R0-R1 G0-G1 B0-B1)
            R01_G01_B01       = 1, // LED panel input pins         : R0, G0, and B0
                                   // LED panel special connection : R0-out -> R1-in,
                                   //                                G0-out -> G1-in,
                                   //                                B0-out -> B1-in

            R0G0B0_R1G1B1     = 2, // 2 pins (chained    : R0-G0-B0 R1-G1-B1)
            RGB0_RGB1         = 2, // LED panel input pins         : R0 and R1
                                   // LED panel special connection : R0-out -> G0-in, G0-out -> B0-in,
                                   //                                R1-out -> G1-in, G1-out -> B1-in

            R0G0B0R1G1B1      = 3, // 1 pin  (chained    : R0-G0-B0-R1-G1-B1)
            RGB01             = 3  // LED panel input pins         : R0
                                   // LED panel special connection : R0-out -> G0-in, G0-out -> B0-in,
                                   //                                B0-out -> R1-in,
                                   //                                R1-out -> G1-in, G1-out -> B1-in
        };

        //
        // Row scanning mode
        //
        enum class RowScanning : uint8_t {
            // 1/2 scan; example:
            //     16-pixel high panel - 1 effective address line (A        ) - 8 rows will be on at the same time
            S_1_2 = 2,

            // 1/4 scan; example:
            //     16-pixel high panel - 2 effective address line (A B      ) - 4 rows will be on at the same time
            S_1_4 = 4,

            // 1/8 scan; example:
            //     16-pixel high panel - 3 effective address line (A B C    ) - 2 rows will be on at the same time
            //     32-pixel high panel - 3 effective address line (A B C    ) - 4 rows will be on at the same time
            S_1_8 = 8,

            // 1/16 scan; example:
            //     32-pixel high panel - 4 effective address line (A B C D  ) - 2 rows will be on at the same time
            //     64-pixel high panel - 4 effective address line (A B C D  ) - 4 rows will be on at the same time
            S_1_16 = 16,

            // 1/32 scan; example:
            //     64-pixel high panel - 5 effective address line (A B C D E) - 2 rows will be on at the same time
            S_1_32 = 32
        };

        //
        // Row addressing mode
        //
        enum class RowAddressing : uint8_t {
            // Direct addressing
            // # The number of address pin(s) should match with the example in the scan mode above.
            // # The n prefix indicates that the address pin(s) need(s) inverted logic.
            Direct      = 0,
            nDirect     = 1,

            // Shift register addressing
            // # Address pin A      is usually the clock.
            // # Address pin B or C is usually the data.
            // # The n prefix indicates that the particular pin will use inverted logic.
            Shift_Clk_Dat   = 2,
            Shift_nClk_Dat  = 3,
            Shift_Clk_nDat  = 4,
            Shift_nClk_nDat = 5,

            // Shift register addressing with enable and selector (e.g. SM5266)
            // # Address pin A       is  usually the clock.
            // # Address pin B       is  usually the data.
            // # Address pin C       is  usually the input enable.
            // # Address pin D and E are usually the active-shifter selector.
            // # The n prefix indicates that the particular pin will use inverted logic.
            Shift_Clk_Dat_IEn_Sel   = 6,
            Shift_Clk_Dat_nIEn_Sel  = 7,
            Shift_Clk_Dat_IEn_nSel  = 8,
            Shift_Clk_Dat_nIEn_nSel = 9
        };

        //
        // BCM configuration
        //
        // NOTE : # 5-bit BCM should be good enough for 512 colors with CIE-1931 lightness color correction.
        //        # Using this scheme, the input will be in 3-bit per channel and the color-corrected output
        //          will be in 5-bit per channel.
        //
        static constexpr uint8_t BCMStepCount  = 31; // (1 + 2 + 4 + 8 + 16)

        static constexpr uint8_t BCMPhaseStart = 0b00000001;
        static constexpr uint8_t BCMPhaseEnd   = 0b00100000;

        static constexpr uint8_t BCM_CCShiftRightAmount = 5; // The input is in 3-bit per channel, hence 8 - 3 = 5
        static const     uint8_t BCM_CCLUT_MSB3_O5[8];
};


//
// A helper struct to determine the number of effective address line(s) for GenericHUB75Driver<T>
//
template <HUB75Config::RowScanning RowScanningE>
struct HUB75EffectiveAddressLineCount {
    static constexpr uint8_t Value = eCxx::log2( static_cast<uint8_t>(RowScanningE) );
};


} // namespace eCxx


#endif
