// IWYU pragma: private

 #ifndef __INC_BLOCK_CLOCKLESS_H
#define __INC_BLOCK_CLOCKLESS_H

#include "fl/chipsets/timing_traits.h"
#include "fastled_delay.h"
#include "bitswap.h"
#include "platforms/arm/sam/is_sam.h"
#include "fl/stl/compiler_control.h"
#include "fl/stl/static_assert.h"
#include "fl/stl/noexcept.h"

FL_DISABLE_WARNING_PUSH
FL_DISABLE_WARNING_DEPRECATED_REGISTER
namespace fl {
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Base template for clockless controllers.  These controllers have 3 control points in their cycle for each bit.  The first point
// is where the line is raised hi.  The second pointsnt is where the line is dropped low for a zero.  The third point is where the
// line is dropped low for a one.  T1, T2, and T3 correspond to the timings for those three in clock cycles.
//
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#if defined(FL_IS_SAM)
#define PORT_MASK (((1<<LANES)-1) & ((FIRST_PIN==2) ? 0xFF : 0xFF))

#define FASTLED_HAS_BLOCKLESS 1

#define PORTD_FIRST_PIN 25
#define PORTA_FIRST_PIN 69
#define PORTB_FIRST_PIN 90

typedef union {
    u8 bytes[8];
    u32 raw[2];
} Lines;

template <u8 LANES, int FIRST_PIN, typename TIMING, EOrder RGB_ORDER = RGB, int XTRA0 = 0, bool FLIP = false>
class InlineBlockClocklessController : public CPixelLEDController<RGB_ORDER, LANES, PORT_MASK> {
	enum : u32 {
		T1 = TIMING::T1,
		T2 = TIMING::T2,
		T3 = TIMING::T3,
		WAIT_TIME = TIMING::RESET
	};
	#define TADJUST 0
	#define TOTAL ( (T1+TADJUST) + (T2+TADJUST) + (T3+TADJUST) )
	#define T1_MARK (TOTAL - (T1+TADJUST))
	#define T2_MARK (T1_MARK - (T2+TADJUST))
	typedef typename FastPin<FIRST_PIN>::port_ptr_t data_ptr_t;
	typedef typename FastPin<FIRST_PIN>::port_t data_t;

	data_t mPinMask;
	data_ptr_t mPort;
	CMinWait<WAIT_TIME> mWait;

public:
	virtual int size() { return CLEDController::size() * LANES; }
	virtual void init() FL_NOEXCEPT {
        FL_STATIC_ASSERT(LANES <= 8, "Maximum of 8 lanes for Due parallel controllers!");
        if(FIRST_PIN == PORTA_FIRST_PIN) {
            switch(LANES) {
                case 8: FastPin<31>::setOutput();
                case 7: FastPin<58>::setOutput();
                case 6: FastPin<100>::setOutput();
                case 5: FastPin<59>::setOutput();
                case 4: FastPin<60>::setOutput();
                case 3: FastPin<61>::setOutput();
                case 2: FastPin<68>::setOutput();
                case 1: FastPin<69>::setOutput();
            }
        } else if(FIRST_PIN == PORTD_FIRST_PIN) {
            switch(LANES) {
                case 8: FastPin<11>::setOutput();
                case 7: FastPin<29>::setOutput();
                case 6: FastPin<15>::setOutput();
                case 5: FastPin<14>::setOutput();
                case 4: FastPin<28>::setOutput();
                case 3: FastPin<27>::setOutput();
                case 2: FastPin<26>::setOutput();
                case 1: FastPin<25>::setOutput();
            }
        } else if(FIRST_PIN == PORTB_FIRST_PIN) {
            switch(LANES) {
                case 8: FastPin<97>::setOutput();
                case 7: FastPin<96>::setOutput();
                case 6: FastPin<95>::setOutput();
                case 5: FastPin<94>::setOutput();
                case 4: FastPin<93>::setOutput();
                case 3: FastPin<92>::setOutput();
                case 2: FastPin<91>::setOutput();
                case 1: FastPin<90>::setOutput();
            }
        }
        mPinMask = FastPin<FIRST_PIN>::mask();
        mPort = FastPin<FIRST_PIN>::port();
    }

    virtual u16 getMaxRefreshRate() const { return 400; }

