#include <ArduinoJson.h>
#include <LiquidCrystal_I2C.h>
#include <PubSubClient.h>
#include <MqttWildcard.h>
#include <PubSubClientTools.h>
#include <BearSSLHelpers.h>
#include <CertStoreBearSSL.h>
#include <ESP8266WiFi.h>
#include <ESP8266WiFiAP.h>
#include <ESP8266WiFiGeneric.h>
#include <ESP8266WiFiMulti.h>
#include <ESP8266WiFiScan.h>
#include <ESP8266WiFiSTA.h>
#include <ESP8266WiFiType.h>
#include <WiFiClient.h>
#include <WiFiClientSecure.h>
#include <WiFiClientSecureAxTLS.h>
#include <WiFiClientSecureBearSSL.h>
#include <WiFiServer.h>
#include <WiFiServerSecureAxTLS.h>
#include <WiFiServerSecureBearSSL.h>
#include <WiFiUdp.h>
#include "private.h"
#include <FlowMeter.h>
#include <Wire.h>
#include <Adafruit_MCP23017.h>
#include <Thread.h>
#include <ThreadController.h>
#include <AceButton.h>
Adafruit_MCP23017 mcp;
long tslr = 0;
WiFiClient espClient;
boolean allowbuttonint=true;
AceButton gpio0(0);
PubSubClient client("siwatsystem.com", 1883, espClient);
PubSubClientTools mqtt(client);
ThreadController threadControl = ThreadController();
Thread mqttupdater = Thread();
Thread datacollector = Thread();
Thread lcdmanager = Thread();
boolean online = true;
boolean sensorstate[12];
boolean lastsensorstate[12];
boolean firstrun = true;
FlowMeter sensor1 = FlowMeter(12);
FlowMeter sensor2 = FlowMeter(13);
boolean devcon=false;
int devconmenu=0;
volatile boolean awakenByInterrupt = false;
LiquidCrystal_I2C lcd(0x3F, 16, 2);
int menu;
void handleGPIO0(AceButton*, uint8_t, uint8_t);
void writelcd(String line1, String line2){
  if(!devcon){
  lcd.clear();
  lcd.print(line1);
  lcd.setCursor(0,1);
  lcd.print(line2);
  Serial.print("Writing to LCD: "+line1+"    "+line2+"\n");
  }
}
void updatelcd()
{
  if(!devcon)writelcd("1: "+String((int)sensor1.getCurrentFlowrate())+"L/h "+String(sensor1.getTotalVolume())+"L","2:"+String((int)sensor2.getCurrentFlowrate())+"L/h "+String(sensor2.getTotalVolume())+"L");
}
void ICACHE_RAM_ATTR read1() {
  sensor1.count();
}
void ICACHE_RAM_ATTR read2() {
  sensor2.count();
}
void collectdata() {
  sensor1.tick(1000);
  sensor2.tick(1000);
}
void updatemqtt() {
  mqtt.publish("/waterishos/node" + nodename + "/flowrate1", String(sensor1.getCurrentFlowrate()));
  mqtt.publish("/waterishos/node" + nodename + "/volume1", String(sensor1.getTotalVolume()));
  mqtt.publish("/waterishos/node" + nodename + "/flowrate2", String(sensor2.getCurrentFlowrate()));
  mqtt.publish("/waterishos/node" + nodename + "/volume2", String(sensor2.getTotalVolume()));
}
// Copyright Siwat INC(tm) 2019
// Created by Siwat Sirichai
// ESPCommander EXTENDED v1.2
// For Legal Distribution of this code, the comment must not be removed!
void espcommander(String topic, String rawcommand)  {
    StaticJsonDocument<200> doc;
    DeserializationError error = deserializeJson(doc, rawcommand);
    if (error) {
      Serial.print(F("deserializeJson() failed: "));
      Serial.println(error.c_str());
    return;
    }
    const char* command = doc["command"];
    const char* value = doc["value"];
    }
