// model-based temperature safety checker declarations
#ifndef TEMP_MGR_INTV
#error "this file is not a public interface, it should be used *only* within temperature.cpp!"
#endif

#include "planner.h"

constexpr uint8_t TEMP_MODEL_CAL_S = 60;     // Maximum recording lenght during calibration (s)
constexpr uint8_t TEMP_MODEL_CAL_R_STEP = 4; // Fan interpolation steps during calibration
constexpr float TEMP_MODEL_fS = 0.065;       // simulation filter (1st-order IIR factor)
constexpr float TEMP_MODEL_fE = 0.05;        // error filter (1st-order IIR factor)

// transport delay buffer size (samples)
constexpr uint8_t TEMP_MODEL_LAG_SIZE = (TEMP_MODEL_LAG / TEMP_MGR_INTV + 0.5);

// resistance values for all fan levels
constexpr uint8_t TEMP_MODEL_R_SIZE = (1 << FAN_SOFT_PWM_BITS);
static const float TEMP_MODEL_R_DEFAULT[TEMP_MODEL_R_SIZE] PROGMEM = TEMP_MODEL_Rv;

namespace temp_model {

struct model_data
{
    // temporary buffers
    float dT_lag_buf[TEMP_MODEL_LAG_SIZE]; // transport delay buffer
    uint8_t dT_lag_idx = 0;                // transport delay buffer index
    float dT_err_prev = 0;                 // previous temperature delta error
    float T_prev = 0;                      // last temperature extruder

    // configurable parameters
    float P;                               // heater power (W)
    float C;                               // heatblock capacitance (J/K)
    float R[TEMP_MODEL_R_SIZE];            // heatblock resistance for all fan levels (K/W)
    float Ta_corr;                         // ambient temperature correction (K)

    // thresholds
    float warn;                            // warning threshold (K/s)
    float err;                             // error threshold (K/s)

    // status flags
    union
    {
        bool flags;
        struct
        {
            bool uninitialized: 1;         // model is not initialized
            bool error: 1;                 // error threshold set
            bool warning: 1;               // warning threshold set
        } flag_bits;
    };

    // pre-computed values (initialized via reset)
    float C_i;                             // heatblock capacitance (precomputed dT/C)
    float warn_s;                          // warning threshold (per sample)
    float err_s;                           // error threshold (per sample)

    // simulation functions
    void reset(uint8_t heater_pwm, uint8_t fan_pwm, float heater_temp, float ambient_temp);
    void step(uint8_t heater_pwm, uint8_t fan_pwm, float heater_temp, float ambient_temp);
};

static bool enabled;          // model check enabled
static bool warn_beep = true; // beep on warning threshold
static model_data data;       // default heater data

static bool calibrated(); // return calibration/model validity status
static void check();      // check and trigger errors or warnings based on current state

// warning state (updated from from isr context)
volatile static struct
{
    float dT_err;    // temperature delta error (per sample)
    bool warning: 1; // warning condition
    bool assert: 1;  // warning is still asserted
} warning_state;

static void handle_warning(); // handle warnings from user context

#ifdef TEMP_MODEL_DEBUG
static struct
{
    volatile struct
    {
        uint32_t stamp;
        int8_t delta_ms;
        uint8_t counter;
        uint8_t cur_pwm;
        float cur_temp;
        float cur_amb;
    } entry;

    uint8_t serial;
    bool enabled;
} log_buf;

static void log_usr(); // user log handler
static void log_isr(); // isr log handler
#endif

} // namespace temp_model

namespace temp_model_cal {

// recording scratch buffer
struct rec_entry
{
    float temp;  // heater temperature
    uint8_t pwm; // heater PWM
};

constexpr uint16_t REC_BUFFER_SIZE = TEMP_MODEL_CAL_S / TEMP_MGR_INTV;
static rec_entry* const rec_buffer = (rec_entry*)block_buffer; // oh-hey, free memory!
static_assert(sizeof(rec_entry[REC_BUFFER_SIZE]) <= sizeof(block_buffer),
    "recording length too long to fit within available buffer");

} // namespace temp_model_cal