#include <Arduino.h>
#include <Wire.h>

bool valueError=false;

unsigned long last_check=0;


#include "wifi_functions.h"

bool debug=false; //print Serial information
bool mqtt=true;
bool eccalibrationoutput=false; //serial output for ec calibration
/* Write to file with:
sudo stty -F /dev/ttyUSB0 115200
cat /dev/ttyUSB0 | tee received.txt

falls nicht geht, vorher einmal kurz per screen verbinden
*/

bool valuesStabilized=false; //gets set true when values are stable (avaeraging arrays filled)



#include "helpfunctions.h"
#include "ADS1X15.h"



// ######## Temperature
#include "temperature.h"


// ######## Water Level
#include "waterlevel.h" 


// ######## EC
ADS1115 ADS(0x48);
bool adsenabled=true;
#include "ec.h"





// ######## Flow Rate
#include "flow.h"

// ######## Soilmoisture
//#include "soilmoisture.h"


#ifdef PIN_NEOPIXEL
#include <Adafruit_NeoPixel.h>
Adafruit_NeoPixel pixels(1, PIN_NEOPIXEL, NEO_GRB + NEO_KHZ800);
#endif



void setup() {
  pinMode(PIN_BUTTON,INPUT_PULLUP);
#ifdef PIN_LED
  pinMode(PIN_LED,OUTPUT);
  digitalWrite(PIN_LED,LOW);
#endif



#if defined(NEOPIXEL_POWER)
  // If this board has a power control pin, we must set it to output and high
  // in order to enable the NeoPixels. We put this in an #if defined so it can
  // be reused for other boards without compilation errors
  pinMode(NEOPIXEL_POWER, OUTPUT);
  digitalWrite(NEOPIXEL_POWER, HIGH);
#endif
#ifdef PIN_NEOPIXEL
  pixels.begin();
  pixels.setBrightness(100); // not so bright
  pixels.clear();

  //Flash colors for debug
  pixels.setPixelColor(0, pixels.Color(255, 0,0));
  pixels.show();
  delay(250);
  pixels.setPixelColor(0, pixels.Color(0, 255,0));
  pixels.show();
  delay(250);
  pixels.setPixelColor(0, pixels.Color(0, 0,255));
  pixels.show();
  delay(250);
  pixels.clear();
  pixels.show();
  
#endif


  Serial.begin(115200);
  if (mqtt) {
    WiFi.begin(ssid, pass);
    client.begin(mqtt_host, net);
    client.onMessage(messageReceived);
    connect();
  }



  Wire.begin(PIN_SDA,PIN_SCL);
  Serial.print("I2C Clock Speed=");
  Serial.println(Wire.getClock());

  i2cscan();

  Serial.println("Setup Waterlevel");
  waterlevel_setup();


  //init ADS1115
  if (!ADS.begin()) {
    Serial.println("Error:"); delay(2000); Serial.println("ADS1115 Init Error!");
    if (mqtt){
      publishInfo("error/general","ADS1115 Init Error");
    }
    adsenabled=false;
  }
  if (adsenabled){
    ADS.setGain(0);
  }


  Serial.println("Setup EC");
  ec_setup();


  Serial.println("Setup Temperature");
  temperature_setup();
  
  Serial.println("Setup Flow");
  flow_setup();

  /*
  Serial.println("Setup Soilmoisture");
  sm_setup();
  */

  Serial.println("Finished Setup");
  delay(200);

  //Test adc to ec function output
  if (eccalibrationoutput) {
    Serial.println();
    Serial.print("adc"); Serial.print(","); Serial.print("ec"); Serial.println();
    for (int i=728;i<14000;i+=100) {
      //float _ec=ec_getECfromADC(i);
      float _ec=ec_getECfromADC(i, ec_calibration_polynom, sizeof(ec_calibration_polynom), ec_calibration_linearize_below_adc, ec_calibration_linear_lowADC, ec_calibration_linear_lowEC);
      
      Serial.print(i); Serial.print(","); Serial.print(_ec); Serial.println();
    }
    Serial.println("Waiting 10 seconds because eccalibrationoutput is enabled");
    delay(10000);
  }
  


  //Serial.println("time,tempReservoir,ECadcCalib,ECadc,ECadcAdjusted,EC,EC25");
  //Serial.println("time,tempReservoir,ECadcCalib,ECadc,ECadcAdjusted");

}

