/*
 * 
 * 
 * System Functions
 * 
 * 
 */

void wipeEEPROM() {
  Serial.println(":: wipe EEPROM ::");
  
  // wipeMsg is helper variable to know if the Serial.print Message was
  // already sent
  byte wipeMsg = 0;
  while(digitalRead(PinWIPE) == LOW ) {
    // only show the Serial message once
    if(wipeMsg == 0) {
      Serial.println("Please release PinWIPE to erase all data saved in EEPROM");
      Serial.println("LAST CHANCE TO KEEP THE DATA BY RESETTING NOW!!");
      
      display.clearDisplay();
      display.setCursor(0,0);
      display.println("!!!!!!!!!!!!!!!!!!!!!");
      display.println("");
      display.println("RELEASE PinWIPE");
      display.println("TO WIPE EEPROM");
      display.display();
      
      // increase i to show the serial message only once
      wipeMsg = 1;
    }
    delay(500);
  }
  
  // write a 0 to all 512 bytes of the EEPROM
  Serial.print("wiping EEPROM... ");
  display.println("Wiping EEPROM...");
  display.println("Will restart in 3s");
  display.display();
  for (int i = 0; i < 512; i++) { EEPROM.write(i, 0); }
  
  // commit everything to EEPROM and end here
  EEPROM.end();
  
  Serial.println("DONE");
  
  // set D4 PinWIPE internal LED to Output to give feedback WIPE
  // was done
  pinMode(PinWIPE, OUTPUT);
    
  Serial.println("!! Device will restart in 3 seconds !!");
  
  // let the internal led blink fast to signalize wipe is done
  for(byte i = 0; i <= 24 ; i++) {
    if(i % 2) {
      digitalWrite(PinWIPE, LOW);
    } else {
      digitalWrite(PinWIPE, HIGH);
    }
    delay(125);
  }
  ESP.restart();
}


