/**
 * @file iic_drv.c
 * @author June6 (https://github.com/Junedoubling6)
 * @brief
 * @version 0.1
 * @date 2023-08-16
 *
 * @copyright Copyright (c) 2022 - , All rights reserved.
 *
 */
#include "iic_drv.h"

#include "driver/i2c.h"
#include "log.h"

/*!< IIC master doesn't need buffer */
#define IIC_MASTER_RX_BUF_DISABLE 0
/*!< IIC master doesn't need buffer */
#define IIC_MASTER_TX_BUF_DISABLE 0
/*!< IIC master timeout */
#define IIC_MASTER_TICK (IIC_MASTER_TIMEOUT_MS / portTICK_PERIOD_MS)

static const char *TAG = "iic_drv";
static bool iic_config_flag = false;

/**
 * @brief Config iic master.
 *
 * @param port_num
 * @param pin_scl
 * @param pin_sda
 * @param clk_speed
 * @return esp_err_t
 */
esp_err_t iic_master_config(int port_num, int pin_scl, int pin_sda,
                            uint32_t clk_speed) {
  esp_err_t ret = ESP_FAIL;

  if (iic_config_flag) {
    USER_LOGD(TAG, "IIC master is configured");
    return ESP_OK;
  }
  const i2c_config_t iic_conf = {
      .mode = I2C_MODE_MASTER,
      .sda_io_num = pin_sda,
      .scl_io_num = pin_scl,
      .sda_pullup_en = GPIO_PULLUP_ENABLE,
      .scl_pullup_en = GPIO_PULLUP_ENABLE,
      .master.clk_speed = clk_speed,
  };
  /* 初始化IIC */
  ret = i2c_param_config(port_num, &iic_conf);
  if (ret != ESP_OK) {
    USER_LOGE(TAG, "iic_master_config error");
    goto End;
  }
  ret = i2c_driver_install(port_num, iic_conf.mode, IIC_MASTER_RX_BUF_DISABLE,
                           IIC_MASTER_TX_BUF_DISABLE, ESP_INTR_FLAG_SHARED);
  if (ret != ESP_OK) {
    USER_LOGE(TAG, "iic_master_config error");
    goto End;
  }
  iic_config_flag = true;
  USER_LOGD(TAG, "Initialize IIC master");

End:
  return ret;
}

/**
 * @brief IIC master read to slave.
 *
 * @param port_num
 * @param dev_addr
 * @param reg_addr
 * @param data
 * @param len
 * @param ack_enable
 * @return esp_err_t
 */
esp_err_t iic_master_read_slave(int port_num, uint8_t dev_addr,
                                uint8_t reg_addr, uint8_t *data, size_t len,
                                bool ack_enable) {
  esp_err_t ret = ESP_FAIL;

  if (sizeof(data) > len) {
    USER_LOGE(TAG, "Insufficient iic read buffer space");
    goto End;
  }
  i2c_cmd_handle_t cmd_handler = i2c_cmd_link_create();
  if (cmd_handler == NULL) {
    USER_LOGE(TAG, "cmd_handler is NULL");
    goto End;
  }
  ret = i2c_master_start(cmd_handler);
  if (ret != ESP_OK) goto End;
  ret = i2c_master_write_byte(cmd_handler, (dev_addr << 1) | I2C_MASTER_WRITE,
                              ack_enable);
  if (ret != ESP_OK) goto End;
  ret = i2c_master_write_byte(cmd_handler, reg_addr, ack_enable);
  if (ret != ESP_OK) goto End;
  ret = i2c_master_start(cmd_handler);
  if (ret != ESP_OK) goto End;
  ret = i2c_master_write_byte(cmd_handler, (dev_addr << 1) | I2C_MASTER_READ,
                              ack_enable);
  if (ret != ESP_OK) goto End;
  ret = i2c_master_read(cmd_handler, data, len, I2C_MASTER_LAST_NACK);
  if (ret != ESP_OK) goto End;
  ret = i2c_master_stop(cmd_handler);
  if (ret != ESP_OK) goto End;
  ret = i2c_master_cmd_begin(port_num, cmd_handler, IIC_MASTER_TICK);
  i2c_cmd_link_delete(cmd_handler);

End:
  return ret;
}

/**
 * @brief Write a byte to a sensor register.
 *
 * @param port_num
 * @param dev_addr
 * @param reg_addr
 * @param data
 * @param len
 * @param ack_enable
 * @return esp_err_t
 * @details The maximum size of data written is 128 bytes.
 */
esp_err_t iic_master_write_slave(int port_num, uint8_t dev_addr,
                                 uint8_t reg_addr, uint8_t *data, size_t len,
                                 bool ack_enable) {
  esp_err_t ret = ESP_OK;
  uint8_t write_buf[129] = {reg_addr};
  uint8_t write_len = len + 1;

  if (len > 128) {
    USER_LOGE(TAG, "Insufficient iic write buffer space");

    return ESP_FAIL;
  }
  /* FIXME: June6 待验证并修复 */
  memcpy(&write_buf[1], data, len);
  i2c_cmd_handle_t cmd_handler = i2c_cmd_link_create();
  ret = i2c_master_start(cmd_handler);
  if (ret != ESP_OK) goto End;
  ret = i2c_master_write_byte(cmd_handler, dev_addr | I2C_MASTER_WRITE,
                              ack_enable);
  if (ret != ESP_OK) goto End;
  ret = i2c_master_write(cmd_handler, write_buf, write_len, ack_enable);
  if (ret != ESP_OK) goto End;
  ret = i2c_master_stop(cmd_handler);
  if (ret != ESP_OK) goto End;
  ret = i2c_master_cmd_begin(port_num, cmd_handler, IIC_MASTER_TICK);
  i2c_cmd_link_delete(cmd_handler);

End:
  return ret;
}
