#include "hal_data.h"
#include "audio_record.h"
#include "ringbuffer.h"

#define PDM_BUFFER_NUM_SAMPLES      1024                            // Number of samples to store in data buffer
#define PDM_CALLBACK_NUM_SAMPLES    (PDM_BUFFER_NUM_SAMPLES / 2)    // Number of samples to store in data buffer before getting a callback
#define PDM_MIC_STARTUP_TIME_US     35000                           // PDM microphone startup time, RA8P1_EK mic startup time is 35ms
#define RING_BUFFER_SIZE            (RING_BUFFER_LEN * sizeof(int16_t))

static int8_t g_bind = 0;
static ring_buffer_t rb_hdl;
static int16_t  audio_rb[RING_BUFFER_LEN];             __attribute__ ((aligned (32)))
static uint32_t g_pdm_buffer[PDM_BUFFER_NUM_SAMPLES];  __attribute__ ((aligned (32)))
static int16_t  g_audio_tmp[PDM_CALLBACK_NUM_SAMPLES]; __attribute__ ((aligned (32)))

fsp_err_t audio_record_init(void)
{
    fsp_err_t  err = FSP_SUCCESS;

    /* Audio Storage Buffer */
    ring_buffer_init(&rb_hdl, (short *) audio_rb, RING_BUFFER_LEN);

    err = g_pdm2.p_api->open(g_pdm2.p_ctrl, g_pdm2.p_cfg);
    if (err != FSP_SUCCESS) {
        assert(0);
        //DBG_UART_TRACE("R_SSI_Open fail.\r\n");
        return err;
    }

    /* Wait for PDM filters to settle. Use settling time calculated by e2 studio.
     * Also wait for PDM mic to startup. */
    R_BSP_SoftwareDelay(PDM2_FILTER_SETTLING_TIME_US + PDM_MIC_STARTUP_TIME_US, BSP_DELAY_UNITS_MICROSECONDS);

    return err;
}

fsp_err_t audio_record_start(void)
{
    fsp_err_t  err = FSP_SUCCESS;

    memset(g_pdm_buffer, 0, sizeof(g_pdm_buffer));

    /* Start receiving PDM data. */
    err = g_pdm2.p_api->start(g_pdm2.p_ctrl, g_pdm_buffer,
                              PDM_BUFFER_NUM_SAMPLES * sizeof(uint32_t),
                              PDM_CALLBACK_NUM_SAMPLES);
    assert(FSP_SUCCESS == err);

    return err;
}

fsp_err_t audio_record_stop(void)
{
    fsp_err_t err = FSP_SUCCESS;

    /* Stop receiving PDM data. */
    err = g_pdm2.p_api->stop(g_pdm2.p_ctrl);
    assert(FSP_SUCCESS == err);
    return err;
}

void g_audio_cb(pdm_callback_args_t *p_args)
{
    switch (p_args->event)
    {
        case PDM_EVENT_SOUND_DETECTION:
        {
            /* Sound has been detected based on configured thresholds. */
            assert(0);
            break;
        }
        case PDM_EVENT_DATA:
        {
            /* PDM_CALLBACK_NUM_SAMPLES has been stored in g_pdm_buffer.
               Only single channel is support w/ 4 byte alignment */
            uint32_t* p_buf = &g_pdm_buffer[g_bind * PDM_CALLBACK_NUM_SAMPLES];
            for(int k = 0; k < PDM_CALLBACK_NUM_SAMPLES; k++){
                g_audio_tmp[k] = (int16_t)(p_buf[k] * 0xFFFF);
            }

            /* Check for Overflow */
            size_t rem = RING_BUFFER_SIZE - uac_frame_buf_size();
            assert( rem >= PDM_CALLBACK_NUM_SAMPLES * sizeof(int16_t) );

            /* Add to Ring Buffer */
            ring_buffer_queue_arr(&rb_hdl, (short *)g_audio_tmp, PDM_CALLBACK_NUM_SAMPLES * sizeof(int16_t));

            /* Increment Buffer Index */
            g_bind = (g_bind + 1) % 2;
            break;
        }
        case PDM_EVENT_ERROR:
        {
            if (p_args->error & PDM_ERROR_SHORT_CIRCUIT)
            {
                /* Short circuit has been detected based on configured counts */
                assert(0);
            }
            if (p_args->error & PDM_ERROR_OVERVOLTAGE_LOWER)
            {
                /* Overvoltage has been detected based on configured lower limit */
                assert(0);
            }
            if (p_args->error & PDM_ERROR_OVERVOLTAGE_UPPER)
            {
                /* Overvoltage has been detected based on configured upper limit */
                assert(0);
            }
            if (p_args->error & PDM_ERROR_BUFFER_OVERWRITE)
            {
                /* Buffer overwrite has occurred */
                //assert(0);
            }
            break;
        }
        default:
        {
            break;
        }
    }
}

size_t uac_frame_buf_get(uint16_t *buf, uint32_t size)
{
    return ring_buffer_dequeue_arr(&rb_hdl, (short *) buf, size);
}

size_t uac_frame_buf_size(void)
{
    return ring_buffer_num_items(&rb_hdl) * sizeof(short);
}