bool loadEEPROM() {
  /*
   * EEPROM Save table
   * 
   * 0 WIFIssid
   * 32 WIFIpassword 
   * 96 WIFIip 
   * 112 WIFInetmask 
   * 128 WIFIgateway 
   * 144 WIFIdns 
   * 160 WIFIuseDHCP 
   * 
   * 161 configured 
   * 162 UseFan 
   * 163 UsePump 
   * 164 PumpOnTime 
   * 165 MoistureSensor_Type 
   * 166 SoilmoistureLow 
   * 167 NtpOffset 
   * 169 UseLEDrelais 
   * 
   * 170 GrowName 
   * 202 GrowStart 
   * 206 DaysVeg 
   * 207 DaysBloom 
   * 208 LighthoursVet 
   * 209 LighthoursBloom 
   * 210 SunriseHour 
   * 211 SunriseMinute 
   * 212 DayOfGrow 
   * 
   * -- afterwards added, need to sort --
   * 
   * 213 PinLEDPWM 
   * 214 TemperatureSensor_Type 
   * 215 UseFANrelais
   * 216 PinFANPWM
   * 217 SunFade
   * 218 SunFadeDuration
   * 219 MaintenanceDuration
   * 221 .. 
   * 
   */
   
  Serial.println(":: loading EEPROM ::");

  display.setCursor(0,36);
  display.fillRect(0,36,128,64-36, 0);
  display.println("loading EEPROM");
  display.display();
  // read var WIFIssid from address 0, 32 byte long  
  // read this first, because we decide on the ssid length (>0?) if
  // we run in unconfigured AP mode, nor not
  EEPROM.get(0, WIFIssid);
  
  // when length is > 0 then read furter EEPROM config data
  if(strlen(WIFIssid)) {
    /*
     * WIFI settings
     */

    // read var WIFIpassword from address 32, 64 byte long  
    EEPROM.get(32, WIFIpassword);
    // read var WIFIip from address 96, 16 byte long  
    EEPROM.get(96, WIFIip);
    // read var WIFInetmask from address 112, 16 byte long  
    EEPROM.get(112, WIFInetmask);
    // read var WIFIgateway from address 128, 16 byte long  
    EEPROM.get(128, WIFIgateway);
    // read var WIFIgateway from address 128, 16 byte long  
    EEPROM.get(144, WIFIdns);
    // read var WIFIuseDHCP from Address 160, 1 byte long
    EEPROM.get(160, WIFIuseDHCP);
    
    
    /*
     * System settings
     */

    // size is 1 byte
    EEPROM.get(161, configured);
    if(configured == true) {
      // size is 1 byte
      EEPROM.get(162, UseFan);
      // size is 1 byte
      EEPROM.get(163, UsePump);
      // size is 1 byte
      EEPROM.get(164, PumpOnTime);
      // size is 1 byte
      EEPROM.get(165, MoistureSensor_Type);
      // size is 1 byte
      EEPROM.get(166, SoilmoistureLow);
      // size is 2 byte
      EEPROM.get(167, NtpOffset);
      // size is 1 byte
      EEPROM.get(169, UseLEDrelais);
      // size is 1 byte
      EEPROM.get(214, TemperatureSensor_Type);
      // size is 1 byte
      EEPROM.get(215, UseFANrelais);
      // size is 2 byte
      EEPROM.get(219, MaintenanceDuration);
    }
    // TODO auth does not work atm 
    // EEPROM.get(160, WebUiUsername);
    // EEPROM.get(176, WebUiPassword); 
    
    /*
     * Grow settings
     */
    
    // size is 32 byte
    EEPROM.get(170, GrowName);
    if(strlen(GrowName) > 0) {
      // size is 4 byte
      EEPROM.get(202, GrowStart);
      // size is 1 byte
      EEPROM.get(206, DaysVeg);
      // size is 1 byte
      EEPROM.get(207, DaysBloom);
      // size is 1 byte
      EEPROM.get(208, LighthoursVeg);
      // size is 1 byte
      EEPROM.get(209, LighthoursBloom);
      // size is 1 byte
      EEPROM.get(210, SunriseHour);
      // size is 1 byte
      EEPROM.get(211, SunriseMinute);
      // size is 1 byte
      EEPROM.get(212, DayOfGrow);
      // size is 1 byte
      EEPROM.get(213, PinLEDPWM);
      // size is 1 byte
      EEPROM.get(216, PinFANPWM);
      EEPROM.get(217, SunFade);
      EEPROM.get(218, SunFadeDuration);
      
    }

    
  
    
    // print values to Serial output
    Serial.println("---- WiFi values ----");
    Serial.print("WIFIssid: ");
    Serial.println(WIFIssid);
    Serial.print("WIFIpassword: ");
    Serial.println(WIFIpassword);
    Serial.print("Use DHCP: ");
    Serial.println(WIFIuseDHCP);
    
    Serial.println("---- System values ----");
    Serial.print("configured: ");
    Serial.println(configured);
    Serial.print("UseFan: ");
    Serial.println(UseFan);
    Serial.print("UsePump: ");
    Serial.println(UsePump);
    Serial.print("PumpOnTime: ");
    Serial.println(PumpOnTime);
    Serial.print("MoistureSensor_Type: ");
    Serial.println(MoistureSensor_Type);
    Serial.print("TemperatureSensor_Type: ");
    Serial.println(TemperatureSensor_Type);
    Serial.print("SoilmoistureLow: ");
    Serial.println(SoilmoistureLow);
    Serial.print("NtpOffset: ");
    Serial.println(NtpOffset);
    Serial.print("UseLEDrelais: ");
    Serial.println(UseLEDrelais);
    Serial.print("UseFANrelais: ");
    Serial.println(UseFANrelais);
    Serial.print("MaintenanceDuration: ");
    Serial.println(MaintenanceDuration);

    Serial.println("---- Grow values ----");
    Serial.print("GrowName: ");
    Serial.println(GrowName);
    Serial.print("GrowStart: ");
    Serial.println(GrowStart);
    Serial.print("DaysVeg: ");
    Serial.println(DaysVeg);
    Serial.print("DaysBloom: ");
    Serial.println(DaysBloom);
    Serial.print("LighthoursVeg: ");
    Serial.println(LighthoursVeg);
    Serial.print("LighthoursBloom: ");
    Serial.println(LighthoursBloom);
    Serial.print("SunriseHour: ");
    Serial.println(SunriseHour);
    Serial.print("SunriseMinute: ");
    Serial.println(SunriseMinute);
    Serial.print("DayOfGrow: ");
    Serial.println(DayOfGrow);
    Serial.print("PinLEDPWM: ");
    Serial.println(PinLEDPWM);
    Serial.print("PinFANPWM: ");
    Serial.println(PinFANPWM);
    Serial.print("SunFade: ");
    Serial.println(SunFade);
    Serial.print("SunFadeDuration: ");
    Serial.println(SunFadeDuration);
    
    
  } else {
    Serial.println("EEPROM value WIFIssid is empty");
  }
  Serial.println(":: EEPROM loaded ::");
  
  display.setCursor(0,42);
  display.println("EEPROM loaded");
  display.display();
  
  return(strlen(WIFIssid));
}

