/***************************************************************************
 *
 * Copyright 2015-2019 BES.
 * All rights reserved. All unpublished rights reserved.
 *
 * No part of this work may be used or reproduced in any form or by any
 * means, or stored in a database or retrieval system, without prior written
 * permission of BES.
 *
 * Use of this work is governed by a license granted by BES.
 * This work contains confidential and proprietary information of
 * BES. which is protected by copyright, trade secret,
 * trademark and other intellectual property rights.
 *
 ****************************************************************************/
/* rbpcmbuf source */
/* pcmbuf management & af control & mixer */
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#ifdef MBED
#include "mbed.h"
#include "rtos.h"
#endif
#include "app_audio.h"
#include "app_utils.h"
#include "audioflinger.h"
#include "cqueue.h"
#include "hal_trace.h"

#include "rbpcmbuf.h"
#include "rbplay.h"
#include "utils.h"

#define RB_PCMBUF_DMA_BUFFER_SIZE (1024 * 12)
#define RB_PCMBUF_MEDIA_BUFFER_SIZE (1024 * 12)
#define RB_DECODE_OUT_BUFFER_SIZE 1024

static uint8_t *rb_decode_out_buff;
static uint8_t *rbplay_dma_buffer;
static uint8_t *rb_pcmbuf_media_buf;
static CQueue rb_pcmbuf_media_buf_queue;
static osMutexId _rb_media_buf_queue_mutex_id = NULL;
static osMutexDef(_rb_media_buf_queue_mutex);

#define LOCK_MEDIA_BUF_QUEUE()                                                 \
  if (osErrorISR ==                                                            \
      osMutexWait(_rb_media_buf_queue_mutex_id, osWaitForever)) {              \
    error("%s LOCK_MEDIA_BUF_QUEUE from IRQ!!!!!!!\n", __func__);              \
  }

#define UNLOCK_MEDIA_BUF_QUEUE()                                               \
  if (osErrorISR == osMutexRelease(_rb_media_buf_queue_mutex_id)) {            \
    error("%s UNLOCK_MEDIA_BUF_QUEUE from IRQ!!!!!!\n");                       \
  }

static uint32_t rbplay_more_data(uint8_t *buf, uint32_t len) {
  CQItemType *e1 = NULL;
  CQItemType *e2 = NULL;
  unsigned int len1 = 0;
  unsigned int len2 = 0;

  LOCK_MEDIA_BUF_QUEUE();
  int ret = PeekCQueue(&rb_pcmbuf_media_buf_queue, len, &e1, &len1, &e2, &len2);
  UNLOCK_MEDIA_BUF_QUEUE();

  if (ret == CQ_OK) {
    if (len1 > 0)
      memcpy(buf, e1, len1);
    if (len2 > 0)
      memcpy(buf + len1, e2, len - len1);
    LOCK_MEDIA_BUF_QUEUE();
    DeCQueue(&rb_pcmbuf_media_buf_queue, 0, len);
    UNLOCK_MEDIA_BUF_QUEUE();
  } else {
    warn("RBplay cache underflow");
  }

  return len;
}

extern uint8_t rb_ctl_get_vol(void);
void rb_pcmbuf_init(void) {
  info("pcmbuff init");
  if (!_rb_media_buf_queue_mutex_id)
    _rb_media_buf_queue_mutex_id =
        osMutexCreate((osMutex(_rb_media_buf_queue_mutex)));

  app_audio_mempool_init();

  app_audio_mempool_get_buff(&rb_pcmbuf_media_buf, RB_PCMBUF_MEDIA_BUFFER_SIZE);
  InitCQueue(&rb_pcmbuf_media_buf_queue, RB_PCMBUF_MEDIA_BUFFER_SIZE,
             (unsigned char *)rb_pcmbuf_media_buf);

  app_audio_mempool_get_buff(&rbplay_dma_buffer, RB_PCMBUF_DMA_BUFFER_SIZE);
  app_audio_mempool_get_buff(&rb_decode_out_buff, RB_DECODE_OUT_BUFFER_SIZE);

  struct AF_STREAM_CONFIG_T stream_cfg;

  memset(&stream_cfg, 0, sizeof(stream_cfg));

  stream_cfg.bits = AUD_BITS_16;
  stream_cfg.channel_num = AUD_CHANNEL_NUM_2;
  stream_cfg.sample_rate = AUD_SAMPRATE_44100;
  stream_cfg.device = AUD_STREAM_USE_INT_CODEC;
  stream_cfg.io_path = AUD_OUTPUT_PATH_SPEAKER;
  stream_cfg.vol = rb_ctl_get_vol();
  stream_cfg.handler = rbplay_more_data;
  stream_cfg.data_ptr = BT_AUDIO_CACHE_2_UNCACHE(rbplay_dma_buffer);
  stream_cfg.data_size = RB_PCMBUF_DMA_BUFFER_SIZE;

  af_stream_open(AUD_STREAM_ID_0, AUD_STREAM_PLAYBACK, &stream_cfg);
  af_stream_start(AUD_STREAM_ID_0, AUD_STREAM_PLAYBACK);
}

void *rb_pcmbuf_request_buffer(int *size) {
  *size = RB_DECODE_OUT_BUFFER_SIZE / 4;
  return rb_decode_out_buff;
}

void rb_pcmbuf_write(unsigned int size) {
  int ret;
  do {
    LOCK_MEDIA_BUF_QUEUE();
    ret = EnCQueue(&rb_pcmbuf_media_buf_queue, (CQItemType *)rb_decode_out_buff,
                   size * (2 * 2));
    UNLOCK_MEDIA_BUF_QUEUE();
    osThreadYield();
  } while (ret == CQ_ERR);
}

void rb_pcmbuf_stop(void) {
  af_stream_stop(AUD_STREAM_ID_0, AUD_STREAM_PLAYBACK);
  af_stream_close(AUD_STREAM_ID_0, AUD_STREAM_PLAYBACK);
}
