#pragma once

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

// ref:
// https://github.com/usefulsensors/person_sensor_pico_c/blob/main/person_sensor.h

namespace esphome::sen21231_sensor {
// The I2C address of the person sensor board.
static const uint8_t PERSON_SENSOR_I2C_ADDRESS = 0x62;
static const uint8_t PERSON_SENSOR_REG_MODE = 0x01;
static const uint8_t PERSON_SENSOR_REG_ENABLE_ID = 0x02;
static const uint8_t PERSON_SENSOR_REG_SINGLE_SHOT = 0x03;
static const uint8_t PERSON_SENSOR_REG_CALIBRATE_ID = 0x04;
static const uint8_t PERSON_SENSOR_REG_PERSIST_IDS = 0x05;
static const uint8_t PERSON_SENSOR_REG_ERASE_IDS = 0x06;
static const uint8_t PERSON_SENSOR_REG_DEBUG_MODE = 0x07;

static const uint8_t PERSON_SENSOR_MAX_FACES_COUNT = 4;
static const uint8_t PERSON_SENSOR_MAX_IDS_COUNT = 7;

// The results returned from the sensor have a short header providing
// information about the length of the data packet:
//   reserved: Currently unused bytes.
//   data_size: Length of the entire packet, excluding the header and
//   checksum.
//     For version 1.0 of the sensor, this should be 40.
using person_sensor_results_header_t = struct {
  uint8_t reserved[2];  // Bytes 0-1.
  uint16_t data_size;   // Bytes 2-3.
};

// Each face found has a set of information associated with it:
//   box_confidence: How certain we are we have found a face, from 0 to 255.
//   box_left: X coordinate of the left side of the box, from 0 to 255.
//   box_top: Y coordinate of the top edge of the box, from 0 to 255.
//   box_width: Width of the box, where 255 is the full view port size.
//   box_height: Height of the box, where 255 is the full view port size.
//   id_confidence: How sure the sensor is about the recognition result.
//   id: Numerical ID assigned to this face.
//   is_looking_at: Whether the person is facing the camera, 0 or 1.
using person_sensor_face_t = struct __attribute__((__packed__)) {
  uint8_t box_confidence;  // Byte 1.
  uint8_t box_left;        // Byte 2.
  uint8_t box_top;         // Byte 3.
  uint8_t box_right;       // Byte 4.
  uint8_t box_bottom;      // Byte 5.
  int8_t id_confidence;    // Byte 6.
  int8_t id;               // Byte 7
  uint8_t is_facing;       // Byte 8.
};

// This is the full structure of the packet returned over the wire from the
// sensor when we do an I2C read from the peripheral address.
// The checksum should be the CRC16 of bytes 0 to 38. You shouldn't need to
// verify this in practice, but we found it useful during our own debugging.
using person_sensor_results_t = struct __attribute__((__packed__)) {
  person_sensor_results_header_t header;                      // Bytes 0-4.
  int8_t num_faces;                                           // Byte 5.
  person_sensor_face_t faces[PERSON_SENSOR_MAX_FACES_COUNT];  // Bytes 6-37.
  uint16_t checksum;                                          // Bytes 38-39.
};

class Sen21231Sensor final : public sensor::Sensor, public PollingComponent, public i2c::I2CDevice {
 public:
  void update() override;
  void dump_config() override;

 protected:
  void read_data_();
};

}  // namespace esphome::sen21231_sensor