void wifiConnect() {
    Serial.println(":: Connecting to WiFi ::");
    FirstRun = false;
    Serial.print("SSID: ");
    Serial.println(WIFIssid);
    
    display.fillRect(0,36,128,64-36, 0);
    display.setCursor(0,36);
    display.println("Connecting to WiFi");
    display.println(WIFIssid);
    display.display();

    // Start WiFi connection
    WiFi.begin(WIFIssid, WIFIpassword);
    if(WIFIuseDHCP == false) {
      WiFi.config(WIFIip, WIFIdns, WIFIgateway, WIFInetmask);
    }

    // wait until WiFi connection is established
    while (WiFi.status() != WL_CONNECTED) {
      delay(500);
      Serial.print(".");
    }
    Serial.println(" CONNECTED!");
    Serial.print("IP: ");
    Serial.println(WiFi.localIP());
    
    Serial.println(":: Getting time from NTP ::");
    display.fillRect(0,36,128,64-36, 0);
    display.setCursor(0,36);
    display.println("Getting NTP time");
    display.display();
    
    timeClient.begin();
    timeClient.setTimeOffset(NtpOffset * 60 * 60);
    timeClient.update();
    while ( ! timeClient.isTimeSet()) {
      timeClient.update();
      delay(500);
      Serial.print(".");
    }
    
    Serial.println(timeClient.getFormattedTime());
    Serial.println(timeClient.getEpochTime());
    display.println(timeClient.getFormattedTime());
    display.display();
    display.print("IP: ");
    display.print(WiFi.localIP());
    display.display();
}

void wifiAp() {
    Serial.println(":: Creating Accesspoint ::");
    
    display.fillRect(0,36,128,64-36, 0);
    display.setCursor(0,36);
    display.println("Creating AccessPoint");
    display.println(APssid);
    display.display();
    
    FirstRun = true;
    // configure WiFi Access Point
    WiFi.softAPConfig(WIFIip, WIFIgateway, WIFInetmask);
    // start Access Point
    // TODO make AP with password - does not work atm. idk why. 
    WiFi.softAP(APssid);
    Serial.print("SSID: ");
    Serial.println(APssid);
    Serial.print("CanGrow IP address: ");
    Serial.println(WiFi.softAPIP());
    
    display.print("IP: ");
    display.println(WiFi.softAPIP());
    display.display();
    // TODO does not work atm, idk why
    //Serial.println("The login credentials for the WebUI are 'cangrow' for username and password");
}

void setOutput(byte Output, byte OutputState) {
  /*
   * Pin assignments
   * 
   * 1 - LED
   * 2 - FAN
   * 3 - PUMP
   * 
   */
  bool UseRelais = true;
  byte OutputPin;
   
  switch(Output) {
    case 1:
      OutputPin = PinLED;
      if(UseLEDrelais == true) {
        UseRelais = true;
      } else {
        UseRelais = false;
      }
      break;
    case 2:
      OutputPin = PinFAN;
      if(UseFANrelais == true) {
        UseRelais = true;
      } else {
        UseRelais = false;
      }
      break;
    // PUMP Pin (D0) does not support PWM, so we do not need to care about
    case 3:
      OutputPin = PinPUMP;
      break;
  }
  
  //~ Serial.print("Output: ");
  //~ Serial.println(Output);
  //~ Serial.print("OutputPin: ");
  //~ Serial.println(OutputPin);
  //~ Serial.print("OutputState: ");
  //~ Serial.println(OutputState);
  //~ Serial.print("UseRelais: ");
  //~ Serial.println(UseRelais);
  
  if( (UseRelais == true) || (OutputPin == PinPUMP) ) {
    digitalWrite(OutputPin, OutputState);
  } else {
    analogWrite(OutputPin, OutputState);
  }
}


