/* This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details. */

#include <ESP8266WiFi.h>
#include <PubSubClient.h>
#include <bsec.h>
#include <Wire.h>
#include <Ticker.h>
#include <Adafruit_GFX.h>
#include <Adafruit_LEDBackpack.h>
#include <Adafruit_NeoPixel.h>

String matrixausgabe_text  = " "; // Ausgabetext als globale Variable

volatile int matrixausgabe_index = 0;// aktuelle Position in Matrix

IPAddress myOwnIP; // ownIP for mDNS 

//-------------- definition mqtt-object ueber WiFi
WiFiClient   espClient; 
PubSubClient mqttclient(espClient);

//--------- list of mqtt callback functions 
#define MAX_MQTT_SUB 10 // maximal 10 subscriptions erlaubt
typedef void (*mqtthandle) (byte*,unsigned int);
typedef struct {       // Typdeklaration Callback
  String topic;        // mqtt-topic
  mqtthandle fun;      // callback function 
}
subscribe_type; 
subscribe_type mqtt_sub[MAX_MQTT_SUB];
int mqtt_sub_count=0;

String MQTT_Rx_Payload = "" ;
//--------- mqtt callback function 
void mqttcallback(char* to, byte* pay, unsigned int len) {
  String topic   = String(to);
  String payload = String((char*)pay);
  MQTT_Rx_Payload=payload.substring(0,len);
  Serial.println("\ncallback topic:" + topic + ", payload:" + MQTT_Rx_Payload);
  for (int i=0;i<mqtt_sub_count;i++) { // durchsuche alle subscriptions, bis topic passt 
    if (topic==mqtt_sub[i].topic) 
      mqtt_sub[i].fun(pay,len);         // Aufruf der richtigen callback-Funktion
  }
}

//------------ reconnect mqtt-client
void mqttreconnect() { // Loop until we're reconnected 
  if (!mqttclient.connected()) { 
    while (!mqttclient.connected()) { 
      Serial.print("Attempting MQTT connection...");
      if (mqttclient.connect("MyConsumer" , "user", "passwort" )) {
        Serial.println("connected");
        for (int i=0;i<mqtt_sub_count;i++) { // subscribe topic
          mqttclient.subscribe(mqtt_sub[i].topic.c_str());
          Serial.println("\nsubscribe");
          Serial.print(mqtt_sub[i].topic);
        }
      } 
      else { 
        Serial.print("failed, rc=");
        Serial.print(mqttclient.state());
        Serial.println(" try again in 5 seconds");
        delay(5000);
      }
    }
  } 
  else { 
    mqttclient.loop(); 
  }
}

int MQTT = 0 ;
void mqtt_callback_topic_Octopus_Temperatur(byte* pay, unsigned int len){ // ---------- my callbackfunction mqtt
  String payload = String((char*)pay); // payload als String interpretieren
  MQTT_Rx_Payload=payload.substring(0,len);    // mit Länge von len Zeichen
  Serial.println("\n in callback payload:" + MQTT_Rx_Payload +"len:"+String(len));
  MQTT = MQTT_Rx_Payload.toFloat() ;
  Serial.print("Value:"+String(MQTT_Rx_Payload));
  Serial.println();
  if (( ( MQTT ) == ( 1000 ) ))
  {
  }
  else
  {
  }
}


int TEMP = 0 ;
/* 
 Bosch BSEC Lib, https://github.com/BoschSensortec/BSEC-Arduino-library
 The BSEC software is only available for download or use after accepting the software license agreement.
 By using this library, you have agreed to the terms of the license agreement: 
 https://ae-bst.resource.bosch.com/media/_tech/media/bsec/2017-07-17_ClickThrough_License_Terms_Environmentalib_SW_CLEAN.pdf */
Bsec iaqSensor;     // Create an object of the class Bsec 
Ticker Bsec_Ticker; // schedule cyclic update via Ticker 

// ------------------------   Helper functions Bosch Bsec - Lib 
void checkIaqSensorStatus(void)
{ 
  String output; 
  if (iaqSensor.status != BSEC_OK) {
    if (iaqSensor.status < BSEC_OK) {
      output = "BSEC error code : " + String(iaqSensor.status);
      for (;;) {
        Serial.println(output);
        delay(500);
      } // Halt in case of failure 
    } 
    else {
      output = "BSEC warning code : " + String(iaqSensor.status);
      Serial.println(output);
    }
  }

  if (iaqSensor.bme680Status != BME680_OK) {
    if (iaqSensor.bme680Status < BME680_OK) {
      output = "BME680 error code : " + String(iaqSensor.bme680Status);
      for (;;){
        Serial.println(output);
        delay(500);
      }  // Halt in case of failure 
    } 
    else {
      output = "BME680 warning code : " + String(iaqSensor.bme680Status);
      Serial.println(output);
    }
  }
}

// Housekeeping: scheduled update using ticker-lib
void iaqSensor_Housekeeping(){  // get new data 
  iaqSensor.run();
}

