//Built-in
#include <Arduino.h>
#include <SPI.h>
#include <Ethernet.h>

// Additoinal Libraries
#include <ArduinoJson.h>
#include <MQTT.h>
#include <LiquidCrystal_I2C.h>
#include <LiquidMenu.h> // LiquidMenu_config.h needs to be modified to use I2C.
#include <MD_REncoder.h>
#include <Adafruit_MAX31865.h>

// My Includes
#include "config.h"
#include <Button.h>
#include <SlowPWM.h>

// Pin definitions
#define encoderCLK 2
#define encoderDT 3
#define encoderBTN 4
#define kettlePWM 5

// Global variables.
byte KettleDuty = 0;
bool KettleOn = false;

// User I/O objects.
Button Enter;
slowPWM boilPWM;
MD_REncoder rotary = MD_REncoder(encoderDT, encoderCLK);
LiquidCrystal_I2C lcd(0x27,20,4);

EthernetClient net;
MQTTClient mqtt_client;

unsigned long lastRun = 0;

// Return a character array to represent the
// On/Off state of the kettle.
char* KettleState() {
  if (KettleOn) {
    return (char*)"On";
  } else {
    return (char*)"Off";
  }
}

// Interrupt function to run when encoder is turned.
//
// Increases/decreases the kettle output to a max
// of 100% and minimum of 0%.
void doEncoder()
{
  uint8_t result = rotary.read();
  uint8_t inc;

  if (result) {
    uint8_t speed = rotary.speed();
    speed >= 10 ? inc = 5 : inc = 1;
  }

  if (result == DIR_CW && KettleDuty < 100) {
    KettleDuty = (KettleDuty / inc) * inc + inc;
  } else if (result == DIR_CCW && KettleDuty > 0) {
    KettleDuty = (KettleDuty / inc) * inc - inc;
  }

}

// LCD menu setup.
LiquidLine KettleState_line(0, 0, "Boil Kettle      ", KettleState);
LiquidLine kettle_power_line(0, 1, "Kettle Power %   ", KettleDuty);
LiquidScreen home_screen(KettleState_line, kettle_power_line);
LiquidMenu menu(lcd);

void setup() {

  unsigned long lastRun = millis() - UpdateInterval;
  Serial.begin(9600);
  rotary.begin();
  Ethernet.begin(mac, ip);
  Serial.println("Setting up...");

  attachInterrupt(digitalPinToInterrupt(encoderCLK), doEncoder, CHANGE);
  attachInterrupt(digitalPinToInterrupt(encoderDT), doEncoder, CHANGE);

  pinMode(encoderCLK, INPUT_PULLUP);
  pinMode(encoderDT, INPUT_PULLUP);
  Enter.begin(encoderBTN);
  boilPWM.begin(kettlePWM, PeriodPWM);

  // if you get a connection, report back via serial:
  if (Ethernet.linkStatus() == LinkON) {
    SetupMQTT(MQTT_BROKER);
  } else {
    // if you didn't get a connection to the server:
    Serial.println("connection failed");
  }

  lcd.init();
  lcd.backlight();

  menu.init();
  menu.add_screen(home_screen);
  menu.update();

};

void UpdateBoilKettle(){
  static byte last_KettleDuty = 0;

  if (Enter.pressed()) {
    KettleOn = !KettleOn;
    menu.update();
  }

  if (last_KettleDuty != KettleDuty) {
    last_KettleDuty = KettleDuty;
    menu.update();
  }

  if (KettleOn) {
    boilPWM.compute(KettleDuty);
  } else {
    boilPWM.compute(0);
  }
}

void loop() {
  UpdateBoilKettle();

  unsigned long elapsedTime = (millis() - lastRun);

  if (Ethernet.linkStatus() == LinkON && elapsedTime >= UpdateInterval) {
    mqtt_client.loop();
    //if (!mqtt_client.connected()) ConnectMQTT();

    SendSensorData();
    lastRun = millis();
  }

}
