// mic_broker.c — single I2S RX owner; delivers PCM16 mono frames to one
// active consumer at a time.
//
// I2S configuration mirrors the working voice_pipeline setup exactly:
//   port  : I2S_NUM_0 (RX only; voice_pipeline TX uses I2S_NUM_1 — no clash)
//   mode  : Philips std, 16-bit, mono
//   rate  : caller-supplied (voice_pipeline uses 16 000 Hz)
// The broker's capture_task reads MB_FRAME-sample chunks (20 ms @ 16 kHz)
// and invokes the registered callback for the current mode on every frame.
// MIC_IDLE suppresses delivery without stopping the I2S clock, keeping DMA
// buffers drained.

#include "mic_broker.h"

#include "driver/i2s_std.h"
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"

static const char *TAG = "mic_broker";

// 20 ms @ 16 kHz mono 16-bit
#define MB_FRAME 320
// NPC callback runs AFE feed/fetch + WS send on this task;
// sized like the old voice_pipeline capture task (CAPTURE_TASK_STACK = 8192).
#define MB_TASK_STACK 8192

static i2s_chan_handle_t        s_rx;
static volatile mic_mode_t      s_mode = MIC_IDLE;
static struct {
    mic_frame_cb_t cb;
    void          *ctx;
} s_consumers[3];   // indexed by mic_mode_t value (0=IDLE unused, 1, 2)

static void capture_task(void *arg) {
    int16_t buf[MB_FRAME];
    size_t  got;
    for (;;) {
        esp_err_t err = i2s_channel_read(s_rx, buf, sizeof(buf), &got,
                                         portMAX_DELAY);
        if (err != ESP_OK) {
            ESP_LOGW(TAG, "i2s read err: %s", esp_err_to_name(err));
            continue;
        }
        mic_mode_t m = s_mode;
        if (m != MIC_IDLE && s_consumers[m].cb) {
            s_consumers[m].cb(buf, got / sizeof(int16_t), s_consumers[m].ctx);
        }
    }
}

esp_err_t mic_broker_init(int bclk, int ws, int din, int sr) {
    // Guard double init: return a distinctive error so callers can treat
    // it as benign ("already up") rather than a hard failure.
    if (s_rx) return ESP_ERR_INVALID_STATE;

    i2s_chan_config_t cc = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_0, I2S_ROLE_MASTER);
    esp_err_t e = i2s_new_channel(&cc, NULL, &s_rx);
    if (e != ESP_OK) return e;

    // Philips mono 16-bit — matches voice_pipeline's working i2s_setup().
    i2s_std_config_t std = {
        .clk_cfg  = I2S_STD_CLK_DEFAULT_CONFIG(sr),
        .slot_cfg = I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT,
                                                        I2S_SLOT_MODE_MONO),
        .gpio_cfg = {
            .mclk  = I2S_GPIO_UNUSED,
            .bclk  = bclk,
            .ws    = ws,
            .din   = din,
            .dout  = I2S_GPIO_UNUSED,
            .invert_flags = { .mclk_inv = false,
                              .bclk_inv = false,
                              .ws_inv   = false },
        },
    };

    e = i2s_channel_init_std_mode(s_rx, &std);
    if (e != ESP_OK) {
        i2s_del_channel(s_rx);
        s_rx = NULL;
        return e;
    }

    e = i2s_channel_enable(s_rx);
    if (e != ESP_OK) {
        i2s_del_channel(s_rx);
        s_rx = NULL;
        return e;
    }

    if (xTaskCreate(capture_task, "mic_broker", MB_TASK_STACK, NULL, 5, NULL) != pdPASS) {
        i2s_channel_disable(s_rx);
        i2s_del_channel(s_rx);
        s_rx = NULL;
        return ESP_ERR_NO_MEM;
    }
    ESP_LOGI(TAG, "init OK (BCLK=%d WS=%d DIN=%d @%d Hz)", bclk, ws, din, sr);
    return ESP_OK;
}

void mic_broker_register(mic_mode_t mode, mic_frame_cb_t cb, void *ctx) {
    if (mode == MIC_IDLE) return;   // IDLE slot is never called
    s_consumers[mode].cb  = cb;
    s_consumers[mode].ctx = ctx;
}

void mic_broker_set_mode(mic_mode_t mode) {
    s_mode = mode;
}

mic_mode_t mic_broker_mode(void) {
    return s_mode;
}
