#ifndef DEVICE_H
#define DEVICE_H
#include <ArduinoJson.h>
#include <PubSubClient.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#include <ESP8266WiFi.h>

#include <Tools.h>
#include <secrets.h>

const char version[] = "0.0.1";

#define ACTION_TPC "action/state"
#define MODE_SET "mode/set"
#define MODE_STATE "mode/state"
#define SWING_STATE "swing/state"
#define SWING_SET "swing/set"
#define TEMP_SET "temp/set"
#define TEMP_STATE "temp/state"
#define TEMP_CURRENT "temp/current"
#define TEMP_HI_SET "temp_hi/set"
#define TEMP_HI_STATE "temp_hi/state"
#define TEMP_LO_SET "temp_lo/set"
#define TEMP_LO_STATE "temp_lo/state"

#define ACTION_OFF "off"
#define ACTION_HEATING "heating"
#define ACTION_COOLING "cooling"
#define ACTION_IDLE "idle"

#ifndef DEVICE_NAME
#define DEVICE_NAME "MQTT Thermostat"
#endif

#ifndef DEVICE_MDL
#define DEVICE_MDL "Thermostat"
#endif

#ifndef DEVICE_MF
#define DEVICE_MF ""
#endif

#ifndef ROOT
#define ROOT "thermostat/"
#endif

#ifndef CONFIG_ROOT
#define CONFIG_ROOT "homeassistant/climate/"
#endif

#ifndef MSG_RETAIN
#define MSG_RETAIN true
#endif

#define TOPIC_LIMIT 4

//~ #define OFF 0
//~ #define COOL 1
//~ #define HEAT 2
//~ #define AUTO 3

#define TEMP 0
#define TEMP_HI 1
#define TEMP_LO 2

uint8_t CHILLER_SP = 70;
uint8_t FERMA_SP = 70;
uint8_t FERMB_SP = 70;

//~ typedef void (*ptr)(uint8_t*, uint8_t*);
//typedef ptr (*pm)();

struct CommandTopic {
  //~ ptr CmdFunc;
  byte* Setting;
  String CmdTopic;
};

struct Mode {
  String CmdTopic;
  String StateTopic;
  String Setting;
};

struct SetPoint {
  String CmdTopic;
  String StateTopic;
  int Setting;
};

struct ControlDevice {
  String name;       // "Glycol Chiller"
  String topic_root; // "brewhouse/"
  String current_temp_topic;
  byte temp_sensor[8];
  String action_topic;
  String action_state;
  String swing_topic;
  String swing_state;
  byte heat_pin;
  byte cool_pin;
  Mode mode;
  SetPoint setpoints[3];
  bool dual;
};

float UpdateTemperature(DallasTemperature *sensors, DeviceAddress glycol_tank) {
  // method 2 - faster
  float tempC = sensors->getTempC(glycol_tank);
  if(tempC == DEVICE_DISCONNECTED_C)
  {
    Serial.println("Error: Could not read temperature data");
    return 0;
  }

  return DallasTemperature::toFahrenheit(tempC);

}

bool DeviceLoop(ControlDevice &device, DallasTemperature *sensors) {
  Serial.println(device.name);

  float dsTemp = UpdateTemperature(sensors, device.temp_sensor);
  String initial_action = device.action_state;

  if (device.mode.Setting == "auto") {
    int setHi = device.setpoints[TEMP_HI].Setting;
    int setLo = device.setpoints[TEMP_LO].Setting;

    if ((setLo <= dsTemp) && (dsTemp <= setHi)) { // Within Temp Range
      device.action_state = ACTION_IDLE;
      digitalWrite(device.cool_pin, LOW);
      digitalWrite(device.heat_pin, LOW);
    } else if (dsTemp < setLo) { // Needs Heat
      device.action_state = ACTION_HEATING;
      digitalWrite(device.cool_pin, LOW);
      digitalWrite(device.heat_pin, HIGH);
    } else if (dsTemp > setHi) { // Needs Cooling
      device.action_state = ACTION_COOLING;
      digitalWrite(device.cool_pin, HIGH);
      digitalWrite(device.heat_pin, LOW);
    }
  }

  int setTemp = device.setpoints[TEMP].Setting;

  if (device.mode.Setting == "cool") {


    if (dsTemp > setTemp) { // Needs Cooling
      device.action_state = ACTION_COOLING;
      digitalWrite(device.cool_pin, HIGH);
      digitalWrite(device.heat_pin, LOW);
    } else {
      device.action_state = ACTION_IDLE;
      digitalWrite(device.cool_pin, LOW);
      digitalWrite(device.heat_pin, LOW);
    }
  }

  if (device.mode.Setting == "heat") {
    if (dsTemp < setTemp) { // Needs Cooling
      device.action_state = ACTION_HEATING;
      digitalWrite(device.cool_pin, LOW);
      digitalWrite(device.heat_pin, HIGH);
    } else {
      device.action_state = ACTION_IDLE;
      digitalWrite(device.cool_pin, LOW);
      digitalWrite(device.heat_pin, LOW);
    }
  }

  if (device.mode.Setting == "off") {
    device.action_state = ACTION_IDLE;
  }

  if (initial_action != device.action_state) {
    return true;
  }
  return false;
}
#endif
