#ifdef __RAND_FROM_MIC__
#include "randfrommic.h"
#include "app_audio.h"
#include "app_bt_stream.h"
#include "app_utils.h"
#include "audioflinger.h"
#include "cmsis_gcc.h"
#include "cmsis_os.h"
#include "hal_timer.h"
#include "hal_trace.h"
#include "stdio.h"
#include "stdlib.h"
#include "string.h"

#if BT_DRV_DEBUG
#define RAND_TRACE(n, fmt, ...) TRACE(n, fmt, ##__VA_ARGS__)
#define RAND_DUMP(s, buff, len) DUMP8(s, buff, len)
#else
#define RAND_TRACE(n, fmt, ...)
#define RAND_DUMP(s, buff, len)
#endif

static void generateRand(bool on);
static uint32_t rand_data_handle(uint8_t *buf, uint32_t len);

static uint8_t deviceId = 0;
static uint8_t *captureBuffer = NULL;
static uint32_t randSeed = 1;
static bool randInitialised = false;

// 4 bytes aligned
#define RAND_GRAB_BITS_PER_SAMPLE 4
#define RAND_GRAB_BITS_MASK_PER_SAMPLE ((1 << RAND_GRAB_BITS_PER_SAMPLE) - 1)

RAND_NUMBER_T randomBuffer = {
    25,
    RAND_STATUS_CLOSE,
};

/**
 * Description: parse mic data according to the stream cfg(bit mode and channel
 * number) only the lowest byte of each frame is taken ADC format: 16bit mode ->
 * [15:0] is valid 24bit mode -> [23:4] is valid 32bit mode -> [31:12] is valid
 *
 */
static int randDataParse(uint8_t *buf, uint32_t len, enum AUD_BITS_T bits,
                         enum AUD_CHANNEL_NUM_T ch_num) {
  uint8_t index = 0;

  union {
    uint32_t seedValue;
    uint8_t value[4];
  } seedData;

  if ((NULL == buf) ||
      ((RANDOM_CAPTURE_BUFFER_SIZE / 2) > len)) // ping-pong buffer
  {
    return -1;
  }

  RAND_TRACE(1, "%s", __func__);
  RAND_DUMP("%x ", buf, 16);

  switch (bits) {
  case AUD_BITS_16: {
    uint16_t *content = (uint16_t *)buf;

    for (index = 0; index < 4; index++) {
      seedData.value[index] =
          ((*content) & RAND_GRAB_BITS_MASK_PER_SAMPLE) |
          (((*(content + ch_num)) & RAND_GRAB_BITS_MASK_PER_SAMPLE)
           << RAND_GRAB_BITS_PER_SAMPLE);
      content += ((8 / RAND_GRAB_BITS_PER_SAMPLE) * ch_num);
    }
    break;
  }
  case AUD_BITS_24: {
    uint32_t *content = (uint32_t *)buf;
    for (index = 0; index < 4; index++) {
      // bit 23:4 are valid
      seedData.value[index] =
          (((*content) >> 4) & RAND_GRAB_BITS_MASK_PER_SAMPLE) |
          ((((*(content + ch_num)) >> 4) & RAND_GRAB_BITS_MASK_PER_SAMPLE)
           << RAND_GRAB_BITS_PER_SAMPLE);
      content += ((8 / RAND_GRAB_BITS_PER_SAMPLE) * ch_num);
    }
    break;
  }
  case AUD_BITS_32: {
    uint32_t *content = (uint32_t *)buf;
    for (index = 0; index < 4; index++) {
      // bit 31:12 are valid
      seedData.value[index] =
          (((*content) >> 12) & RAND_GRAB_BITS_MASK_PER_SAMPLE) |
          ((((*(content + ch_num)) >> 12) & RAND_GRAB_BITS_MASK_PER_SAMPLE)
           << RAND_GRAB_BITS_PER_SAMPLE);
      content += ((8 / RAND_GRAB_BITS_PER_SAMPLE) * ch_num);
    }
    break;
  }
  default: {
    return -1;
  } break;
  }

  randSeed = seedData.seedValue;

  return 0;
}

