//*******************************************************
//INCLUDES
//*******************************************************
#include <ESP8266WiFi.h>          //https://github.com/esp8266/Arduino
#include <DNSServer.h>
#include <ESP8266WebServer.h>
#include <WiFiManager.h>         //https://github.com/tzapu/WiFiManager
#include <Esp.h>                 //https://github.com/esp8266/Arduino
#include <stdint.h>
#include <ArduinoJson.h>         //https://github.com/bblanchon/ArduinoJson
#include <PubSubClient.h>        //https://github.com/knolleary/pubsubclient

extern "C" {
#include "user_interface.h"
ADC_MODE(ADC_VCC);
}

//*******************************************************
//MQTT SETUP
//*******************************************************
const char* mqtt_server = "192.168.0.128";
WiFiClient espClient;
PubSubClient client(espClient);
long lastMsg = 0;
char msg[50];
int value = 0;

struct configuration{
    int qos = 1;
    uint32_t id = ESP.getChipId();
    String prefix = "MAT-EM-";
    char* intopic = "set/topic";
    char* outtopic = "esp/topic";
    char* lwttopic = "dead/topic";  
    char* mysqlprimar = "spotreba";
    char* mysqlsecundar = "teplota";  
    char* mysqlconfig = "konfigurace";
    char* valnameprimar = "kwh";  
    char* valnamesecundar = "temp"; 
    
};

typedef struct configuration Config;
Config pconfig;

///*******************************************************
//WIFI SETUP
//********************************************************
WiFiManager wifiManager;

//*******************************************************
//GLOBAL VARIABLES
//*******************************************************
int connecting = 0;
int pulse = 250;
int count = 0;
int send_data = 0;
byte buf[2];

//*******************************************************
//DEFINES
//*******************************************************
//#define SERIAL_DEBUG
#define COUNT_ADDR 66
#define STATUS_ADDR 67
#define OPTO_PIN 13
#define LED_PIN 14
#define AND_PIN 16

//*******************************************************
//FUNCTIONS DECLARATION
//*******************************************************
extern void pulsecounter();
extern void pulsedetected();
extern void connect_wifi();
extern void reconnect();
extern void callback(char* topic, byte* payload, unsigned int length); 

//*******************************************************
//START OF THE PROGRAM
//*******************************************************
void setup() {
WiFi.forceSleepBegin();  
yield();

pinMode(OPTO_PIN, INPUT);
pinMode(AND_PIN, OUTPUT);
pinMode(LED_PIN, OUTPUT);
digitalWrite(LED_PIN, HIGH);
digitalWrite(AND_PIN, LOW);

#ifdef SERIAL_DEBUG
    Serial.begin(115200);
    delay(10);
    Serial.println();
    Serial.println();
    Serial.println("I am alive!");
#endif 
}

void loop() {
  system_rtc_mem_read(STATUS_ADDR,buf,2);
  Serial.print("STATUS: ");
  Serial.println(buf[0]);
  Serial.println(buf[1]); 
  if((buf[0] == 0xF0) && (buf[1] == 0xAA)){   
    pulsecounter();  
  }else{
    connect_wifi();
    buf[0] = 0xF0;
    buf[1] = 0xAA;
    Serial.println("Buffer to write: ");
    Serial.println(buf[0]);
    Serial.println(buf[1]);
    system_rtc_mem_write(STATUS_ADDR, buf, 2);
    buf[0] = 0;
    buf[1] = (byte)pulse;
    system_rtc_mem_write(COUNT_ADDR, buf, 2);
    
  }
  if(send_data != 0){
      reconnect();
      send_data = 0;  
  }
  //WiFi.disconnect();        
  Serial.println("Going to sleep...");
  ESP.deepSleep(0);
  yield();
}

//*******************************************************
//END OF THE PROGRAM
//*******************************************************