    virtual void showPixels(PixelController<RGB_ORDER, LANES, PORT_MASK> & pixels) FL_NOEXCEPT {
        mWait.wait();
        showRGBInternal(pixels);
        sei();
        mWait.mark();
    }

    static u32 showRGBInternal(PixelController<RGB_ORDER, LANES, PORT_MASK> &allpixels) FL_NOEXCEPT {
        // Serial.println("Entering show");

        int nLeds = allpixels.mLen;

        // Setup the pixel controller and load/scale the first byte
        Lines b0,b1,b2;

        allpixels.preStepFirstByteDithering();
        for(u8 i = 0; i < LANES; i++) {
            b0.bytes[i] = allpixels.loadAndScale0(i);
        }

        // Setup and start the clock
        TC_Configure(DUE_TIMER,DUE_TIMER_CHANNEL,TC_CMR_TCCLKS_TIMER_CLOCK1) FL_NOEXCEPT;
        pmc_enable_periph_clk(DUE_TIMER_ID);
        TC_Start(DUE_TIMER,DUE_TIMER_CHANNEL) FL_NOEXCEPT;

        #if (FASTLED_ALLOW_INTERRUPTS == 1)
        cli();
        #endif
        u32 next_mark = (DUE_TIMER_VAL + (TOTAL));
        while(nLeds--) {
            allpixels.stepDithering();
            #if (FASTLED_ALLOW_INTERRUPTS == 1)
            cli();
            if(DUE_TIMER_VAL > next_mark) {
                if((DUE_TIMER_VAL - next_mark) > ((WAIT_TIME-INTERRUPT_THRESHOLD)*CLKS_PER_US)) {
                    sei(); TC_Stop(DUE_TIMER,DUE_TIMER_CHANNEL); return DUE_TIMER_VAL;
                }
            }
            #endif

            // Write first byte, read next byte
            writeBits<8+XTRA0,1>(next_mark, b0, b1, allpixels);

            // Write second byte, read 3rd byte
            writeBits<8+XTRA0,2>(next_mark, b1, b2, allpixels);

            allpixels.advanceData();
            // Write third byte
            writeBits<8+XTRA0,0>(next_mark, b2, b0, allpixels);

            #if (FASTLED_ALLOW_INTERRUPTS == 1)
            sei();
            #endif
        }

        return DUE_TIMER_VAL;
    }

    template<int BITS,int PX> __attribute__ ((always_inline)) inline static void writeBits(FASTLED_REGISTER u32 & next_mark, FASTLED_REGISTER Lines & b, Lines & b3, PixelController<RGB_ORDER,LANES, PORT_MASK> &pixels) FL_NOEXCEPT { // , FASTLED_REGISTER uint32_t & b2)  {
        Lines b2;
        fl::transpose8x1(b.bytes,b2.bytes);

        FASTLED_REGISTER u8 d = pixels.template getd<PX>(pixels);
        FASTLED_REGISTER u8 scale = pixels.template getscale<PX>(pixels);

        for(u32 i = 0; (i < LANES) && (i<8); i++) {
            while(DUE_TIMER_VAL < next_mark);
            next_mark = (DUE_TIMER_VAL+TOTAL);

            *FastPin<FIRST_PIN>::sport() = PORT_MASK;

            while((next_mark - DUE_TIMER_VAL) > (T2+T3+6));
            *FastPin<FIRST_PIN>::cport() = (~b2.bytes[7-i]) & PORT_MASK;

            while((next_mark - (DUE_TIMER_VAL)) > T3);
            *FastPin<FIRST_PIN>::cport() = PORT_MASK;

            b3.bytes[i] = pixels.template loadAndScale<PX>(pixels,i,d,scale);
        }

        for(u32 i = LANES; i < 8; i++) {
            while(DUE_TIMER_VAL < next_mark);
            next_mark = (DUE_TIMER_VAL+TOTAL);
            *FastPin<FIRST_PIN>::sport() = PORT_MASK;

            while((next_mark - DUE_TIMER_VAL) > (T2+T3+6));
            *FastPin<FIRST_PIN>::cport() = (~b2.bytes[7-i]) & PORT_MASK;

            while((next_mark - DUE_TIMER_VAL) > T3);
            *FastPin<FIRST_PIN>::cport() = PORT_MASK;
        }
    }
};

#endif
}  // namespace fl

FL_DISABLE_WARNING_POP

#endif
