#ifndef FanStateService_h
#define FanStateService_h

#include <FSPersistence.h>
#include <HttpEndpoint.h>
#include <FanMqttSettingsService.h>
#include <MqttPubSub.h>

#include <OneWire.h>
#include <DallasTemperature.h>

#define PRINT_DELAY 5000

#define DEFAULT_FAN_STATE        false
#define DEFAULT_FAN_PWM_GPIO     12
#define DEFAULT_FAN_TACH_GPIO    13
#define DEFAULT_ONEWIRE_GPIO     14
#define DEFAULT_THRES_LOW        25
#define DEFAULT_THRES_HIGH       70
#define DEFAULT_FAN_MAX_SPEED    1021
#define OFF_STATE                "OFF"
#define ON_STATE                 "ON"

// Note that the built-in LED is on when the pin is low on most NodeMCU boards.
// This is because the anode is tied to VCC and the cathode to the GPIO 4 (Arduino pin 2).
#ifdef ESP32
#define FAN_ON 0x1
#define FAN_OFF 0x0
#elif defined(ESP8266)
#define FAN_ON 0x0
#define FAN_OFF 0x1
#endif

#define FAN_SETTINGS_FILE "/config/fanSettings.json"
#define FAN_SETTINGS_ENDPOINT_PATH "/rest/fanState"


class FanState {
 public:
  bool fanStatus;
  int  fanSpeed;
  int  fanPwmGPIO;
  int  fanTachGPIO;
  int  fanMaxSpeed;
  int  thresLow;
  int  thresHigh;

  int   OneWireGPIO;
  float sensorInTemp;
  float sensorOutTemp;

  static void read(FanState& settings, JsonObject& root) {
    root["oneWire_gpio"]            = settings.OneWireGPIO;
    root["sensor_in_temp"]          = settings.sensorInTemp;
    root["sensor_out_temp"]         = settings.sensorOutTemp;
    root["fan_status"]              = settings.fanStatus;
    root["fan_pwm_gpio"]            = settings.fanPwmGPIO;
    root["fan_tach_gpio"]           = settings.fanTachGPIO;
    root["fan_max_speed"]           = settings.fanMaxSpeed;
    root["temperature_thres_low"]   = settings.thresLow;
    root["temperature_thres_high"]  = settings.thresHigh;
    // String s = (String)root["fan_status"] + "|" + (String)root["fan_gpio"] + "|" + (String)root["fan_max_speed"] + "|" + (String)root["temperature_thres_low"] + "|" + (String)root["temperature_thres_high"];
    String s = root["fan_status"];             Serial.println(" ** Fan read [fan_status             : " + s + "]");
    String t = root["fan_pwm_gpio"];           Serial.println(" ** Fan read [fan_pwm_gpio           : " + t + "]");
    String u = root["fan_tach_gpio"];          Serial.println(" ** Fan read [fan_tach_gpio          : " + u + "]");
    String v = root["fan_max_speed"];          Serial.println(" ** Fan read [fan_max_speed          : " + v + "]");
    String w = root["temperature_thres_low"];  Serial.println(" ** Fan read [temperature_thres_low  : " + w + "]");
    String x = root["temperature_thres_high"]; Serial.println(" ** Fan read [temperature_thres_high : " + x + "]");
    String y = root["oneWire_gpio"];           Serial.println(" ** Fan read [oneWire_gpio           : " + y + "]");
  }

  static StateUpdateResult update(JsonObject& root, FanState& fanState) {
    boolean newFanState     = root["fan_status"]              | DEFAULT_FAN_STATE;
    int     newFanPwmGPIO   = root["fan_pwm_gpio"]            | DEFAULT_FAN_PWM_GPIO;
    int     newFanTachGPIO  = root["fan_tach_gpio"]           | DEFAULT_FAN_TACH_GPIO;
    int     newMaxSpeed     = root["fan_max_speed"]           | DEFAULT_FAN_MAX_SPEED;
    int     newThresLow     = root["temperature_thres_low"]   | DEFAULT_THRES_LOW;
    int     newThresHigh    = root["temperature_thres_high"]  | DEFAULT_THRES_HIGH;
    int     newOneWireGPIO  = root["oneWire_gpio"]            | DEFAULT_ONEWIRE_GPIO;
    Serial.println(" ** Fan update [status:" + (String)newFanState + "|gpio:" + (String)newFanPwmGPIO + "|mxspd:" + (String)newMaxSpeed + "|low:" + (String)newThresLow + "|high:" + (String)newThresHigh + "]");
    if (  fanState.fanStatus   != newFanState 
       || fanState.fanPwmGPIO  != newFanPwmGPIO
       || fanState.fanTachGPIO != newFanTachGPIO
       || fanState.fanMaxSpeed != newMaxSpeed 
       || fanState.thresLow    != newThresLow 
       || fanState.thresHigh   != newThresHigh
       || fanState.OneWireGPIO != newOneWireGPIO  ) {
      fanState.fanStatus   = newFanState;
      fanState.fanPwmGPIO  = newFanPwmGPIO;
      fanState.fanTachGPIO = newFanTachGPIO;
      fanState.fanMaxSpeed = newMaxSpeed;
      fanState.thresLow    = newThresLow;
      fanState.thresHigh   = newThresHigh;
      fanState.OneWireGPIO = newOneWireGPIO;
      Serial.println(" ** Fan CONFIG : CHANGED");
      return StateUpdateResult::CHANGED;
    }
    Serial.println(" ** Fan CONFIG : UNCHANGED");
    return StateUpdateResult::UNCHANGED;
  }

  static void haRead(FanState& settings, JsonObject& root) {
    root["state"] = settings.fanStatus ? ON_STATE : OFF_STATE;
    String s = root["state"];
    Serial.println(" ** Fan haRead [" + s + "]");
  }

  static StateUpdateResult haUpdate(JsonObject& root, FanState& fanState) {
    String state = root["state"];
    String s = root["state"];
    Serial.println(" ** Fan haUpdate [" + s + "]");
    // parse new led state 
    boolean newState = false;
    if (state.equals(ON_STATE)) {
      newState = true;
    } else if (!state.equals(OFF_STATE)) {
      return StateUpdateResult::ERROR;
    }
    // change the new state, if required
    if (fanState.fanStatus != newState) {
      fanState.fanStatus = newState;
      return StateUpdateResult::CHANGED;
    }
    return StateUpdateResult::UNCHANGED;
  }
};

class FanStateService : public StatefulService<FanState> {
 public:
  unsigned long lastpoll;
  float Tin;
  float Tout;
  OneWire oneWire1;
  DallasTemperature sensor_intake;
  unsigned int RPM;
  

  FanStateService(AsyncWebServer* server, FS* fs, SecurityManager* securityManager, AsyncMqttClient* mqttClient, FanMqttSettingsService* fanMQTTSettingsService);
  void begin();
  void save();
  void compute();
  void SetLastPoll(unsigned long l);
  int getTachGPIO();

 private:
  HttpEndpoint<FanState> _httpEndpoint;
  FSPersistence<FanState> _fsPersistence;

  FanMqttSettingsService* _fanMQTTSettingsService;
  AsyncMqttClient* _mqttClient;
  MqttPubSub<FanState> _mqttPubSub;

  void registerConfig();
  void onConfigUpdated();
};

#endif
