/***************************************************************************
 *
 * 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.
 *
 ****************************************************************************/
#include "app_bt_stream.h"
#include "app_factory.h"
#include "app_media_player.h"
#include "cmsis_os.h"
#include "hal_trace.h"
#include "resources.h"
#include "string.h"

// for audio
#include "app_audio.h"
#include "app_utils.h"
#include "audioflinger.h"

#include "app_factory_audio.h"

#ifdef __FACTORY_MODE_SUPPORT__

#define BT_AUDIO_FACTORMODE_BUFF_SIZE (1024 * 2)
static enum APP_AUDIO_CACHE_T a2dp_cache_status = APP_AUDIO_CACHE_QTY;
static int16_t *app_audioloop_play_cache = NULL;

static uint32_t app_factorymode_data_come(uint8_t *buf, uint32_t len) {
  DUMP16("%d,", (int *)buf, 30);

  app_audio_pcmbuff_put(buf, len);
  if (a2dp_cache_status == APP_AUDIO_CACHE_QTY) {
    a2dp_cache_status = APP_AUDIO_CACHE_OK;
  }
  return len;
}

static uint32_t app_factorymode_more_data(uint8_t *buf, uint32_t len) {
  if (a2dp_cache_status != APP_AUDIO_CACHE_QTY) {
    app_audio_pcmbuff_get((uint8_t *)app_audioloop_play_cache, len / 2);
    app_bt_stream_copy_track_one_to_two_16bits(
        (int16_t *)buf, app_audioloop_play_cache, len / 2 / 2);
  }
  return len;
}

int app_factorymode_audioloop(bool on, enum APP_SYSFREQ_FREQ_T freq) {
  uint8_t *buff_play = NULL;
  uint8_t *buff_capture = NULL;
  uint8_t *buff_loop = NULL;
  struct AF_STREAM_CONFIG_T stream_cfg;
  static bool isRun = false;
  APP_FACTORY_TRACE(3, "app_factorymode_audioloop work:%d op:%d freq:%d", isRun,
                    on, freq);

  if (isRun == on)
    return 0;

  if (on) {
    if (freq < APP_SYSFREQ_52M) {
      freq = APP_SYSFREQ_52M;
    }
    app_sysfreq_req(APP_SYSFREQ_USER_APP_0, freq);

    a2dp_cache_status = APP_AUDIO_CACHE_QTY;
    app_audio_mempool_init();
    app_audio_mempool_get_buff(&buff_capture, BT_AUDIO_FACTORMODE_BUFF_SIZE);
    app_audio_mempool_get_buff(&buff_play, BT_AUDIO_FACTORMODE_BUFF_SIZE * 2);
    app_audio_mempool_get_buff((uint8_t **)&app_audioloop_play_cache,
                               BT_AUDIO_FACTORMODE_BUFF_SIZE * 2 / 2 / 2);
    app_audio_mempool_get_buff(&buff_loop, BT_AUDIO_FACTORMODE_BUFF_SIZE << 2);
    app_audio_pcmbuff_init(buff_loop, BT_AUDIO_FACTORMODE_BUFF_SIZE << 2);
    memset(&stream_cfg, 0, sizeof(stream_cfg));
    stream_cfg.bits = AUD_BITS_16;
    // stream_cfg.channel_num = AUD_CHANNEL_NUM_1;

#ifdef SPEECH_TX_AEC_CODEC_REF
    stream_cfg.channel_num = AUD_CHANNEL_NUM_2;
#else
    stream_cfg.channel_num = AUD_CHANNEL_NUM_1;
#endif
#if defined(__AUDIO_RESAMPLE__) && defined(SW_CAPTURE_RESAMPLE)
    stream_cfg.sample_rate = AUD_SAMPRATE_8463;
#else
    stream_cfg.sample_rate = AUD_SAMPRATE_8000;
#endif
    stream_cfg.device = AUD_STREAM_USE_INT_CODEC;
    stream_cfg.vol = TGT_VOLUME_LEVEL_15;
    stream_cfg.io_path = AUD_INPUT_PATH_MAINMIC;
    stream_cfg.handler = app_factorymode_data_come;

    stream_cfg.data_ptr = BT_AUDIO_CACHE_2_UNCACHE(buff_capture);
    stream_cfg.data_size = BT_AUDIO_FACTORMODE_BUFF_SIZE;
    af_stream_open(AUD_STREAM_ID_0, AUD_STREAM_CAPTURE, &stream_cfg);

    stream_cfg.channel_num = AUD_CHANNEL_NUM_2;
    stream_cfg.io_path = AUD_OUTPUT_PATH_SPEAKER;
    stream_cfg.handler = app_factorymode_more_data;

    stream_cfg.data_ptr = BT_AUDIO_CACHE_2_UNCACHE(buff_play);
    stream_cfg.data_size = BT_AUDIO_FACTORMODE_BUFF_SIZE * 2;
    af_stream_open(AUD_STREAM_ID_0, AUD_STREAM_PLAYBACK, &stream_cfg);

    af_stream_start(AUD_STREAM_ID_0, AUD_STREAM_PLAYBACK);
    af_stream_start(AUD_STREAM_ID_0, AUD_STREAM_CAPTURE);
    APP_FACTORY_TRACE(0, "app_factorymode_audioloop on");
  } else {
    af_stream_stop(AUD_STREAM_ID_0, AUD_STREAM_CAPTURE);
    af_stream_stop(AUD_STREAM_ID_0, AUD_STREAM_PLAYBACK);
    af_stream_close(AUD_STREAM_ID_0, AUD_STREAM_CAPTURE);
    af_stream_close(AUD_STREAM_ID_0, AUD_STREAM_PLAYBACK);
    APP_FACTORY_TRACE(0, "app_factorymode_audioloop off");

    app_sysfreq_req(APP_SYSFREQ_USER_APP_0, APP_SYSFREQ_32K);
  }

  isRun = on;
  return 0;
}

