#pragma once

#include "esphome/components/i2c/i2c.h"
#include "esphome/components/sensor/sensor.h"
#include "esphome/core/component.h"
#include "esphome/core/hal.h"
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"

namespace esphome::qmp6988 {

/* oversampling */
enum QMP6988Oversampling : uint8_t {
  QMP6988_OVERSAMPLING_SKIPPED = 0x00,
  QMP6988_OVERSAMPLING_1X = 0x01,
  QMP6988_OVERSAMPLING_2X = 0x02,
  QMP6988_OVERSAMPLING_4X = 0x03,
  QMP6988_OVERSAMPLING_8X = 0x04,
  QMP6988_OVERSAMPLING_16X = 0x05,
  QMP6988_OVERSAMPLING_32X = 0x06,
  QMP6988_OVERSAMPLING_64X = 0x07,
};

/* filter */
enum QMP6988IIRFilter : uint8_t {
  QMP6988_IIR_FILTER_OFF = 0x00,
  QMP6988_IIR_FILTER_2X = 0x01,
  QMP6988_IIR_FILTER_4X = 0x02,
  QMP6988_IIR_FILTER_8X = 0x03,
  QMP6988_IIR_FILTER_16X = 0x04,
  QMP6988_IIR_FILTER_32X = 0x05,
};

using qmp6988_cali_data_t = struct Qmp6988CaliData {
  int32_t COE_a0;
  int16_t COE_a1;
  int16_t COE_a2;
  int32_t COE_b00;
  int16_t COE_bt1;
  int16_t COE_bt2;
  int16_t COE_bp1;
  int16_t COE_b11;
  int16_t COE_bp2;
  int16_t COE_b12;
  int16_t COE_b21;
  int16_t COE_bp3;
};

using qmp6988_fk_data_t = struct Qmp6988FkData {
  float a0, b00;
  float a1, a2, bt1, bt2, bp1, b11, bp2, b12, b21, bp3;
};

using qmp6988_ik_data_t = struct Qmp6988IkData {
  int32_t a0, b00;
  int32_t a1, a2;
  int64_t bt1, bt2, bp1, b11, bp2, b12, b21, bp3;
};

using qmp6988_data_t = struct Qmp6988Data {
  uint8_t chip_id;
  uint8_t power_mode;
  float temperature;
  float pressure;
  float altitude;
  qmp6988_cali_data_t qmp6988_cali;
  qmp6988_ik_data_t ik;
};

class QMP6988Component : public PollingComponent, public i2c::I2CDevice {
 public:
  void set_temperature_sensor(sensor::Sensor *temperature_sensor) { this->temperature_sensor_ = temperature_sensor; }
  void set_pressure_sensor(sensor::Sensor *pressure_sensor) { this->pressure_sensor_ = pressure_sensor; }

  void setup() override;
  void dump_config() override;
  void update() override;

  void set_iir_filter(QMP6988IIRFilter iirfilter) { this->iir_filter_ = iirfilter; }
  void set_temperature_oversampling(QMP6988Oversampling oversampling_t) {
    this->temperature_oversampling_ = oversampling_t;
  }
  void set_pressure_oversampling(QMP6988Oversampling oversampling_p) { this->pressure_oversampling_ = oversampling_p; }

 protected:
  qmp6988_data_t qmp6988_data_;
  sensor::Sensor *temperature_sensor_{nullptr};
  sensor::Sensor *pressure_sensor_{nullptr};

  QMP6988Oversampling temperature_oversampling_{QMP6988_OVERSAMPLING_16X};
  QMP6988Oversampling pressure_oversampling_{QMP6988_OVERSAMPLING_16X};
  QMP6988IIRFilter iir_filter_{QMP6988_IIR_FILTER_OFF};

  void software_reset_();
  bool get_calibration_data_();
  bool device_check_();
  void set_power_mode_(uint8_t power_mode);
  void write_oversampling_temperature_(QMP6988Oversampling oversampling_t);
  void write_oversampling_pressure_(QMP6988Oversampling oversampling_p);
  void write_filter_(QMP6988IIRFilter filter);
  void calculate_pressure_();
  void calculate_altitude_(float pressure, float temp);

  int32_t get_compensated_pressure_(qmp6988_ik_data_t *ik, int32_t dp, int16_t tx);
  int16_t get_compensated_temperature_(qmp6988_ik_data_t *ik, int32_t dt);
};

}  // namespace esphome::qmp6988
