// IWYU pragma: private

// ok no namespace fl
#ifndef __INC_ARBITER_NRF52
#define __INC_ARBITER_NRF52

#include "platforms/arm/nrf52/is_nrf52.h"

#if defined(FL_IS_NRF52)

#include "platforms/arm/nrf52/led_sysdefs_arm_nrf52.h"
#include "fl/stl/noexcept.h"
#include "fl/stl/static_assert.h"

typedef void (*FASTLED_NRF52_PWM_INTERRUPT_HANDLER)();

// a trick learned from other embedded projects .. 
// use the enum as an index to a statically-allocated array
// to store unique information for that instance.
// also provides a count of how many instances were enabled.
//
// See led_sysdefs_arm_nrf52.h for selection....
//
typedef enum _FASTLED_NRF52_ENABLED_PWM_INSTANCE { // ok plain enum
#if defined(FASTLED_NRF52_ENABLE_PWM_INSTANCE0)
    FASTLED_NRF52_PWM0_INSTANCE_IDX,
#endif
#if defined(FASTLED_NRF52_ENABLE_PWM_INSTANCE1)
    FASTLED_NRF52_PWM1_INSTANCE_IDX,
#endif
#if defined(FASTLED_NRF52_ENABLE_PWM_INSTANCE2)
    FASTLED_NRF52_PWM2_INSTANCE_IDX,
#endif
#if defined(FASTLED_NRF52_ENABLE_PWM_INSTANCE3)
    FASTLED_NRF52_PWM3_INSTANCE_IDX,
#endif
    FASTLED_NRF52_PWM_INSTANCE_COUNT
} FASTLED_NRF52_ENABLED_PWM_INSTANCES;

FL_STATIC_ASSERT(FASTLED_NRF52_PWM_INSTANCE_COUNT > 0, "Instance count must be greater than zero -- define FASTLED_NRF52_ENABLE_PWM_INSTNACE[n] (replace `[n]` with digit)");

template <fl::u32 _PWM_ID>
class PWM_Arbiter {
private:
    FL_STATIC_ASSERT(_PWM_ID < 32, "PWM_ID over 31 breaks current arbitration bitmask");
    //const  uint32_t _ACQUIRE_MASK =             (1u << _PWM_ID) ;
    //const  uint32_t _CLEAR_MASK   = ~((uint32_t)(1u << _PWM_ID));
    static fl::u32                              s_PwmInUse;
    static NRF_PWM_Type * const                  s_PWM;
    static IRQn_Type      const                           s_PWM_IRQ;
    static FASTLED_NRF52_PWM_INTERRUPT_HANDLER volatile   s_Isr;

public:
    static void isr_handler() FL_NOEXCEPT {
        return s_Isr();
    }
    FASTLED_NRF52_INLINE_ATTRIBUTE static bool            isAcquired() FL_NOEXCEPT {
        return (0u != (s_PwmInUse & 1u)); // _ACQUIRE_MASK
    }
    FASTLED_NRF52_INLINE_ATTRIBUTE static void            acquire(FASTLED_NRF52_PWM_INTERRUPT_HANDLER isr) FL_NOEXCEPT {
        while (!tryAcquire(isr));
    }
    FASTLED_NRF52_INLINE_ATTRIBUTE static bool            tryAcquire(FASTLED_NRF52_PWM_INTERRUPT_HANDLER isr) FL_NOEXCEPT {
        fl::u32 oldValue = __sync_fetch_and_or(&s_PwmInUse, 1u); // _ACQUIRE_MASK
        if (0u == (oldValue & 1u)) { // _ACQUIRE_MASK
            s_Isr = isr;
            return true;
        }
        return false;
    }
    FASTLED_NRF52_INLINE_ATTRIBUTE static void            releaseFromIsr() FL_NOEXCEPT {
        fl::u32 oldValue = __sync_fetch_and_and(&s_PwmInUse, ~1u); // _CLEAR_MASK
        if (0u == (oldValue & 1u)) { // _ACQUIRE_MASK
            // TODO: This should never be true... indicates was not held.
            // Assert here?
            (void)oldValue;
        }
        return;
    }
    FASTLED_NRF52_INLINE_ATTRIBUTE static NRF_PWM_Type *  getPWM() FL_NOEXCEPT {
        return s_PWM;
    }
    FASTLED_NRF52_INLINE_ATTRIBUTE static IRQn_Type       getIRQn() { return s_PWM_IRQ; }
};
template <fl::u32 _PWM_ID> NRF_PWM_Type * const PWM_Arbiter<_PWM_ID>::s_PWM           =
    #if defined(FASTLED_NRF52_ENABLE_PWM_INSTANCE0)
        (_PWM_ID == 0 ? NRF_PWM0 :
    #endif
    #if defined(FASTLED_NRF52_ENABLE_PWM_INSTANCE1)
        (_PWM_ID == 1 ? NRF_PWM1 :
    #endif
    #if defined(FASTLED_NRF52_ENABLE_PWM_INSTANCE2)
        (_PWM_ID == 2 ? NRF_PWM2 :
    #endif
    #if defined(FASTLED_NRF52_ENABLE_PWM_INSTANCE3)
        (_PWM_ID == 3 ? NRF_PWM3 :
    #endif
        (NRF_PWM_Type*)-1
    #if defined(FASTLED_NRF52_ENABLE_PWM_INSTANCE0)
        )
    #endif
    #if defined(FASTLED_NRF52_ENABLE_PWM_INSTANCE1)
        )
    #endif
    #if defined(FASTLED_NRF52_ENABLE_PWM_INSTANCE2)
        )
    #endif
    #if defined(FASTLED_NRF52_ENABLE_PWM_INSTANCE3)
        )
    #endif
    ;
template <fl::u32 _PWM_ID> IRQn_Type    const                            PWM_Arbiter<_PWM_ID>::s_PWM_IRQ   = ((IRQn_Type)((fl::u8)((fl::u32)(s_PWM) >> 12)));
template <fl::u32 _PWM_ID> fl::u32                                      PWM_Arbiter<_PWM_ID>::s_PwmInUse  = 0;
template <fl::u32 _PWM_ID> FASTLED_NRF52_PWM_INTERRUPT_HANDLER volatile  PWM_Arbiter<_PWM_ID>::s_Isr       = nullptr;

#endif // FL_IS_NRF52
#endif // __INC_ARBITER_NRF52
