#include "env_sens.h"

#include "nav.h"
#include "sens_fus.h"

#include "esp_log.h"
#include <Adafruit_BME280.h>
#include <SPI.h>
#include <Wire.h>
#include <cmath>

#include "freertos/idf_additions.h"

#define SEALEVELPRESSURE_HPA (1030)

Adafruit_BME280 bme; // use I2C interface
Adafruit_Sensor *bme_temp = bme.getTemperatureSensor();
Adafruit_Sensor *bme_pressure = bme.getPressureSensor();
Adafruit_Sensor *bme_humidity = bme.getHumiditySensor();

static const constexpr char *TAG = "BARO";

#define BARO_SDA GPIO_NUM_47 // SDI
#define BARO_SCL GPIO_NUM_48

namespace env_sens {
void setup() {

  baro_mutex = xSemaphoreCreateMutex();

  TwoWire *wire = new TwoWire(0);

  wire->begin(BARO_SDA, BARO_SCL);

  if (!bme.begin(119, wire)) {

    ESP_LOGE(TAG, "Couldn't find a valid sensor");

    ESP.restart();
    return;
  }
  ESP_LOGI(TAG, "BARO SETUP COMPLETE.");
  bme.setSampling(Adafruit_BME280::MODE_NORMAL, Adafruit_BME280::SAMPLING_X1,
                  Adafruit_BME280::SAMPLING_X1, Adafruit_BME280::SAMPLING_NONE,
                  Adafruit_BME280::FILTER_OFF, Adafruit_BME280::STANDBY_MS_500);

  bme_temp->printSensorDetails();
  bme_pressure->printSensorDetails();
  bme_humidity->printSensorDetails();
}

float get_temperature() {

  if (millis() - env_sens::time_term_read > 20) {

    sensors_event_t temp_event;
    if (baro_mutex && xSemaphoreTake(baro_mutex, 30)) {

      bme_temp->getEvent(&temp_event);
      xSemaphoreGive(baro_mutex);
    }

    return temp_event.temperature;
  }
  return env_sens::term_read;
}

float get_pressure() {

  if (millis() - env_sens::time_baro_read > 20) {

    sensors_event_t e;
    if (baro_mutex && xSemaphoreTake(baro_mutex, 30)) {

      bme_pressure->getEvent(&e);
      xSemaphoreGive(baro_mutex);
    }

    return e.pressure;
  }
  return env_sens::baro_read;
}

float calculateAltitude(float pressure, float seaLevelPressure,
                        float tempCelsius) {
  float altitude =
      (((std::pow((seaLevelPressure / pressure), (1.0 / 5.257))) - 1.0) *
       (tempCelsius + 273.15)) /
      0.0065;
  return altitude;
}

float get_altitude() {
  return calculateAltitude(get_pressure(), SEALEVELPRESSURE_HPA,
                           get_temperature());
}

void dbg_sens() {
  ESP_LOGI(TAG, "T (ºC): %f, P (hPa): %f, Alt (m): %f", get_temperature(),
           get_pressure(), get_altitude());
}

void baro_poll_task(void *_) {
  env_sens::setup();

  float last_alt = env_sens::get_altitude();
  uint32_t last_time = xTaskGetTickCount();

  float filtered_alt = last_alt;
  const float alt_lpf = 0.1f;

  while (true) {
    uint32_t now = xTaskGetTickCount();
    float dt = (now - last_time) * portTICK_PERIOD_MS / 1000.0f;

    if (dt > 0.001f) { // Prevent division by zero
      float current_alt = env_sens::get_altitude();
      if (current_alt == INFINITY) {
        continue;
      }

      filtered_alt = (alt_lpf * current_alt) + (1.0f - alt_lpf) * filtered_alt;

      float v_z = (filtered_alt - last_alt) / dt;

      Eigen::Vector3f baro_pos = sens_fus.position;
      baro_pos.z() = filtered_alt;

      Eigen::Vector3f baro_vel = sens_fus.velocity;
      baro_vel.z() = v_z;
      if (sens_fus_mutex &&
          xSemaphoreTake(sens_fus_mutex, (TickType_t)20) == pdTRUE) {

        // Update the filter with Baro data
        sens_fus.measure_baro(dt, baro_pos, baro_vel);

        xSemaphoreGive(sens_fus_mutex);
      }

      last_alt = filtered_alt;
      last_time = now;
    }

    // BME280 config has a 20ms standby, so 20ms-50ms poll is ideal
    vTaskDelay(pdMS_TO_TICKS(500));
  }
}

} // namespace env_sens
