// Copyright 2024 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.
#include <string.h>
#include <stdlib.h>
#include <xcore/assert.h>
#include <debug_print.h>
#include "cmds.h" // Autogenerated
#include "cmd_offsets.h" // Autogenerated
#include "stages/delay.h"

void delay_process(int32_t ** input, int32_t ** output, void * app_data_state)
{
    xassert(app_data_state != NULL);
    delay_state_t *state = app_data_state;

    // do while saves instructions for cases
    // where the loop will always execute at
    // least once
    int i = 0;
    do {
        int32_t *in = input[i];
        int32_t *out = output[i];

        int j = 0;
        do {
            *out++ = adsp_delay(&state->delay[i], *in++);
        } while(++j < state->frame_size);
    } while(++i < state->n_outputs);
}

void delay_init(module_instance_t* instance,
                adsp_bump_allocator_t* allocator,
                uint8_t id,
                int n_inputs,
                int n_outputs,
                int frame_size)
{
    xassert(n_inputs == n_outputs && "Delay should have the same number of inputs and outputs");
    delay_state_t *state = instance->state;
    delay_config_t *config = instance->control.config;

    state->n_inputs = n_inputs;
    state->n_outputs = n_outputs;
    state->frame_size = frame_size;

    state->delay = adsp_bump_allocator_malloc(allocator, n_inputs * sizeof(delay_t));

    for(int i = 0; i < n_inputs; i++)
    {
        state->delay[i].max_delay = config->max_delay;
        state->delay[i].delay = config->delay;
        state->delay[i].buffer_idx = 0;
        state->delay[i].buffer = (int32_t *)adsp_bump_allocator_malloc(allocator, DELAY_DSP_REQUIRED_MEMORY_SAMPLES(config->max_delay));
    }
}

void delay_control(void *state, module_control_t *control)
{
    xassert(state != NULL);
    delay_state_t *delay_state = state;
    xassert(control != NULL);
    delay_config_t *delay_config = control->config;

    if(control->config_rw_state == config_write_pending) {
        // avoid overrunning delay buffer
        uint32_t new_delay = (delay_config->delay <= delay_state->delay[0].max_delay) ? delay_config->delay : delay_state->delay[0].max_delay;

        for(int i = 0; i < delay_state->n_inputs; i++)
        {
            delay_state->delay[i].delay = new_delay;
        }
        control->config_rw_state = config_none_pending;
    }
    else if(control->config_rw_state == config_read_pending) {
        delay_config->max_delay = delay_state->delay[0].max_delay;
        delay_config->delay = delay_state->delay[0].delay;
        control->config_rw_state = config_read_updated;
    }
    else
    {
        // nothing to do
    }
}
