#pragma once

#include <Arduino.h>

#include "config.h"

class FanCharacterize {
public:
  enum class Phase : uint8_t { Idle, Precool, Heat, Hold, Cooldown, Done, Failed };

  FanCharacterize();

  Phase phase() const { return phase_; }

  bool isActive() const;

  uint32_t elapsedMs(uint32_t nowMs) const;

  uint8_t profileIndex() const { return profileIndex_; }

  uint8_t profileCount() const { return FANCHARS_COARSE_COUNT + 1; }

  uint8_t currentFanPwm() const;

  float heaterPct() const { return heaterPct_; }

  uint8_t winnerFanPwm() const { return winnerFanPwm_; }

  bool isRefineRun() const { return refineRun_ && profileIndex_ >= FANCHARS_COARSE_COUNT; }

  const char *phaseName() const;

  bool start(float maxCornerC, float avgTempC);

  void abort();

  void reset();

  bool update(float avgTempC, float maxTempC, float spreadC, uint32_t nowMs, float &heaterDutyOut,
              uint8_t &fanPwmOut);

  void logIfDue(const float *sensorTemps, const bool *sensorValid, uint8_t sensorCount,
                float avgTempC, float minTempC, float maxTempC, float spreadC, uint32_t nowMs);

private:
  struct ProfileResult {
    uint8_t fanPwm;
    float meanSpreadC;
  };

  void resetProfileStats();
  void beginProfileHeat(uint32_t nowMs);
  void enterHold(uint32_t nowMs);
  void finishProfile(uint32_t nowMs);
  void skipProfile(uint32_t nowMs, float maxTempC);
  void planRefine(uint32_t nowMs);
  void finishAll(uint32_t nowMs);
  void fail(const __FlashStringHelper *reason);

  Phase phase_;
  float maxCornerC_;
  float coolAvgC_;
  float heaterPct_;
  uint8_t profileIndex_;
  uint8_t refineFanPwm_;
  bool refineRun_;
  uint32_t sessionStartMs_;
  uint32_t phaseStartMs_;
  uint32_t lastLogMs_;
  float spreadSum_;
  uint16_t spreadSamples_;
  uint8_t resultCount_;
  uint8_t winnerFanPwm_;
  ProfileResult results_[FANCHARS_MAX_RESULTS];
};