//*******************************************************
//START OF FUNCTIONS
//*******************************************************
void pulsecounter(){
  digitalWrite(LED_PIN, LOW);  
  system_rtc_mem_read(COUNT_ADDR, buf, 2);
  buf[0] = buf[0] + 1;
  system_rtc_mem_write(COUNT_ADDR, buf, 1);
  Serial.print("Pulse count: ");
  Serial.println(buf[0]);
  if(buf[0] >= buf[1]){
    digitalWrite(LED_PIN, HIGH);    
    buf[0] = 0;
    system_rtc_mem_write(COUNT_ADDR, buf, 1);
    connect_wifi();
    reconnect();          
  }  
  digitalWrite(LED_PIN, HIGH);    
}

void pulsedetected(){ 
  if (digitalRead(OPTO_PIN) == 1){
    digitalWrite(LED_PIN, LOW);
    while(digitalRead(OPTO_PIN) == 1);
    digitalWrite(LED_PIN, HIGH);
    system_rtc_mem_read(COUNT_ADDR, buf, 2);
    buf[0] = buf[0] + 1;
    system_rtc_mem_write(COUNT_ADDR, buf, 1);
    Serial.print("Pulse count interrupt: ");
    Serial.println(buf[0]);
    if (buf[0] >= buf[1]){
      send_data = 1;
      buf[0] = 0;
      system_rtc_mem_write(COUNT_ADDR, buf, 1);
    }  
  }
}

void connect_wifi(){
    WiFi.forceSleepWake();
    delay(100);
    String wifiname = "CARD_READER: " + String(pconfig.id);
    char awifiname[wifiname.length()];
    wifiname.toCharArray(awifiname, wifiname.length());
    
    wifiManager.autoConnect(awifiname);                   
}

void reconnect() {
    int attempt = 0;
            
    client.setServer(mqtt_server, 1883);
    client.setCallback(callback);
   
    while (!client.connected()) {
    Serial.print("Attempting MQTT connection...");
    String mqttname = pconfig.prefix + String(pconfig.id);
    String mqttlwt = mqttname + ": died";
    char clientname[mqttname.length()];
    char clientlwt[mqttlwt.length()];
    mqttname.toCharArray(clientname, mqttname.length());
    if (client.connect(clientname, pconfig.lwttopic, pconfig.qos, 1, clientlwt )) {
      Serial.println("connected");
      int test = client.subscribe(pconfig.intopic);
      Serial.println(test);
            
      int napeti_bat = ESP.getVcc();
      char msgbuffer[256];
      StaticJsonBuffer<200> jsonBuffer;
      JsonObject& root = jsonBuffer.createObject();
      root["CHIP_ID"] = pconfig.id;
      root["mysql"] = pconfig.mysqlprimar;
      root["value"] = 1;
      root["value_name"] = pconfig.valnameprimar;
      root["napeti_bat"] = napeti_bat;
      root.printTo(msgbuffer, sizeof(msgbuffer));
      root.printTo(Serial);
    
    
    client.publish(pconfig.outtopic, msgbuffer);
    Serial.println("data sent");
    } else {
      Serial.print("failed, rc=");
      Serial.print(client.state());
      Serial.println(" try again in 2.5 seconds");
      // Wait 5 seconds before retrying
      attempt = attempt + 1;
      if (attempt == 5){
        break;
      }else{
        delay(2500);
      }
    }
  }
  for(int i = 0; i < 1000; i++){
    delay(10);
    client.loop();
    yield();    
  }  
}

void callback(char* topic, byte* payload, unsigned int length) {
  char msgbuffer[length];
  Serial.print("Message arrived [");
  Serial.print(topic);
  Serial.print("] ");
  for (int i = 0; i < length; i++) {
    Serial.print((char)payload[i]);
    msgbuffer[i] = (char)payload[i];
  }
  Serial.println();
  StaticJsonBuffer<200> jsonBuffer;
  JsonObject& root = jsonBuffer.parseObject(msgbuffer);
  if (!root.success()) {
    Serial.println("parseObject() failed");
    return;
  }
  if (root["CHIP_ID"] == pconfig.id){
    if (root["pulse"] != NULL){
      system_rtc_mem_read(COUNT_ADDR, buf, 2);
      buf[1] = root["pulse"];
      system_rtc_mem_write(COUNT_ADDR, buf, 2);
    }  
  }
}
//*******************************************************
//END OF FUNCTIONS
//*******************************************************