int HUM = 0 ;
int PRESS = 0 ;
// Adafruit Feather 7 Segment https://learn.adafruit.com/adafruit-7-segment-led-featherwings/overview
Adafruit_7segment matrix7Seg = Adafruit_7segment(); // 7Segment Feather
Adafruit_NeoPixel pixels = Adafruit_NeoPixel(2,13,NEO_GRBW + NEO_KHZ800);

void setup(){ // Einmalige Initialisierung
  Serial.begin(115200);
  //----------------------------------MQTT-Client 
  mqttclient.setServer("broker.mqttdashboard.com", 1883);
  mqttclient.setCallback(mqttcallback);

  Serial.println();
  mqttclient.setCallback(mqttcallback);

  //--------- prepare mqtt subscription 
  mqtt_sub_count++; // add new element 
  if (mqtt_sub_count < MAX_MQTT_SUB) { 
    mqtt_sub[mqtt_sub_count-1].topic = "Octopus/status";
    mqtt_sub[mqtt_sub_count-1].fun = mqtt_callback_topic_Octopus_Temperatur; //callback function
  } 
  else Serial.println(" err max. mqtt subscription");

  Wire.begin(); // ---- Initialisiere den I2C-Bus 

  iaqSensor.begin(BME680_I2C_ADDR_PRIMARY, Wire);
  String output = "\nBSEC library version " + String(iaqSensor.version.major) + "." + String(iaqSensor.version.minor) + "." + String(iaqSensor.version.major_bugfix) + "." + String(iaqSensor.version.minor_bugfix);
  Serial.println(output);
  checkIaqSensorStatus();

  bsec_virtual_sensor_t sensorList[10] = {
    BSEC_OUTPUT_RAW_TEMPERATURE,
    BSEC_OUTPUT_RAW_PRESSURE,
    BSEC_OUTPUT_RAW_HUMIDITY,
    BSEC_OUTPUT_RAW_GAS,
    BSEC_OUTPUT_IAQ,
    BSEC_OUTPUT_STATIC_IAQ,
    BSEC_OUTPUT_CO2_EQUIVALENT,
    BSEC_OUTPUT_BREATH_VOC_EQUIVALENT,
    BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_TEMPERATURE,
    BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_HUMIDITY,
  };

  iaqSensor.updateSubscription(sensorList, 10, BSEC_SAMPLE_RATE_LP);
  checkIaqSensorStatus();  
  iaqSensor_Housekeeping();
  Bsec_Ticker.attach_ms(3000, iaqSensor_Housekeeping);

  if (Wire.status() != I2C_OK) Serial.println("Something wrong with I2C");

  matrix7Seg.begin(0x70); // ---- Initialisiere 7Segment Matrix
  matrix7Seg.clear();     // 
  matrix7Seg.writeDisplay();

  pixels.begin();//-------------- Initialisierung Neopixel
  delay(1);
  pixels.show();
  pixels.setPixelColor(0,0,0,0,0); // alle aus
  pixels.setPixelColor(1,0,0,0,0);
  pixels.show();                 // und anzeigen

  //------------ WLAN initialisieren 
  WiFi.persistent(false);
  WiFi.mode(WIFI_STA);
  delay(100);
  Serial.print ("\nWLAN connect to:");
  Serial.print("WIFI");
  WiFi.begin("SSID-Wifi","Wifi-Password-XXX");
  while (WiFi.status() != WL_CONNECTED) { // Warte bis Verbindung steht 
    delay(500); 
    Serial.print(".");
  };
  Serial.println ("\nconnected, meine IP:"+ WiFi.localIP().toString());
  matrixausgabe_text = " Meine IP:" + WiFi.localIP().toString();
  myOwnIP = WiFi.localIP();
  matrixausgabe_index=0;


  mqttreconnect();
}

void loop() { // Kontinuierliche Wiederholung 
  mqttclient.loop();// MQTT-Client bedienen
  TEMP = iaqSensor.temperature ;
  HUM = iaqSensor.humidity ;
  PRESS = (iaqSensor.pressure/100.) ;
  if (( ( MQTT ) == ( 1000 ) ))
  {
    matrix7Seg.print(TEMP);
    matrix7Seg.writeDigitNum(0,1,true);
    matrix7Seg.writeDisplay();
  }
  else
  {
    if (( ( MQTT ) == ( 1001 ) ))
    {
      matrix7Seg.print(HUM);
      matrix7Seg.writeDigitNum(0,2,true);
      matrix7Seg.writeDisplay();
    }
    else
    {
      if (( ( MQTT ) == ( 1002 ) ))
      {
        matrix7Seg.print(PRESS);
        matrix7Seg.writeDigitNum(0,3,true);
        matrix7Seg.writeDisplay();
      }
    }
  }
  pixels.setPixelColor(0,0,30,0,0);
  pixels.show();    
  delay( 100 );
  pixels.setPixelColor(0,0,0,0,0);
  pixels.show();    
  delay( 500 );
}
