/*
 * Copyright 2016 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#define LOG_TAG "a2dp_vendor_ldac_decoder"
#define ATRACE_TAG ATRACE_TAG_AUDIO

#include "a2dp_vendor_ldac_decoder.h"

#ifndef OS_GENERIC
#include <cutils/trace.h>
#endif
#include <dlfcn.h>
#include <inttypes.h>
#include <stdio.h>
#include <string.h>

#include <ldacBT.h>

#include "a2dp_vendor.h"
#include "a2dp_vendor_ldac.h"
#include "bt_common.h"
#include "osi/include/log.h"
#include "osi/include/osi.h"

//
// Decoder for LDAC Source Codec
//

//
// The LDAC decoder shared library, and the functions to use
//
static const char* LDAC_DECODER_LIB_NAME = "libldacBT_dec.so";
static void* ldac_decoder_lib_handle = NULL;

static const char* LDAC_GET_HANDLE_NAME = "ldacBT_get_handle";
typedef HANDLE_LDAC_BT (*tLDAC_GET_HANDLE)(void);

static const char* LDAC_FREE_HANDLE_NAME = "ldacBT_free_handle";
typedef void (*tLDAC_FREE_HANDLE)(HANDLE_LDAC_BT hLdacParam);

static const char* LDAC_CLOSE_HANDLE_NAME = "ldacBT_close_handle";
typedef void (*tLDAC_CLOSE_HANDLE)(HANDLE_LDAC_BT hLdacParam);

static const char* LDAC_GET_VERSION_NAME = "ldacBT_get_version";
typedef int (*tLDAC_GET_VERSION)(void);

static const char* LDAC_GET_BITRATE_NAME = "ldacBT_get_bitrate";
typedef int (*tLDAC_GET_BITRATE)(HANDLE_LDAC_BT hLdacParam);

static const char* LDAC_GET_SAMPLING_FREQ_NAME = "ldacBT_get_sampling_freq";
typedef int (*tLDAC_GET_SAMPLING_FREQ)(HANDLE_LDAC_BT hLdacParam);

static const char* LDAC_INIT_HANDLE_DECODE_NAME = "ldacBT_init_handle_decode";
typedef int (*tLDAC_INIT_HANDLE_DECODE)(HANDLE_LDAC_BT hLdacParam, int cm,
                                        int sf, int var0, int var1, int var2);

static const char* LDAC_DECODE_NAME = "ldacBT_decode";
typedef int (*tLDAC_DECODE)(HANDLE_LDAC_BT hLdacBt, unsigned char* p_bs,
                            unsigned char* p_pcm, LDACBT_SMPL_FMT_T fmt,
                            int bs_bytes, int* used_bytes, int* wrote_bytes);

static const char* LDAC_GET_ERROR_CODE_NAME = "ldacBT_get_error_code";
typedef int (*tLDAC_GET_ERROR_CODE)(HANDLE_LDAC_BT hLdacParam);

static tLDAC_GET_HANDLE ldac_get_handle_func;
static tLDAC_FREE_HANDLE ldac_free_handle_func;
static tLDAC_CLOSE_HANDLE ldac_close_handle_func;
static tLDAC_GET_VERSION ldac_get_version_func;
static tLDAC_GET_BITRATE ldac_get_bitrate_func;
static tLDAC_GET_SAMPLING_FREQ ldac_get_sampling_freq_func;
static tLDAC_INIT_HANDLE_DECODE ldac_init_handle_decode_func;
static tLDAC_DECODE ldac_decode_func;
static tLDAC_GET_ERROR_CODE ldac_get_error_code_func;

// offset
#if (BTA_AV_CO_CP_SCMS_T == TRUE)
#define A2DP_LDAC_OFFSET (AVDT_MEDIA_OFFSET + A2DP_LDAC_MPL_HDR_LEN + 1)
#else
#define A2DP_LDAC_OFFSET (AVDT_MEDIA_OFFSET + A2DP_LDAC_MPL_HDR_LEN)
#endif

typedef struct {
  uint32_t sample_rate;
  uint8_t channel_mode;
  uint8_t bits_per_sample;
  int pcm_wlength;
  LDACBT_SMPL_FMT_T pcm_fmt;
} tA2DP_LDAC_DECODER_PARAMS;

typedef struct {
  bool use_SCMS_T;
  bool is_peer_edr;          // True if the peer device supports EDR
  bool peer_supports_3mbps;  // True if the peer device supports 3Mbps EDR
  uint16_t peer_mtu;         // MTU of the A2DP peer
  uint32_t timestamp;        // Timestamp for the A2DP frames

  HANDLE_LDAC_BT ldac_handle;
  bool has_ldac_handle;  // True if ldac_handle is valid
  unsigned char* decode_buf;
  decoded_data_callback_t decode_callback;
} tA2DP_LDAC_DECODER_CB;

static tA2DP_LDAC_DECODER_CB a2dp_ldac_decoder_cb;

static void* load_func(const char* func_name) {
  void* func_ptr = dlsym(ldac_decoder_lib_handle, func_name);
  if (func_ptr == NULL) {
    LOG_ERROR(LOG_TAG,
              "%s: cannot find function '%s' in the decoder library: %s",
              __func__, func_name, dlerror());
    A2DP_VendorUnloadDecoderLdac();
    return NULL;
  }
  return func_ptr;
}

bool A2DP_VendorLoadDecoderLdac(void) {
  if (ldac_decoder_lib_handle != NULL) return true;  // Already loaded

  // Initialize the control block
  memset(&a2dp_ldac_decoder_cb, 0, sizeof(a2dp_ldac_decoder_cb));

  // Open the decoder library
  ldac_decoder_lib_handle = dlopen(LDAC_DECODER_LIB_NAME, RTLD_NOW);
  if (ldac_decoder_lib_handle == NULL) {
    LOG_ERROR(LOG_TAG, "%s: cannot open LDAC decoder library %s: %s", __func__,
              LDAC_DECODER_LIB_NAME, dlerror());
    return false;
  }

  // Load all functions
  ldac_get_handle_func = (tLDAC_GET_HANDLE)load_func(LDAC_GET_HANDLE_NAME);
  if (ldac_get_handle_func == NULL) return false;
  ldac_free_handle_func = (tLDAC_FREE_HANDLE)load_func(LDAC_FREE_HANDLE_NAME);
  if (ldac_free_handle_func == NULL) return false;
  ldac_close_handle_func =
      (tLDAC_CLOSE_HANDLE)load_func(LDAC_CLOSE_HANDLE_NAME);
  if (ldac_close_handle_func == NULL) return false;
  ldac_get_version_func = (tLDAC_GET_VERSION)load_func(LDAC_GET_VERSION_NAME);
  if (ldac_get_version_func == NULL) return false;
  ldac_get_bitrate_func = (tLDAC_GET_BITRATE)load_func(LDAC_GET_BITRATE_NAME);
  if (ldac_get_bitrate_func == NULL) return false;
  ldac_get_sampling_freq_func =
      (tLDAC_GET_SAMPLING_FREQ)load_func(LDAC_GET_SAMPLING_FREQ_NAME);
  if (ldac_get_sampling_freq_func == NULL) return false;
  ldac_init_handle_decode_func =
      (tLDAC_INIT_HANDLE_DECODE)load_func(LDAC_INIT_HANDLE_DECODE_NAME);
  if (ldac_init_handle_decode_func == NULL) return false;
  ldac_decode_func = (tLDAC_DECODE)load_func(LDAC_DECODE_NAME);
  if (ldac_decode_func == NULL) return false;
  ldac_get_error_code_func =
      (tLDAC_GET_ERROR_CODE)load_func(LDAC_GET_ERROR_CODE_NAME);
  if (ldac_get_error_code_func == NULL) return false;

  return true;
}

void A2DP_VendorUnloadDecoderLdac(void) {
  // Cleanup any LDAC-related state
  if (a2dp_ldac_decoder_cb.has_ldac_handle && ldac_free_handle_func != NULL)
    ldac_free_handle_func(a2dp_ldac_decoder_cb.ldac_handle);
  memset(&a2dp_ldac_decoder_cb, 0, sizeof(a2dp_ldac_decoder_cb));

  ldac_get_handle_func = NULL;
  ldac_free_handle_func = NULL;
  ldac_close_handle_func = NULL;
  ldac_get_version_func = NULL;
  ldac_get_bitrate_func = NULL;
  ldac_get_sampling_freq_func = NULL;
  ldac_init_handle_decode_func = NULL;
  ldac_decode_func = NULL;
  ldac_get_error_code_func = NULL;

  if (ldac_decoder_lib_handle != NULL) {
    dlclose(ldac_decoder_lib_handle);
    ldac_decoder_lib_handle = NULL;
  }
}

bool a2dp_vendor_ldac_decoder_init(decoded_data_callback_t decode_callback) {
  if (a2dp_ldac_decoder_cb.has_ldac_handle)
    ldac_free_handle_func(a2dp_ldac_decoder_cb.ldac_handle);
  memset(&a2dp_ldac_decoder_cb, 0, sizeof(a2dp_ldac_decoder_cb));

  a2dp_vendor_ldac_decoder_cleanup();

  a2dp_ldac_decoder_cb.ldac_handle = ldac_get_handle_func();
  a2dp_ldac_decoder_cb.has_ldac_handle = true;
  a2dp_ldac_decoder_cb.decode_buf = static_cast<unsigned char*>(
      osi_malloc(sizeof(a2dp_ldac_decoder_cb.decode_buf[0]) * LDACBT_MAX_LSU *
                 LDAC_PRCNCH * sizeof(int)));
  a2dp_ldac_decoder_cb.decode_callback = decode_callback;

  // initialize
  ldac_init_handle_decode_func(a2dp_ldac_decoder_cb.ldac_handle,
                               LDACBT_CHANNEL_MODE_STEREO, 96000, 0, 0, 0);
  return true;
}

void a2dp_vendor_ldac_decoder_cleanup(void) {
  if (a2dp_ldac_decoder_cb.has_ldac_handle)
    ldac_free_handle_func(a2dp_ldac_decoder_cb.ldac_handle);
  memset(&a2dp_ldac_decoder_cb, 0, sizeof(a2dp_ldac_decoder_cb));
}

bool a2dp_vendor_ldac_decoder_decode_packet(BT_HDR* p_buf) {
  unsigned char* pBuffer =
      reinterpret_cast<unsigned char*>(p_buf->data + p_buf->offset);
  //  unsigned int bufferSize = p_buf->len;
  unsigned int bytesValid = p_buf->len;
  int err;
  LDACBT_SMPL_FMT_T fmt;
  int bs_bytes, used_bytes, wrote_bytes, frame_number;

  fmt = LDACBT_SMPL_FMT_S32;
  frame_number = (int)pBuffer[0];
  pBuffer++;
  bs_bytes = (int)bytesValid - 1;
  bytesValid -= 1;
  LOG_VERBOSE(LOG_TAG, "%s:INPUT size : %d, frame : %d", __func__, bs_bytes,
              frame_number);

  while (bytesValid > 0) {
#if 0
    err = ldacDecoder_Fill(a2dp_ldac_decoder_cb.ldac_handle,
                               &pBuffer, &bufferSize, &bytesValid);
    if (err != LDACBT_ERR_NONE) {
      LOG_ERROR(LOG_TAG, "%s: ldacDecoder_Fill failed: 0x%x", __func__,
                static_cast<unsigned>(err));
      return false;
    }
#endif
    while (true) {
      // Todo : implement LDAC Buffer Control Operation instead of
      // ldac_decode_func().
      err = ldac_decode_func(a2dp_ldac_decoder_cb.ldac_handle, pBuffer,
                             a2dp_ldac_decoder_cb.decode_buf, fmt, bs_bytes,
                             &used_bytes, &wrote_bytes);
      //      if (err == LDAC_DEC_NOT_ENOUGH_FRAMES) {
      //        break;
      //      }
      if (LDACBT_ERROR(err)) {
        err = ldac_get_error_code_func(a2dp_ldac_decoder_cb.ldac_handle);
        LOG_ERROR(LOG_TAG, "%s: ldacDecoder_DecodeFrame failed: %d:%d:%d",
                  __func__, LDACBT_API_ERR(err), LDACBT_HANDLE_ERR(err),
                  LDACBT_BLOCK_ERR(err));
        if (LDACBT_FATAL(err)) {
          break;
        }
      }

      if (wrote_bytes > 0) {
        size_t frame_len = (size_t)wrote_bytes;
        a2dp_ldac_decoder_cb.decode_callback(
            reinterpret_cast<uint8_t*>(a2dp_ldac_decoder_cb.decode_buf),
            frame_len);
      }
      pBuffer += used_bytes;
      bs_bytes -= used_bytes;
      if (bs_bytes <= 1) {
        bytesValid = 0;
        break;
      }
    }
  }

  return true;
}