/**************************************************************************
 * Copyright (C) 2012-2021  Unisound
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 **************************************************************************
 *
 * Description : lvp_buffer.c
 * Author      : yzs.unisound.com
 * Date        : 2021.03.01
 *
 **************************************************************************/
#include <autoconf.h>
#include <stdio.h>
#include <string.h>
#include <types.h>
#include <lvp_context.h>
#include <lvp_buffer.h>
#include <lvp_attr.h>

#include <driver/uni_cache.h>

#define LOG_TAG "[LVP_BUFF]"

//=================================================================================================

// Check some parameters
#if (PCM_FRAME_SIZE * CONFIG_LVP_PCM_FRAME_NUM_PER_CHANNEL * 2) % 128 != 0
#error "Active Channel Buffer Size SHOULD be times of 1024!"
#endif

#if CONFIG_LVP_PCM_FRAME_NUM_PER_CHANNEL % CONFIG_LVP_PCM_FRAME_NUM_PER_CONTEXT != 0
#error "SRAM Frame Number of Channel SHOULD be times of Frame Number of Context!"
#endif

#if CONFIG_LVP_LOGFBANK_FRAME_NUM_PER_CHANNEL % CONFIG_LVP_PCM_FRAME_NUM_PER_CONTEXT != 0
#error "Logfbank Frame Number of Channel SHOULD be times of Frame Number of Context!"
#endif

//-------------------------------------------------------------------------------------------------

#ifdef CONFIG_LVP_BUFFER_HAS_MIC
// Microphone Input Buffer in SRAM
static short s_mic_buffer[PCM_FRAME_SIZE * CONFIG_LVP_PCM_FRAME_NUM_PER_CHANNEL * CONFIG_MIC_CHANNEL_NUM] ALIGNED_ATTR(16) DRAM0_AUDIO_IN_ATTR;
#endif

#ifdef CONFIG_LVP_BUFFER_HAS_REF
// Ref Input Buffer in SRAM
static short s_ref_buffer[PCM_FRAME_SIZE * CONFIG_LVP_PCM_FRAME_NUM_PER_CHANNEL * CONFIG_REF_CHANNEL_NUM] ALIGNED_ATTR(16);
#endif

#ifdef CONFIG_LVP_BUFFER_HAS_FFT
// FFT(complex) Input Buffer in SRAM
static int s_fft_buffer[FFT_DIM_PER_FRAME * 2 * CONFIG_LVP_FFT_FRAME_NUM_PER_CHANNEL * CONFIG_FFT_CHANNEL_NUM] ALIGNED_ATTR(16);
#endif

#ifdef CONFIG_LVP_BUFFER_HAS_LOGFBANK
// Logfbank Input Buffer in SRAM
static short s_logfbank_buffer[LOGFBANK_DIM_PER_FRAME * CONFIG_LVP_LOGFBANK_FRAME_NUM_PER_CHANNEL * CONFIG_LOGFBANK_CHANNEL_NUM] ALIGNED_ATTR(16) DRAM0_AUDIO_IN_ATTR;
#endif

#ifdef CONFIG_LVP_BUFFER_HAS_LOGFBANK
// TODO
static short s_feats_buffer[CONFIG_KWS_MODEL_INPUT_WIN_LENGTH * CONFIG_KWS_MODEL_FEATURES_DIM_PER_FRAME] ALIGNED_ATTR(16);
#endif

// Context Header
static LVP_CONTEXT_HEADER s_context_header ALIGNED_ATTR(16);
// Context Buffer
static LVP_CONTEXT_BUFFER s_context_buffer[CONFIG_LVP_CONTEXT_NUM] ALIGNED_ATTR(16);

//-------------------------------------------------------------------------------------------------