int app_factorymode_output_pcmpatten(audio_test_pcmpatten_t *pcmpatten,
                                     uint8_t *buf, uint32_t len) {
  uint32_t remain_size = len;
  uint32_t curr_size = 0;

  if (remain_size > pcmpatten->len) {
    do {
      if (pcmpatten->cuur_buf_pos) {
        curr_size = pcmpatten->len - pcmpatten->cuur_buf_pos;
        memcpy(buf, &(pcmpatten->buf[pcmpatten->cuur_buf_pos / 2]), curr_size);
        remain_size -= curr_size;
        pcmpatten->cuur_buf_pos = 0;
      } else if (remain_size > pcmpatten->len) {
        memcpy(buf + curr_size, pcmpatten->buf, pcmpatten->len);
        curr_size += pcmpatten->len;
        remain_size -= pcmpatten->len;
      } else {
        memcpy(buf + curr_size, pcmpatten->buf, remain_size);
        pcmpatten->cuur_buf_pos = remain_size;
        remain_size = 0;
      }
    } while (remain_size);
  } else {
    if ((pcmpatten->len - pcmpatten->cuur_buf_pos) >= len) {
      memcpy(buf, &(pcmpatten->buf[pcmpatten->cuur_buf_pos / 2]), len);
      pcmpatten->cuur_buf_pos += len;
    } else {
      curr_size = pcmpatten->len - pcmpatten->cuur_buf_pos;
      memcpy(buf, &(pcmpatten->buf[pcmpatten->cuur_buf_pos / 2]), curr_size);
      pcmpatten->cuur_buf_pos = len - curr_size;
      memcpy(buf + curr_size, pcmpatten->buf, pcmpatten->cuur_buf_pos);
    }
  }

  return 0;
}

#include "fft128dot.h"

#define N 64
#define NFFT 128

struct mic_st_t {
  FftTwiddle_t w[N];
  FftTwiddle_t w128[N * 2];
  FftData_t x[N * 2];
  FftData_t data_odd[N];
  FftData_t data_even[N];
  FftData_t data_odd_d[N];
  FftData_t data_even_d[N];
  FftData_t data[N * 2];
  signed long out[N];
};