void loop() {
  unsigned long loopmillis=millis();
  enableTiming=true; //reactivate

  
  ec_loop(loopmillis);
  
  temperature_loop(loopmillis);
  
  if (!ec_measurementRunning()){ //skip tof read when ec measurement running, because vlxx sensor reading takes quite long per cycle
     waterlevel_loop(loopmillis);
  }
  
  flow_loop(loopmillis);

  //sm_loop(loopmillis);
 

  static bool getReading=false;

  if (!eccalibrationoutput) { //Is in normal operation mode
    if (!digitalRead(PIN_BUTTON)) { //button pressed
      valueError=false;
      Serial.println("Reset ValueError flag by user");
#ifdef PIN_LED
      digitalWrite(PIN_LED,valueError); //set led before delay to blink if error persists
#endif
#ifdef PIN_NEOPIXEL
      pixels.setPixelColor(0, pixels.Color(255*valueError, 0,0));
      pixels.show();
#endif
      delay(100);
    }

    static bool last_valueError=true;
    if (!valuesStabilized) { //if values are not okay since boot
#ifdef PIN_LED
      digitalWrite(PIN_LED,(loopmillis/250)%2==0); //blink led
#endif
#ifdef PIN_NEOPIXEL
      pixels.setPixelColor(0, pixels.Color(((loopmillis/250)%2==0)*255,((loopmillis/250)%2==0)*255,0));
      pixels.show();
#endif
    }else{ //LED shows valueError flag status when values were okay once
      if (last_valueError!=valueError) { //update led if valueerror flag changed
        last_valueError=valueError;
#ifdef PIN_LED
        digitalWrite(PIN_LED,valueError);
#endif
#ifdef PIN_NEOPIXEL
      pixels.setPixelColor(0, pixels.Color((loopmillis/250)%2==0,(loopmillis/250)%2==0,0));
      pixels.show();
#endif
      }
    }
  }


  

  if (eccalibrationoutput && !digitalRead(PIN_BUTTON) && !getReading) { //Calibration UI . Button Pressed
    if (!isValueArrayOK(ec_calib_array,EC_CALIB_ARRAY_SIZE,EC_ADC_UNAVAILABLE)) { //Calibration Array filled?
      for (uint8_t blink=0;blink<5;blink++) { //Error Blink
#ifdef PIN_LED
        digitalWrite(PIN_LED,HIGH);
        delay(100);
        digitalWrite(PIN_LED,LOW);
        delay(100);
#endif
#ifdef PIN_NEOPIXEL
        pixels.setPixelColor(0, pixels.Color(0,0,255));
        pixels.show();
        delay(100);
        pixels.setPixelColor(0, pixels.Color(0,0,0));
        pixels.show();
        delay(100);
#endif
      }
    }else{ //Calibration Array is ok, initiate ec reading
      getReading=true;
      force_ec_measurement=true;
      ec_flag_measurement_available=false;
#ifdef PIN_LED
      digitalWrite(PIN_LED,HIGH);
#endif
#ifdef PIN_NEOPIXEL
        pixels.setPixelColor(0, pixels.Color(0,255,0));
        pixels.show();
#endif
    }
  }

  
  if (eccalibrationoutput && ec_flag_measurement_available && getReading) { //Calibration UI
    
    ec_flag_measurement_available=false;
    getReading=false;
#ifdef PIN_LED
    digitalWrite(PIN_LED,LOW);
#endif
#ifdef PIN_NEOPIXEL
        pixels.setPixelColor(0, pixels.Color(0,0,0));
        pixels.show();
#endif
    Serial.print(CLIENT_ID);  Serial.print(",");
    Serial.print(loopmillis); Serial.print(",");
    Serial.print(tempCmean_reservoir); Serial.print(",");
    Serial.print(ec_calib_adc); Serial.print(",");
    Serial.print(ec_adc); Serial.print(",");
    Serial.print(ec_adc_adjusted);
    Serial.println();
  }
  
  if (loopmillis>last_check+2000) {  //check values
  
    last_check=loopmillis;

    
    bool _noErrorsDuringLoop=true; 
    
    if (tempCmean_reservoir==DEVICE_DISCONNECTED_C || tempCmean_case==DEVICE_DISCONNECTED_C) {
      if (!valueError && valuesStabilized) { //error just appeared
        if (tempCmean_reservoir==DEVICE_DISCONNECTED_C) {
          Serial.println("valueError tempCmean_reservoir");
          if (mqtt){
            publishInfo("error/temperature","valueError tempCmean_reservoir");
          }
        }
        if (tempCmean_case==DEVICE_DISCONNECTED_C) {
          Serial.println("valueError tempCmean_case");
          if (mqtt){
            publishInfo("error/temperature","valueError tempCmean_case");
          }
        }
        
        
      }
      valueError=true;
      _noErrorsDuringLoop=false;
    }
    
    /*
    if (sm_mean1==SM_DISCONNECTED || sm_mean2==SM_DISCONNECTED) {
      valueError=true;
    }*/
    if (ec==EC_UNAVAILABLE){
      if (!valueError && valuesStabilized) { //error just appeared
        if (ec==EC_UNAVAILABLE){
          Serial.println("valueError ec");
          if (mqtt){
            publishInfo("error/ec","valueError ec");
          }
        }
      }
      valueError=true;
      _noErrorsDuringLoop=false;
    }
    


    
    if (distance_unsuccessful_count>20) {
      if (!valueError && valuesStabilized) { //error just appeared
        Serial.println("valueError distance");
        if (mqtt){ 
          publishInfo("error/waterlevel","valueError distance");
        }
      }
      valueError=true;
      _noErrorsDuringLoop=false;
    }
    

   

    if (_noErrorsDuringLoop && !valuesStabilized) {
      valuesStabilized=true; //gets only set to true once
      valueError=false; //clear error flag once after boot
      Serial.println("Values Stable, clear error flag");
    }

    if (debug) {
      Serial.println("_______________________");
      Serial.print(millis()/1000.0,2); Serial.println(":");

      Serial.print("temperature reservoir = ");
      Serial.print(tempCmean_reservoir);
      Serial.println();
      Serial.print("temperature case = ");
      Serial.print(tempCmean_case);
      Serial.println();



      /*
      Serial.print("sm_mean 1,2,3 = ");
      Serial.print(sm_mean1); Serial.print(",");
      Serial.print(sm_mean2); Serial.print(",");
      Serial.print(sm_mean3);
      Serial.println();
      */

      /*
      Serial.print("sm_mean 1,2,3 = ");
      Serial.print(getMean(sm_mean1array,SM_SIZE)); Serial.print(",");
      Serial.print(getMean(sm_mean2array,SM_SIZE)); Serial.print(",");
      Serial.print(getMean(sm_mean3array,SM_SIZE));
      Serial.println();
      Serial.print("sm_max 1,2,3 = ");
      Serial.print(getMax(sm_mean1array,SM_SIZE)); Serial.print(",");
      Serial.print(getMax(sm_mean2array,SM_SIZE)); Serial.print(",");
      Serial.print(getMax(sm_mean3array,SM_SIZE));
      Serial.println();
      Serial.print("sm_min 1,2,3 = ");
      Serial.print(getMin(sm_mean1array,SM_SIZE)); Serial.print(",");
      Serial.print(getMin(sm_mean2array,SM_SIZE)); Serial.print(",");
      Serial.print(getMin(sm_mean3array,SM_SIZE));
      Serial.println();
      //Serial.print(getMax(sm_mean3array,SM_SIZE)); Serial.println();
      */

      Serial.print("Flow = "); Serial.print(flow);
      Serial.println();

      Serial.print("EC ec_calib_adc,ec_adc,ec_adc_adjusted = ");
      Serial.print(ec_calib_adc); Serial.print(",");
      Serial.print(ec_adc); Serial.print(",");
      Serial.print(ec_adc_adjusted);
      Serial.println();
      Serial.print("EC ec,ec25 = ");
      Serial.print(ec); Serial.print(",");
      Serial.print(ec25); 
      Serial.println();



      Serial.print("distance,Waterlevel,Volume = ");
      Serial.print(distance); Serial.print(",");
      Serial.print(waterlevel); Serial.print(",");
      Serial.print(watervolume); Serial.println();

      Serial.println();    



    }


    if (mqtt && mqtt_loop(loopmillis)) {
      if (sendallnext_flag) {
        sendallnext_flag=false;
        enableTiming=false;
      }

      if (tempCmean_reservoir!=DEVICE_DISCONNECTED_C) {
        publishValueTimed("temperature/reservoir",tempCmean_reservoir,2,timing_temperature_reservoir,loopmillis); 
      }
      if (tempCmean_case!=DEVICE_DISCONNECTED_C) {
        publishValueTimed("case/temperature",tempCmean_case,2,timing_temperature_case,loopmillis); 
      }
      /*
      if (sm_mean1!=SM_DISCONNECTED) {
        publishValueTimed("soilmoisture/sm1",sm_mean1,3,timing_soilmoisture_sm1,loopmillis); 
      }
      if (sm_mean2!=SM_DISCONNECTED) {
        publishValueTimed("soilmoisture/sm2",sm_mean2,3,timing_soilmoisture_sm2,loopmillis); 
      }
      if (sm_mean3!=SM_DISCONNECTED) {
        publishValueTimed("soilmoisture/sm3",sm_mean3,3,timing_soilmoisture_sm3,loopmillis); 
      }
      */
      

      
      static float last_flow=0;
      if (valuesStabilized){
        if (flow==0.0 && last_flow!=flow) {
          publishValueTimedOverride("flow",flow,2,timing_flow,loopmillis);  //publish without waiting if flow is 0
        }else{
          publishValueTimed("flow",flow,2,timing_flow,loopmillis); 
        }
        last_flow=flow;
      }

      
      
      if (waterlevel!=WATERLEVEL_UNAVAILABLE) {
        bool _published=publishValueTimed("waterlevel/height",waterlevel,2,timing_waterlevel,loopmillis); 
        if (_published) { //use height for timing. send calculated volume with it
          publishValue("waterlevel/volume",watervolume,2);
        }
      }
      


      if (ec_flag_measurement_available){
        ec_flag_measurement_available=false;

        if (ec_calib_adc!=0) {
          publishValue("ec/eccalibadc",ec_calib_adc,0); 
        }

        //Probe A
        if (ec_adc!=0) {
          publishValue("ec/adc",ec_adc,0); 
        }
        
        if (ec_adc_adjusted!=0) {
          publishValue("ec/adcadjusted",ec_adc_adjusted,0); 
        }
        if (ec!=EC_UNAVAILABLE){
          publishValue("ec/ec",ec,0); 
          publishValue("ec/sc",ec25,0); 
        }


      }

      /*
      if (ec_adc!=0) {
        publishValueTimed("ec/adc",ec_adc,0,timing_ec_adc,loopmillis); 
      }
      if (ec_calib_adc!=0) {
        publishValueTimed("ec/eccalibadc",ec_calib_adc,0,timing_ec_calibadc,loopmillis); 
      }
      if (ec_adc_adjusted!=0) {
        publishValueTimed("ec/adcadjusted",ec_adc_adjusted,0,timing_ec_adcadjusted,loopmillis); 
      }
      if (ec!=EC_UNAVAILABLE){
        publishValueTimed("ec/ec",ec,0,timing_ec_ec,loopmillis); 
        publishValueTimed("ec/sc",ec25,0,timing_ec_sc,loopmillis); 
      }*/
    }
    
  }
  
}