#include "AP_Airspeed.h"

#include <AP_Common/AP_Common.h>
#include <AP_GPS/AP_GPS.h>
#include <AP_Math/AP_Math.h>
#include <GCS_MAVLink/GCS.h>
#include <AP_AHRS/AP_AHRS.h>
#include <AP_Logger/AP_Logger.h>

void AP_Airspeed::check_sensor_failures()
{
#ifndef HAL_BUILD_AP_PERIPH
    for (uint8_t i=0; i<AIRSPEED_MAX_SENSORS; i++) {
        check_sensor_ahrs_wind_max_failures(i);
    }
#endif
}

void AP_Airspeed::check_sensor_ahrs_wind_max_failures(uint8_t i)
{
#ifndef HAL_BUILD_AP_PERIPH
    const uint32_t now_ms = AP_HAL::millis();
    if ((now_ms - state[i].failures.last_check_ms) <= 200) {
        // slow the checking rate
        return;
    }
    state[i].failures.last_check_ms = now_ms;

    if (!is_positive(_wind_max) && !is_positive(_wind_gate)) {
        // nothing to do
        return;
    }

    if (state[i].airspeed <= 0) {
        // invalid estimate
        return;
    }

    const AP_GPS &gps = AP::gps();
    if (gps.status() < AP_GPS::GPS_Status::GPS_OK_FIX_3D) {
        // GPS speed can't be trusted, re-enable airspeed as a fallback
        if ((param[i].use == 0) && (state[i].failures.param_use_backup == 1)) {
            GCS_SEND_TEXT(MAV_SEVERITY_NOTICE, "Airspeed sensor %d, Re-enabled as GPS fall-back", i+1);
            param[i].use.set_and_notify(state[i].failures.param_use_backup); 
            state[i].failures.param_use_backup = -1;
        }
        return;
    }

    // check for airspeed consistent with wind and vehicle velocity using the EKF
    uint32_t age_ms;
    float innovation, innovationVariance;
    if (AP::ahrs().airspeed_health_data(innovation, innovationVariance, age_ms) && age_ms < 1000 && is_positive(innovationVariance)) {
        state[i].failures.test_ratio = fabsf(innovation) / safe_sqrt(innovationVariance);
    } else {
        state[i].failures.test_ratio = 0.0f;
    }
    bool data_is_inconsistent = false;
    if (is_positive(_wind_gate) && (AP_Airspeed::OptionsMask::USE_EKF_CONSISTENCY & _options) != 0) {
        float gate_size = MAX(_wind_gate, 0.0f);
        if (param[i].use == 0) {
            // require a smaller inconsistency for a disabled sensor to be declared consistent
            gate_size *= 0.7f;
        }
        data_is_inconsistent = state[i].failures.test_ratio > gate_size;
    }

    const float speed_diff = fabsf(state[i].airspeed-gps.ground_speed());
    const bool data_is_implausible = is_positive(_wind_max) && speed_diff > _wind_max;
    // update health_probability with LowPassFilter
    if (data_is_implausible || data_is_inconsistent) {
        // bad, decay fast
        const float probability_coeff = 0.90f;
        state[i].failures.health_probability = probability_coeff*state[i].failures.health_probability;

    } else if (!data_is_implausible && !data_is_inconsistent) {
        // good, grow slow
        const float probability_coeff = 0.98f;
        state[i].failures.health_probability = probability_coeff*state[i].failures.health_probability + (1.0f-probability_coeff)*1.0f;
    }

    // Now check if we need to disable or enable the sensor

    // here are some probability thresholds
    static const float DISABLE_PROB_THRESH_CRIT = 0.1f;
    static const float RE_ENABLE_PROB_THRESH_OK = 0.95f;

    if (param[i].use > 0) {
        if (((AP_Airspeed::OptionsMask::ON_FAILURE_AHRS_WIND_MAX_DO_DISABLE & _options) != 0) &&
                (state[i].failures.health_probability < DISABLE_PROB_THRESH_CRIT)) {
            // if "disable" option is allowed and sensor is enabled and is probably not healthy
            GCS_SEND_TEXT(MAV_SEVERITY_ERROR, "Airspeed sensor %d failure. Disabling", i+1);
            state[i].failures.param_use_backup = param[i].use;
            param[i].use.set_and_notify(0);
            state[i].healthy = false;
        }

        // Warn if necessary

        // set warn to max if not set or larger than max
        float wind_warn = _wind_warn;
        if ((!is_positive(wind_warn) || (wind_warn > _wind_max)) && is_positive(_wind_max)) {
            wind_warn = _wind_max;
        }

        if (is_positive(wind_warn) && (speed_diff > wind_warn) && ((now_ms - state[i].failures.last_warn_ms) > 15000)) {
            state[i].failures.last_warn_ms = now_ms;
            GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "Airspeed %d warning %0.1fm/s air to gnd speed diff", i+1, speed_diff);
        }

    // if Re-Enable options is allowed, and sensor is disabled but was previously enabled, and is probably healthy
    } else if (((AP_Airspeed::OptionsMask::ON_FAILURE_AHRS_WIND_MAX_RECOVERY_DO_REENABLE & _options) != 0) &&
                (state[i].failures.param_use_backup > 0) && 
                (state[i].failures.health_probability > RE_ENABLE_PROB_THRESH_OK)) {

        GCS_SEND_TEXT(MAV_SEVERITY_NOTICE, "Airspeed sensor %d now OK. Re-enabled", i+1);
        param[i].use.set_and_notify(state[i].failures.param_use_backup); // resume
        state[i].failures.param_use_backup = -1; // set to invalid so we don't use it
    }
#endif // HAL_BUILD_AP_PERIPH
}