//END OF ESPCOMMANDER CODE
void setup() {
  Serial.begin(115200);
  lcd.begin();
  writelcd(" Siwat INC (tm) ","  Waterish OS");
  delay(1000);
  WiFi.mode(WIFI_STA);
  WiFi.disconnect(true);
  esp_wifi_sta_wpa2_ent_set_identity((uint8_t *)eap_usernam, strlen(eap_username));
  esp_wifi_sta_wpa2_ent_set_username((uint8_t *)eap_username, strlen(eap_username));
  esp_wifi_sta_wpa2_ent_set_password((uint8_t *)eap_password, strlen(eap_password));
  esp_wpa2_config_t config = WPA2_CONFIG_INIT_DEFAULT();
  esp_wifi_sta_wpa2_ent_enable(&config);
  WiFi.begin(ssid);
  int connectionattempt = 0;
  while (WiFi.status() != WL_CONNECTED && online)
  {
    //wait for it ... (Wait for Wifi Connection)
    writelcd("WiFi Connecting","   Attempt "+String(connectionattempt));
    connectionattempt++;
    delay(500);
    if (connectionattempt >= 60) {
      writelcd(" Cannot Connect"," Going Offline!");
      online = false;
    }
  }
  String wifiname(ssid);
  if(online)writelcd(" WiFi Connected",wifiname);
  delay(3000);
  writelcd("Boot Sequence P3"," Loading Kernel");
  pinMode(14, INPUT_PULLUP);
  pinMode(12, INPUT);
  pinMode(13, INPUT);
  pinMode(0, INPUT_PULLUP);
  gpio0.setEventHandler(handleGPIO0);
  attachInterrupt(digitalPinToInterrupt(12), read1, RISING);
  attachInterrupt(digitalPinToInterrupt(13), read2, RISING);
  delay(1000);
  writelcd("Boot Sequence P3","Waking Processor");
  delay(1000);
  writelcd("   SETTINGS.H","co-processor:OFF");
  delay(3000);
  mcp.begin();
  for (int i = 0; i <= 15; i++)
  {
    mcp.pinMode(i, INPUT);
    mcp.pullUp(i, LOW);
    mcp.setupInterruptPin(i, RISING);
  }
  delay(1000);
  writelcd("Boot Sequence P3","    Success!");
  delay(2000);
  writelcd("Waterish OS a3.9","Reading  Sensors");
  delay(1000);
  writelcd(" Telemetry Node","siwatsystem.com");
  if (client.connect("waterishos",telemetryuser,telemetrykey)) {
    writelcd(" Telemetry Node","Connected");
    mqtt.subscribe("/waterishos/node" + nodename + "command", espcommander);
    delay(1000);
  } else {
    writelcd(" Telemetry Node"," Failed Offline");
    online=false;
    delay(3000);
  }
  datacollector.onRun(collectdata);
  datacollector.setInterval(1000);
  if(online)mqttupdater.onRun(updatemqtt);
  if(online)mqttupdater.setInterval(1000);
  lcdmanager.onRun(updatelcd);
  lcdmanager.setInterval(250);
  threadControl.add(&datacollector);
  threadControl.add(&lcdmanager);
  if(online)threadControl.add(&mqttupdater);
  if(online)updatemqtt();
  
}

void loop() {
  if(online)client.loop();
  if(!devcon)threadControl.run();
  if(allowbuttonint)gpio0.check();
}
void drawcon(){
  if(devconmenu==1){
    writelcd("connectionchck.c","Check Connection");
  }
}
void handleGPIO0(AceButton* , uint8_t eventType,
    uint8_t ) {
  switch (eventType) {
    case AceButton::kEventPressed:
      if(devcon=false){
        allowbuttonint=false;
      devcon=true; //no going back, Delete Waterish OS and Install Siwat INC(R) DEVCON OS
      writelcd("  SIWAT INC(R)  ","DEVCON OS V3.2.4");
      delay(1000);
      writelcd("DEVCON  TOOLKITS","3 Extension Load");
      delay(1000);
      writelcd("Initia Extension","connectionchck.c");
      delay(2000);
      writelcd("Initia Extension","hall_pid_tuner.c")
      delay(2000);
      writelcd("Initia Extension","credit_display.c");
      delay(2000);
      writelcd("DEVCON  TOOLKITS","set WTOS at 0x3D");
      delay(1000);
      allowbuttonint=true
      devconmenu=1;
      drawcon();
      break;
      }
      
    case AceButton::kEventReleased:
      //debounce
      break;
  }
}