void controlLED() {
  byte lightHours;
  byte PinLEDPWM_tmp;
  unsigned int secondsSunrise = (SunriseHour * 60 * 60) + (SunriseMinute * 60);
  unsigned int secondsToday = (timeClient.getHours() * 60 * 60) + (timeClient.getMinutes() * 60) + timeClient.getSeconds();
  
  if(DayOfGrow > DaysVeg ) {
    lightHours = LighthoursBloom;
  } else {
    lightHours = LighthoursVeg;
  }
   
  // check if secondsToday is larger then secondsSunrise time AND if 
  // secondsToday is smaller then the sum of secondsSunrise + seconds of lightHours
  if( ((secondsToday >= secondsSunrise) && (secondsToday <= ( secondsSunrise + (lightHours * 60 * 60))) ) ){
    //Serial.println("light on time");
    
    // when SunFade is true, fade LED light. Otherwise just turn on or off
    if( (SunFade == true) && (UseLEDrelais == false) && (secondsSunrise + SunFadeDuration * 60 >= secondsToday) ) {
      // in the first n minutes of lighting (SunFadeDuration), we want 
      // to raise the light slowly to prevent stress from the plant
      // convert progress sunrise to PWM value
      PinLEDPWM_tmp = (SunFadeDuration * 60 - ((secondsSunrise + SunFadeDuration * 60) - secondsToday)) * PinLEDPWM / (SunFadeDuration * 60);
      setOutput(1, PinLEDPWM_tmp);
      //Serial.print("sunrise PWM; ");
      //Serial.println(PinLEDPWM_tmp);
      
    } else if( (SunFade == true) && (UseLEDrelais == false) && (secondsToday >= ((secondsSunrise + lightHours * 60 * 60) - SunFadeDuration * 60) ) ) {
      // calculate progress sunset to PWM value
      PinLEDPWM_tmp = (secondsSunrise + (lightHours * 60 * 60) - secondsToday) * PinLEDPWM / (SunFadeDuration * 60);
      setOutput(1, PinLEDPWM_tmp);
      //Serial.print("sunset PWM: ");
      //Serial.println(PinLEDPWM_tmp);
      
    } else {
      //Serial.println("just turn on the light");
      // no sunrise or sunset, just keep the LED turned on
      setOutput(1, PinLEDPWM);
    }
    
  } else {
    //Serial.println("good night time");
    // turn off
    setOutput(1, 0);
  }
}

void refreshSensors() {
  valSoilmoisture = getSoilmoisture(MoistureSensor_Type);
  valHumidity = getHumidity();
  valTemperature = getTemperature(TemperatureSensor_Type);
  valWaterlevel = getWaterlevel();
}

void displayScreens() {
  /*
   * which screen to display
   * interate through different screens
   * 
   */
  if(ScreenIterationPassed > DisplayScreenDuration){
    ScreenIterationPassed = 0;
    // helper variable, maybe i find a better way in future
    byte LastScreen = 2;
    // when the next screen gets displayed, clear display
    display.clearDisplay();
    display.display();
    // when ScreenToDisplay has reach last number of screens, reset to first (0)
    if(ScreenToDisplay >= LastScreen) {
      ScreenToDisplay = 0;
    } else {
      ScreenToDisplay++;
    }
  }
   
  display.setCursor(0,0);
  
  if(MaintenanceMode == true) {
    display.drawBitmap(0, 0, bmpCanGrow_Logo, 128, 32, WHITE);
    display.display();
    display.setCursor(0,32);
    display.println("Maintenance mode active");
    display.print("Time left: ");
    display.print(MaintenanceDuration - ((millis() - MaintenanceStarted) / 1000));
    display.println("s");
  } else {
  // in this switch case the single screens gets defined
    switch(ScreenToDisplay) {
      case 0:
        display.print("Humidity: ");
        display.print(valHumidity);
        display.println(" %");
        display.println("");
        display.print("Temperature: ");
        display.print(valTemperature);
        display.println(" C");
        display.println("");
        display.print("Moisture: ");
        display.print(valSoilmoisture);
        display.println(" %");
        display.println("");
        if(UsePump == true) {
          display.print("Pump Waterlvl: ");
          switch(valWaterlevel) {
            case 0:
              display.println("OK");
              break;
            case 1: 
              display.println("Warn");
              break;
            case 2:
              display.println("Crit");
              break;
          }
        }
        break;
        
      case 1:
        display.print("LED: ");
        display.print(PinLEDPWM * 100 / 255);
        display.println(" %");
        display.print("State: ");
        display.println(digitalRead(PinLED));
        display.println("");
        display.print("FAN: ");
        display.print(PinFANPWM * 100 / 255);
        display.println(" %");
        display.print("State: ");
        display.println(digitalRead(PinFAN));
        display.println("");
        display.print("Pump state: ");
        display.println(digitalRead(PinPUMP));
        break;
        
      case 2:
        // display Logo
        display.drawBitmap(0, 0, bmpCanGrow_Logo, 128, 32, WHITE);
        display.display();
        display.setCursor(0,32);
        display.println(GrowName);
        display.print("DoG: ");
        display.print(DayOfGrow);
        display.print(", ");
        display.println(timeClient.getFormattedTime());
        display.print("IP: ");
        display.println(WiFi.localIP());
        break;
    }
  }
  ScreenIterationPassed++;
  display.display();
  
}