// Copyright 2024-2025 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/compressor_sidechain_stereo.h"
#include "xmath/xmath.h"

static inline void compressor_stereo_copy_config_to_state(compressor_stereo_t *comp_state, const compressor_sidechain_stereo_config_t *comp_config)
{
    comp_state->env_det_l.attack_alpha = comp_config->attack_alpha;
    comp_state->env_det_l.release_alpha = comp_config->release_alpha;
    comp_state->env_det_r.attack_alpha = comp_config->attack_alpha;
    comp_state->env_det_r.release_alpha = comp_config->release_alpha;
    comp_state->threshold = comp_config->threshold;
    comp_state->slope = comp_config->slope;
}

static inline void compressor_stereo_copy_state_to_config(compressor_sidechain_stereo_config_t *comp_config, const compressor_stereo_t *comp_state)
{
    // Copy from channel 0 state to the config
    comp_config->attack_alpha = comp_state->env_det_l.attack_alpha;
    comp_config->release_alpha = comp_state->env_det_l.release_alpha;
    comp_config->envelope = MAX(comp_state->env_det_l.envelope, comp_state->env_det_r.envelope);
    comp_config->gain = comp_state->gain;
    comp_config->threshold = comp_state->threshold;
    comp_config->slope = comp_state->slope;
}

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

    // do while saves instructions for cases
    // where the loop will always execute at
    // least once
    int32_t *in_l = input[0];
    int32_t *in_r = input[1];
    int32_t *detect_l = input[2];
    int32_t *detect_r = input[3];
    int32_t *out_l = output[0];
    int32_t *out_r = output[1];

    int j = 0;
    do {
        int32_t outs[2];
        adsp_compressor_rms_sidechain_stereo(&state->comp, outs, *in_l++, *in_r++, *detect_l++, *detect_r++);
        *out_l++ = outs[0];
        *out_r++ = outs[1];
    } while(++j < state->frame_size);
}

void compressor_sidechain_stereo_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 == 4 && "Sidechain compressor should have the 4 inputs");
    xassert(n_outputs == 2 && "Sidechain compressor should have 2 outputs");
    compressor_sidechain_stereo_state_t *state = instance->state;
    compressor_sidechain_stereo_config_t *config = instance->control.config;

    memset(state, 0, sizeof(compressor_sidechain_stereo_state_t));
    state->n_inputs = n_inputs;
    state->n_outputs = n_outputs;
    state->frame_size = frame_size;

    state->comp.gain = INT32_MAX;
    state->comp.env_det_l.envelope = 0;
    state->comp.env_det_r.envelope = 0;

    compressor_stereo_copy_config_to_state(&state->comp, config);
}

void compressor_sidechain_stereo_control(void *module_state, module_control_t *control)
{
    xassert(module_state != NULL);
    compressor_sidechain_stereo_state_t *state = module_state;
    xassert(control != NULL);
    compressor_sidechain_stereo_config_t *config = control->config;

    if(control->config_rw_state == config_write_pending)
    {

        compressor_stereo_copy_config_to_state(&state->comp, config);
        control->config_rw_state = config_none_pending;
    }
    else if(control->config_rw_state == config_read_pending)
    {
        compressor_stereo_copy_state_to_config(config, &state->comp);
        control->config_rw_state = config_read_updated;
    }
    else
    {
        // nothing to do
    }
}