int LvpInitBuffer(void)
{
    //printf (LOG_TAG"Enter LvpInitBuffer\n");

    // Clear
    memset(&s_context_header, 0, sizeof(s_context_header));
    memset(&s_context_buffer, 0, sizeof(s_context_buffer));
#ifdef CONFIG_LVP_BUFFER_HAS_LOGFBANK
    memset(&s_feats_buffer  , 0, sizeof(s_feats_buffer));
#endif

    // Initialize Context Header
    s_context_header.version = LVP_CONTEXT_VERSION;

#ifdef CONFIG_LVP_BUFFER_HAS_MIC
    s_context_header.mic_num = CONFIG_MIC_CHANNEL_NUM;
    s_context_header.mic_buffer = s_mic_buffer;
    s_context_header.mic_buffer_size = sizeof(s_mic_buffer);
#else
    s_context_header.mic_num = 0;
    s_context_header.mic_buffer = NULL;
    s_context_header.mic_buffer_size = 0;
#endif

#ifdef CONFIG_LVP_BUFFER_HAS_REF
    s_context_header.ref_num = CONFIG_LVP_REF_CHANNEL_NUM;
    s_context_header.ref_buffer = s_ref_buffer;
    s_context_header.ref_buffer_size = sizeof(s_ref_buffer);
#else
    s_context_header.ref_num = 0;
    s_context_header.ref_buffer = NULL;    // NULL
    s_context_header.ref_buffer_size = 0;
#endif

#ifdef CONFIG_LVP_BUFFER_HAS_FFT
    s_context_header.fft_num = CONFIG_LVP_FFT_CHANNEL_NUM;
    s_context_header.fft_buffer = s_fft_buffer;
    s_context_header.fft_buffer_size = sizeof(s_fft_buffer);
    s_context_header.fft_frame_num_per_channel = CONFIG_LVP_FFT_FRAME_NUM_PER_CHANNEL;
#else
    s_context_header.fft_num = 0;
    s_context_header.fft_buffer = NULL;    // NULL
    s_context_header.fft_buffer_size = 0;
    s_context_header.fft_frame_num_per_channel = 0;
#endif

#ifdef CONFIG_LVP_BUFFER_HAS_LOGFBANK
    s_context_header.logfbank_num = CONFIG_LOGFBANK_CHANNEL_NUM;
    s_context_header.logfbank_buffer = s_logfbank_buffer;
    s_context_header.logfbank_buffer_size = sizeof(s_logfbank_buffer);
    s_context_header.logfbank_frame_num_per_channel = CONFIG_LVP_LOGFBANK_FRAME_NUM_PER_CHANNEL;
#else
    s_context_header.logfbank_num = 0;
    s_context_header.logfbank_buffer = NULL;
    s_context_header.logfbank_buffer_size = 0;
    s_context_header.logfbank_frame_num_per_channel = 0;
#endif

#ifdef CONFIG_OUT_CHANNEL_NUM
    s_context_header.out_num = CONFIG_OUT_CHANNEL_NUM;
    s_context_header.out_buffer_size = CONFIG_LVP_PCM_FRAME_NUM_PER_CONTEXT * PCM_FRAME_SIZE * CONFIG_OUT_CHANNEL_NUM * sizeof(short);
#else
    s_context_header.out_num = 0;
    s_context_header.out_buffer_size  = 0;
#endif

    s_context_header.frame_length = PCM_FRAME_LENGTH;
    s_context_header.sample_rate  = PCM_SAMPLE_RATE;
    s_context_header.pcm_frame_num_per_context = CONFIG_LVP_PCM_FRAME_NUM_PER_CONTEXT;
    s_context_header.pcm_frame_num_per_channel = CONFIG_LVP_PCM_FRAME_NUM_PER_CHANNEL;
    s_context_header.logfbank_dim_per_frame    = LOGFBANK_DIM_PER_FRAME;
    s_context_header.fft_dim_per_frame         = FFT_DIM_PER_FRAME;

#ifdef CONFIG_KWS_SNPU_BUFFER_SIZE
    s_context_header.snpu_buffer_size = CONFIG_KWS_SNPU_BUFFER_SIZE/16*16 + 16;
#else
    s_context_header.snpu_buffer_size = 0;
#endif

    s_context_header.ctx_buffer = s_context_buffer;
    s_context_header.ctx_size   = sizeof(LVP_CONTEXT_BUFFER);
    s_context_header.ctx_num    = CONFIG_LVP_CONTEXT_NUM;
#ifdef CONFIG_LVP_FFT_VAD_ENABLE
    s_context_header.fft_vad_en = 1;
#else
    s_context_header.fft_vad_en = 0;
#endif

    return 0;
}

void *LvpGetMicBufferAddr(void)
{
    return (void *)s_context_header.mic_buffer;
}

int LvpGetMicBufferSize(void)
{
    return s_context_header.mic_buffer_size;
}

void *LvpGetLogfbankBufferAddr(void)
{
    return (void *)s_context_header.logfbank_buffer;
}

int LvpGetLogfbankBufferSize(void)
{
    return s_context_header.logfbank_buffer_size;
}

void *LvpGetFftBuffer(void)
{
    return (void *)s_context_header.fft_buffer;
}

int LvpGetFftBufferSize(void)
{
    return s_context_header.fft_buffer_size;
}

void *LvpGetContextHeader(void)
{
    return (void *)&s_context_header;
}

void *LvpGetFeatsBuffer(void)
{
#ifdef CONFIG_LVP_BUFFER_HAS_LOGFBANK
    return (void *)s_feats_buffer;
#else
    return NULL;
#endif
}

void *LvpGetLogfankBuffer(LVP_CONTEXT *context, unsigned int index)
{
    LVP_CONTEXT_HEADER *ctx_header          = context->ctx_header;
    unsigned int logfbank_num               = ctx_header->logfbank_frame_num_per_channel;
    unsigned int logfbank_dim               = ctx_header->logfbank_dim_per_frame;
    unsigned int pcm_frame_num_per_context  = ctx_header->pcm_frame_num_per_context;
    unsigned int curr_index                 = (index * pcm_frame_num_per_context) % logfbank_num;
    void *logfbank_buffer                   = ctx_header->logfbank_buffer;

    // uni_dcache_invalid_range(logfbank_buffer, ctx_header->logfbank_buffer_size);
    return (LVP_CONTEXT *)((unsigned int)logfbank_buffer + curr_index * logfbank_dim * sizeof(short));
}

int LvpGetContext(unsigned int index, LVP_CONTEXT **context, unsigned int *size)
{
    LVP_CONTEXT_BUFFER *context_buffer  = &s_context_buffer[index % CONFIG_LVP_CONTEXT_NUM];
    context_buffer->context.ctx_header  = &s_context_header;

#ifdef CONFIG_OUT_CHANNEL_NUM
    context_buffer->context.out_buffer  = context_buffer->out_buffer;
#else
    context_buffer->context.out_buffer  = NULL;
#endif

#ifdef CONFIG_KWS_SNPU_BUFFER_SIZE
    context_buffer->context.snpu_buffer = context_buffer->snpu_buffer;
#else
    context_buffer->context.snpu_buffer = NULL;
#endif

    *context = &context_buffer->context;
    *size    = sizeof(LVP_CONTEXT_BUFFER);

    return 0;
}