int app_factorymode_mic_cancellation_run(void *mic_st, signed short *inbuf,
                                         int sample) {
  struct mic_st_t *st = (struct mic_st_t *)mic_st;
  int i, k, jj, ii;
  // int dataWidth = 16;     // input word format is 16 bit twos complement
  // fractional format 1.15
  int twiddleWidth =
      16; // input word format is 16 bit twos complement fractional format 2.14
  FftMode_t ifft = FFT_MODE;

  make_symmetric_twiddles(st->w, N, twiddleWidth);
  make_symmetric_twiddles(st->w128, N * 2, twiddleWidth);
  // input data
  for (i = 0; i < sample; i++) {
    st->x[i].re = inbuf[i];
    st->x[i].im = 0;
  }

  for (ii = 0; ii < 1; ii++) {
    k = 0;
    for (jj = 0; jj < N * 2; jj += 2) {
      FftData_t tmp;

      tmp.re = st->x[jj].re;
      tmp.im = st->x[jj].im;

      st->data_even[k].re =
          tmp.re; //(int) (double(tmp.re)*double(1 << FFTR4_INPUT_FORMAT_Y)) ;
      st->data_even[k].im =
          tmp.im; //(int) (double(tmp.im)*double(1 << FFTR4_INPUT_FORMAT_Y)) ;
      tmp.re = st->x[jj + 1].re;
      tmp.im = st->x[jj + 1].im;
      st->data_odd[k].re =
          tmp.re; //(int) (double(tmp.re)*double(1 << FFTR4_INPUT_FORMAT_Y)) ;
      st->data_odd[k].im =
          tmp.im; //(int) (double(tmp.im)*double(1 << FFTR4_INPUT_FORMAT_Y)) ;
      k++;
    }

    fftr4(NFFT / 2, st->data_even, st->w, FFTR4_TWIDDLE_WIDTH, FFTR4_DATA_WIDTH,
          ifft);
    fftr4(NFFT / 2, st->data_odd, st->w, FFTR4_TWIDDLE_WIDTH, FFTR4_DATA_WIDTH,
          ifft);

    for (jj = 0; jj < NFFT / 2; jj++) {

      int idx = dibit_reverse_int(jj, NFFT / 2);
      st->data_even_d[jj].re = st->data_even[idx].re;
      st->data_even_d[jj].im = st->data_even[idx].im;
      st->data_odd_d[jj].re = st->data_odd[idx].re;
      st->data_odd_d[jj].im = st->data_odd[idx].im;
    }
    for (jj = 0; jj < NFFT / 2; jj++) {
      long long mbr, mbi;
      FftData_t ta;
      FftData_t tmp;
      double a;
      mbr = (long long)(st->data_odd_d[jj].re) * st->w128[jj].re -
            (long long)(st->data_odd_d[jj].im) * st->w128[jj].im;
      mbi = (long long)(st->data_odd_d[jj].im) * st->w128[jj].re +
            (long long)(st->data_odd_d[jj].re) * st->w128[jj].im;
      ta.re = int(mbr >> (FFTR4_TWIDDLE_WIDTH - 2));
      ta.im = int(mbi >> (FFTR4_TWIDDLE_WIDTH - 2));
      st->data[jj].re = (st->data_even_d[jj].re + ta.re) / 2;
      st->data[jj].im = (st->data_even_d[jj].im + ta.im) / 2;
      // data[jj] = sat(data[jj],FFTR4_DATA_WIDTH);
      st->data[jj + NFFT / 2].re = (st->data_even_d[jj].re - ta.re) / 2;
      st->data[jj + NFFT / 2].im = (st->data_even_d[jj].im - ta.im) / 2;
      // data[jj+NFFT/2] = sat(data[jj+NFFT/2],FFTR4_DATA_WIDTH);

      a = st->data[jj].re; /// double(1 << FFTR4_OUTPUT_FORMAT_Y);// * double(1
                           /// << FFTR4_SCALE);
      tmp.re = (int)a;
      a = st->data[jj].im; /// double(1 << FFTR4_OUTPUT_FORMAT_Y);// * double(1
                           /// << FFTR4_SCALE);
      tmp.im = (int)a;
      st->x[ii * NFFT + jj].re = (int)tmp.re;
      st->x[ii * NFFT + jj].im = (int)tmp.im;
      a = st->data[jj + NFFT / 2].re; /// double(1 << FFTR4_OUTPUT_FORMAT_Y);//
                                      /// * double(1 << FFTR4_SCALE);
      tmp.re = (int)a;
      a = st->data[jj + NFFT / 2].im; /// double(1 << FFTR4_OUTPUT_FORMAT_Y);//
                                      /// * double(1 << FFTR4_SCALE);
      tmp.im = (int)a;
      st->x[ii * NFFT + jj + NFFT / 2].re = (int)tmp.re;
      st->x[ii * NFFT + jj + NFFT / 2].im = (int)tmp.im;
    }
  }

  for (i = 0; i < N; i++) {
    st->out[i] = st->x[i].re * st->x[i].re + st->x[i].im * st->x[i].im;
  }

  return 0;
}

void *app_factorymode_mic_cancellation_init(void *(*alloc_ext)(int)) {
  struct mic_st_t *mic_st;
  mic_st = (struct mic_st_t *)alloc_ext(sizeof(struct mic_st_t));
  return (void *)mic_st;
}

#endif