static void generateRand(bool on) {
  struct AF_STREAM_CONFIG_T stream_cfg;

  RAND_TRACE(2, "%s op:%d", __func__, on);

  if (on) {
    randomBuffer.skipRound = 10;

    randomBuffer.status = random_mic_is_on(&deviceId);
    RAND_TRACE(2, "%s random status = %d", __func__, randomBuffer.status);

    if (RAND_STATUS_CLOSE == randomBuffer.status) {
      app_sysfreq_req(APP_SYSFREQ_USER_RANDOM, APP_SYSFREQ_208M);
      app_capture_audio_mempool_init();
      app_capture_audio_mempool_get_buff(&captureBuffer,
                                         RANDOM_CAPTURE_BUFFER_SIZE);
      memset(&stream_cfg, 0, sizeof(stream_cfg));
      stream_cfg.bits = AUD_BITS_16;
      stream_cfg.channel_num = AUD_CHANNEL_NUM_1;
      stream_cfg.device = AUD_STREAM_USE_INT_CODEC;
      stream_cfg.sample_rate = AUD_SAMPRATE_8000;
      stream_cfg.vol = TGT_VOLUME_LEVEL_15;
      stream_cfg.io_path = AUD_INPUT_PATH_MAINMIC;
      stream_cfg.handler = rand_data_handle;

      stream_cfg.data_ptr = BT_AUDIO_CACHE_2_UNCACHE(captureBuffer);
      stream_cfg.data_size = RANDOM_CAPTURE_BUFFER_SIZE;
      af_stream_open(AUD_STREAM_ID_0, AUD_STREAM_CAPTURE, &stream_cfg);
      af_stream_start(AUD_STREAM_ID_0, AUD_STREAM_CAPTURE);
      randomBuffer.status = RAND_STATUS_OPEN;
    } else if (RAND_STATUS_MIC_OPENED == randomBuffer.status) {
      af_stream_start(deviceId, AUD_STREAM_CAPTURE);
    }
  } else {
    // release the acquired system clock
    app_sysfreq_req(APP_SYSFREQ_USER_RANDOM, APP_SYSFREQ_32K);
    if (RAND_STATUS_MIC_OPENED == randomBuffer.status) {
      af_stream_stop(deviceId, AUD_STREAM_CAPTURE);
    } else if (RAND_STATUS_OPEN == randomBuffer.status) {
      af_stream_stop(AUD_STREAM_ID_0, AUD_STREAM_CAPTURE);
      af_stream_close(AUD_STREAM_ID_0, AUD_STREAM_CAPTURE);
    }
    randomBuffer.status = RAND_STATUS_CLOSE;
  }
}

static uint32_t rand_data_handle(uint8_t *buf, uint32_t len) {
  if (buf == NULL) {
    return len;
  }

  if ((1 == randomBuffer.skipRound) &&
      (!randDataParse(buf, len, AUD_BITS_16, AUD_CHANNEL_NUM_1))) {
    generateRand(false);
    randomBuffer.skipRound = 0;
  } else if (1 != randomBuffer.skipRound) {
    randomBuffer.skipRound--;
  }

  return len;
}

void initSeed(void) {
  uint8_t count = 100; // avoid deed loop

  RAND_TRACE(2, "%s:+++ initialised = %d", __func__, randInitialised);

  if (randInitialised) {
    generateRand(true);

    while ((0 != randomBuffer.skipRound) && (0 != count)) {
      osDelay(10);
      count--;
    }
  }

  if ((0 == count) || (false == randInitialised)) {
    RAND_TRACE(1, "%s not ready", __func__);
    randSeed = (uint32_t)hal_sys_timer_get();
    generateRand(false);
  }

  srand(randSeed);
  RAND_TRACE(2, "%s:--- count = %d", __func__, count);
}

void random_status_sync(void) {
  if (RAND_STATUS_OPEN == randomBuffer.status) {
    RAND_TRACE(1, "%s random mic has already on,should be closed", __func__);
    generateRand(false);
  }
}

void random_data_process(uint8_t *buf, uint32_t len, enum AUD_BITS_T bits,
                         enum AUD_CHANNEL_NUM_T ch_num) {
  if (buf == NULL) {
    return;
  }

  if ((RAND_STATUS_MIC_STARTED == randomBuffer.status) ||
      (RAND_STATUS_MIC_OPENED == randomBuffer.status)) {
    if (len >= RANDOM_CAPTURE_BUFFER_SIZE / 2) {
      RAND_TRACE(4, "%s buf address = 0x%p, bits = %d, channel num = %d",
                 __func__, buf, bits, ch_num);
      RAND_DUMP("%02x ", buf, 32);
      if ((1 == randomBuffer.skipRound) &&
          (!randDataParse(buf, len, bits, ch_num))) {
        generateRand(false);
        randomBuffer.skipRound = 0;
      } else if (1 != randomBuffer.skipRound) {
        randomBuffer.skipRound--;
      }
    }
  }
}

void randInit(void) { randInitialised = true; }

#endif
