/* Includes ------------------------------------------------------------------*/

#include <nrfx_dppi.h>
#include <nrfx_gpiote.h>
#include <nrfx_timer.h>
#include <drivers/clock_control/nrf_clock_control.h>

/* Private define ------------------------------------------------------------*/

#define OUTPUT_PIN NRF_GPIO_PIN_MAP(1,7)

#define PERIOD       (40*16000)
#define SHORT_PERIOD (20*16000)
#define OFFSET       1
#define PULSE_WIDTH  16000

/* Private variables ---------------------------------------------------------*/

// Timer need 4 CC
// CC0: overflow
// CC1: short period
// CC2: for generating the raise of the output signal
// CC3: for generating the fall of the output signal
static const nrfx_timer_t timer = NRFX_TIMER_INSTANCE(0);

static nrf_dppi_channel_group_t group;

static uint32_t counter = 0;

/* Private functions declaration ---------------------------------------------*/

static void timer_init(void);
static void configure_one_time_clear(void);
static void timer_handler(nrf_timer_event_t event_type, void *p_context);
static void output_init(void);

/* Public functions ----------------------------------------------------------*/

void main(void)
{
    z_nrf_clock_bt_ctlr_hf_request();

    timer_init();
    output_init();

    while(1) {
        k_sleep(K_MSEC(1000));
    }
}

/* Private functions implementation ------------------------------------------*/

static void timer_init(void)
{
    nrfx_timer_config_t timer_config = NRFX_TIMER_DEFAULT_CONFIG;
    timer_config.frequency = NRF_TIMER_FREQ_125kHz;
    timer_config.bit_width = NRF_TIMER_BIT_WIDTH_32;
    nrfx_timer_init(&timer, &timer_config, timer_handler);
    nrfx_timer_compare(&timer, NRF_TIMER_CC_CHANNEL0, PERIOD, true);
    nrf_timer_shorts_enable(timer.p_reg, NRF_TIMER_SHORT_COMPARE0_CLEAR_MASK);
    nrfx_timer_compare(&timer, NRF_TIMER_CC_CHANNEL1, SHORT_PERIOD, true);
    nrfx_timer_compare(&timer, NRF_TIMER_CC_CHANNEL2, OFFSET, false);
    nrfx_timer_compare(&timer, NRF_TIMER_CC_CHANNEL3, OFFSET + PULSE_WIDTH, false);

    IRQ_CONNECT(TIMER0_IRQn, NRFX_TIMER_DEFAULT_CONFIG_IRQ_PRIORITY - 1,
        nrfx_timer_0_irq_handler, NULL, 0);

    nrfx_timer_enable(&timer);

    configure_one_time_clear();
}

static void configure_one_time_clear(void)
{
    uint8_t channel;

    nrfx_dppi_group_alloc(&group);
    nrfx_dppi_channel_alloc(&channel);
    nrf_timer_publish_set(timer.p_reg, NRF_TIMER_EVENT_COMPARE1, channel);
    nrf_timer_subscribe_set(timer.p_reg, NRF_TIMER_TASK_CLEAR, channel);
    nrfx_dppi_channel_include_in_group(channel, group);
    nrf_dppi_subscribe_set(NRF_DPPIC, nrf_dppi_group_disable_task_get(group), channel);    
}

static void timer_handler(nrf_timer_event_t event_type, void *p_context)
{
    if (event_type == NRF_TIMER_EVENT_COMPARE0) {
        printk("0");
        nrfx_dppi_group_enable(group);
    }
    if (event_type == NRF_TIMER_EVENT_COMPARE1) {
        printk("1");
    }
}

static void output_init(void)
{
    if (!nrfx_gpiote_is_init()) {
        nrfx_gpiote_init(0);
    }

    nrfx_gpiote_out_config_t const out_config = {
            .init_state = NRF_GPIOTE_INITIAL_VALUE_LOW,
            .action     = NRF_GPIOTE_POLARITY_TOGGLE,
            .task_pin   = true,
    };

    nrfx_gpiote_out_init(OUTPUT_PIN, &out_config);
    nrfx_gpiote_out_task_enable(OUTPUT_PIN);

    uint8_t channel;
    nrfx_dppi_channel_alloc(&channel);
    nrf_timer_publish_set(timer.p_reg, NRF_TIMER_EVENT_COMPARE2, channel);
    nrf_gpiote_subscribe_set(
            NRF_GPIOTE,
            nrfx_gpiote_set_task_get(OUTPUT_PIN),
            channel);
    nrfx_dppi_channel_enable(channel);

    nrfx_dppi_channel_alloc(&channel);
    nrf_timer_publish_set(timer.p_reg, NRF_TIMER_EVENT_COMPARE3, channel);
    nrf_gpiote_subscribe_set(
            NRF_GPIOTE,
            nrfx_gpiote_clr_task_get(OUTPUT_PIN),
            channel);
    nrfx_dppi_channel_enable(channel);
}