/*
 * Copyright 2021-2023 NXP
 * Copyright 2024 Google
 *
 * 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.
 */

#include "phNxpUciHal_ext.h"
#include "phNxpUwbCalib.h"
#include "phUwbStatus.h"

//
// SR1XX Device Calibrations:
//
// Based on NXP SR1xx UCI v2.0.5
// current HAL impl only supports "xtal" read from otp
// others should be existed in .conf files

static tHAL_UWB_STATUS sr1xx_set_calibration(uint8_t channel, const std::vector<uint8_t> &tlv)
{
  // SET_CALIBRATION_CMD header: GID=0xF OID=0x21
  std::vector<uint8_t> packet({ (0x20 | UCI_GID_PROPRIETARY_0X0F), UCI_MSG_SET_DEVICE_CALIBRATION, 0x00, 0x00});

  // use 9 for channel-independent parameters
  if (!channel) {
    channel = 9;
  }
  packet.push_back(channel);
  packet.insert(packet.end(), tlv.begin(), tlv.end());
  packet[3] = packet.size() - 4;
  return phNxpUciHal_send_ext_cmd(packet.size(), packet.data());
}

static tHAL_UWB_STATUS sr1xx_set_conf(const std::vector<uint8_t> &tlv)
{
  // SET_CALIBRATION_CMD header: GID=0xF OID=0x21
  std::vector<uint8_t> packet({ (0x20 | UCI_GID_CORE), UCI_MSG_CORE_SET_CONFIG, 0x00, 0x00});
  packet.push_back(1);  // number of parameters
  packet.insert(packet.end(), tlv.begin(), tlv.end());
  packet[3] = packet.size() - 4;
  return phNxpUciHal_send_ext_cmd(packet.size(), packet.data());
}

tHAL_UWB_STATUS sr1xx_apply_calibration(extcal_param_id_t id, const uint8_t ch, const uint8_t *data, size_t data_len)
{
  // Device Calibration
  const uint8_t UCI_PARAM_ID_RF_CLK_ACCURACY_CALIB    = 0x01;
  const uint8_t UCI_PARAM_ID_RX_ANT_DELAY_CALIB       = 0x02;
  const uint8_t UCI_PARAM_ID_TX_POWER_PER_ANTENNA     = 0x04;

  // Device Configurations
  const uint16_t UCI_PARAM_ID_TX_BASE_BAND_CONFIG     = 0xe426;
  const uint16_t UCI_PARAM_ID_DDFS_TONE_CONFIG        = 0xe427;
  const uint16_t UCI_PARAM_ID_TX_PULSE_SHAPE_CONFIG   = 0xe428;

  switch (id) {
  case EXTCAL_PARAM_CLK_ACCURACY:
    {
      if (data_len != 6) {
        return UWBSTATUS_FAILED;
      }

      std::vector<uint8_t> tlv;
      // Tag
      tlv.push_back(UCI_PARAM_ID_RF_CLK_ACCURACY_CALIB);
      // Length
      tlv.push_back((uint8_t)data_len + 1);
      // Value
      tlv.push_back(3); // number of register (must be 0x03)
      tlv.insert(tlv.end(), data, data + data_len);

      return sr1xx_set_calibration(ch, tlv);
    }
  case EXTCAL_PARAM_RX_ANT_DELAY:
    {
      // [0] = number of entries
      // {
      //   [0] = rx antenna id
      //   [1,2] = rx delay
      // }
      if (!ch || data_len < 1 || !data[0] || (data[0] * 3) != (data_len - 1)) {
        return UWBSTATUS_FAILED;
      }

      std::vector<uint8_t> tlv;
      // Tag
      tlv.push_back(UCI_PARAM_ID_RX_ANT_DELAY_CALIB);
      // Length
      tlv.push_back((uint8_t)data_len);
      // Value
      tlv.insert(tlv.end(), data, data + data_len);

      return sr1xx_set_calibration(ch, tlv);
    }
  case EXTCAL_PARAM_TX_POWER:
    {
      if (!ch || data_len < 1 || !data[0] || (data[0] * 5) != (data_len - 1)) {
        return UWBSTATUS_FAILED;
      }

      std::vector<uint8_t> tlv;
      // Tag
      tlv.push_back(UCI_PARAM_ID_TX_POWER_PER_ANTENNA);
      // Length
      tlv.push_back((uint8_t)data_len);
      // Value
      tlv.insert(tlv.end(), data, data + data_len);

      return sr1xx_set_calibration(ch, tlv);
    }
  case EXTCAL_PARAM_TX_BASE_BAND_CONTROL:
    {
      if (data_len != 1) {
        return UWBSTATUS_FAILED;
      }

      std::vector<uint8_t> tlv;
      // Tag
      tlv.push_back(UCI_PARAM_ID_TX_BASE_BAND_CONFIG >> 8);
      tlv.push_back(UCI_PARAM_ID_TX_BASE_BAND_CONFIG & 0xff);
      // Length
      tlv.push_back(1);
      // Value
      tlv.push_back(data[0]);

      return sr1xx_set_conf(tlv);
    }
  case EXTCAL_PARAM_DDFS_TONE_CONFIG:
    {
      if (!data_len) {
        return UWBSTATUS_FAILED;
      }

      std::vector<uint8_t> tlv;
      // Tag
      tlv.push_back(UCI_PARAM_ID_DDFS_TONE_CONFIG >> 8);
      tlv.push_back(UCI_PARAM_ID_DDFS_TONE_CONFIG & 0xff);
      // Length
      tlv.push_back(data_len);
      // Value
      tlv.insert(tlv.end(), data, data + data_len);

      return sr1xx_set_conf(tlv);
    }
  case EXTCAL_PARAM_TX_PULSE_SHAPE:
    {
      if (!data_len) {
        return UWBSTATUS_FAILED;
      }

      std::vector<uint8_t> tlv;
      // Tag
      tlv.push_back(UCI_PARAM_ID_TX_PULSE_SHAPE_CONFIG >> 8);
      tlv.push_back(UCI_PARAM_ID_TX_PULSE_SHAPE_CONFIG & 0xff);
      // Length
      tlv.push_back(data_len);
      // Value
      tlv.insert(tlv.end(), data, data + data_len);

      return sr1xx_set_conf(tlv);
    }
  default:
    NXPLOG_UCIHAL_E("Unsupported parameter: 0x%x", id);
    return UWBSTATUS_FAILED;
  }
}
