#include <WiFi.h>
#include <AsyncTCP.h>
#include <ESPAsyncWebServer.h>
#include <Preferences.h>
#include "esp_camera.h"
#include "esp_timer.h"
#include "img_converters.h"
#include "Arduino.h"
#include "index_html.h"
#include "esp32-hal-ledc.h"
#include "soc/soc.h"           
#include "soc/rtc_cntl_reg.h"  
#include "driver/rtc_io.h"

#include <FS.h>
#include "SD_MMC.h"            // SD Card ESP32


#define FILE_PHOTO "/photo.jpg"
// Pin definition for CAMERA_MODEL_AI_THINKER
#define PWDN_GPIO_NUM     32
#define RESET_GPIO_NUM    -1
#define XCLK_GPIO_NUM      0
#define SIOD_GPIO_NUM     26
#define SIOC_GPIO_NUM     27

#define Y9_GPIO_NUM       35
#define Y8_GPIO_NUM       34
#define Y7_GPIO_NUM       39
#define Y6_GPIO_NUM       36
#define Y5_GPIO_NUM       21
#define Y4_GPIO_NUM       19
#define Y3_GPIO_NUM       18
#define Y2_GPIO_NUM        5
#define VSYNC_GPIO_NUM    25
#define HREF_GPIO_NUM     23
#define PCLK_GPIO_NUM     22

#define uS_TO_S_FACTOR 1000000ULL  
#define TIME_TO_SLEEP  20  

#define LED_LEDC_CHANNEL 2

const char* ssid = "HMS_Vanguard_officer_cantine";
const char* password = "44313265";

const char* PARAM_INPUT_1 = "name";
const char* PARAM_INPUT_2 = "value";

const int serverPort = 80;

String rpServer = "192.168.1.100";
String serverPath = "/upload.php"; 

WiFiClient client;

camera_config_t config;
Preferences camSettings;

bool checkMenus[15] = {false, true, true, true, false, true, false, true, true, true, false, false, true, false, false};
int sliderValue[9] = {10, 0, 0, 0, 0, 30, 0, 0, 300};
int dropMenuValue[5] = {0, 0, 0, 13, 0};

bool takeNewPhoto = false;

// Create AsyncWebServer object on port 80
AsyncWebServer server(80);

void setup(){
  // Serial port for debugging purposes
  Serial.begin(115200);

  Serial.print("Initializing the camera module...");
  initCamera();
  Serial.println("Ok!");

  // Connect to Wi-Fi
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.println("Connecting to WiFi..");
  }

  // Print ESP Local IP Address
  Serial.println(WiFi.localIP());

  // Turn-off the 'brownout detector'
  WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0);

  // Route for root / web page
  server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send_P(200, "text/html", index_html, updateScreen);
  });

  server.on("/capture", HTTP_GET, [](AsyncWebServerRequest * request) {
    takeNewPhoto = true;
  });

  server.on("/save", HTTP_GET, [](AsyncWebServerRequest * request) {
    saveSettings();
  });

  server.on("/saved-photo", HTTP_GET, [](AsyncWebServerRequest * request) {
    request->send(SD_MMC, FILE_PHOTO, "image/jpg", false);  
  });

  server.on("/updateSlider", HTTP_GET, [] (AsyncWebServerRequest *request) {
    String messageName;
    String messageNameOri;
    String messageData;
    
    if (request->hasParam(PARAM_INPUT_1) && request->hasParam(PARAM_INPUT_2)) {
      messageNameOri = request->getParam(PARAM_INPUT_1)->value();
      messageName = messageNameOri.substring(7);
      messageData = request->getParam(PARAM_INPUT_2)->value();
      sliderValue[messageName.toInt()] = messageData.toInt();
      writeCameraSettings(messageNameOri);
    }
    else {
      messageName = "Sender unknown";
      messageData = "No message sent";
    }
    Serial.println("Slider " + messageName);
    Serial.println(messageData);
    request->send(200, "text/plain", "OK");
  });

  server.on("/updateCheckBox", HTTP_GET, [] (AsyncWebServerRequest *request) {
    String messageName;
    String messageNameOri;
    String messageData;
   
   if (request->hasParam(PARAM_INPUT_1) && request->hasParam(PARAM_INPUT_2)) {
      messageNameOri = request->getParam(PARAM_INPUT_1)->value();
      messageName = messageNameOri.substring(10);
      messageData = request->getParam(PARAM_INPUT_2)->value();
      checkMenus[messageName.toInt()] = messageData.toInt();
      writeCameraSettings(messageNameOri);
    }
    else {
      messageName = "Sender unknown";
      messageData = "No message sent";
    }
    Serial.println("Check " + messageName);
    Serial.println(messageData);
    request->send(200, "text/plain", "OK");
  });

  server.on("/updateDropMenu", HTTP_GET, [] (AsyncWebServerRequest *request) {
    String messageName;
    String messageNameOri;
    String messageData;
   
   if (request->hasParam(PARAM_INPUT_1) && request->hasParam(PARAM_INPUT_2)) {
      messageNameOri = request->getParam(PARAM_INPUT_1)->value();
      messageName = messageNameOri.substring(9);
      messageData = request->getParam(PARAM_INPUT_2)->value();
      dropMenuValue[messageName.toInt()] = messageData.toInt();
      writeCameraSettings(messageNameOri);
    }
    else {
      messageName = "Sender unknown";
      messageData = "No message sent";
    }
    Serial.println("Drop " + messageName);
    Serial.println(messageData);
    request->send(200, "text/plain", "OK");
  });
  
  // Start server
  server.begin();

 
  if(checkMenus[0]){
    controlLED(checkMenus[14], sliderValue[7]);
    delay(200);
    capturePhoto();
    controlLED(0, sliderValue[7]);
    deviceSleep(TIME_TO_SLEEP);
  }
}
  
void loop() {
  //controlLED(true, 200);
  if (takeNewPhoto){
    controlLED(checkMenus[14], sliderValue[7]);
    delay(200);
    capturePhoto();
    controlLED(0, sliderValue[7]);
    takeNewPhoto = false;
  }
}

void deviceSleep(uint64_t sleepTime){
  esp_sleep_enable_timer_wakeup(sleepTime *60* uS_TO_S_FACTOR);
  Serial.println("Setup ESP32 to sleep for every " + String(sleepTime) + " Minutes");
  
   delay(2000);
   Serial.println("Going to sleep now");
   delay(2000);
   esp_deep_sleep_start();
   Serial.println("This will never be printed");
}

String updateScreen(const String& var){
  //Serial.println(var);
   if(var == "CHECKPLACEHOLDER_1"){
    String boxes1 = drawCheckBoxes1();
    return boxes1;
  }
  else if(var == "CHECKPLACEHOLDER_2"){
    String boxes2 = drawCheckBoxes2();
    return boxes2;
  }
   else if(var == "CHECKPLACEHOLDER_3"){
    String boxes3 = drawCheckBoxes3();
    return boxes3;
  }
  else if(var == "CHECKPLACEHOLDER_4"){
    String boxes4 = drawCheckBoxes4();
    return boxes4;
  }
  else if (var == "QUALITYSLIDERVALUE"){
    return String(sliderValue[0]);
  }
  else if (var == "BRIGTNESSSLIDERVALUE"){
    return String(sliderValue[1]);
  }
  else if (var == "CONTRASTSLIDERVALUE"){
    return String(sliderValue[2]);
  }
  else if (var == "SATURATIONSLIDERVALUE"){
    return String(sliderValue[3]);
  }
  else if (var == "AESLIDERVALUE"){
    return String(sliderValue[4]);
  }
  else if (var == "AGCSLIDERVALUE"){
    return String(sliderValue[5]);
  }
  else if (var == "GAINSLIDERVALUE"){
    return String(sliderValue[6]);
  }
  else if (var == "LIGHTSLIDERVALUE"){
    return String(sliderValue[7]);
  }
  else if (var == "AECSLIDERVALUE"){
    return String(sliderValue[8]);
  }
  else if (var == "EFFECTMENUVALUE"){
    String option1 = dropMenuEffectState();
    
    return option1;
  }
  else if (var == "WHITEMENUVALUE"){
    String option2 = dropMenuWhiteState();
    
    return option2;
  }
  else if (var == "QUALITYMENUVALUE"){
    String option3 = dropMenuQualityState();
    
    return option3;
  }
  else if (var == "METERMENUVALUE"){
    String option4 = dropMenuMeterState();
    
    return option4;
  }
  return String();
}

String drawCheckBoxes1(){
  String boxes = "";
   //1 - white balance 
  boxes += "<p><div><label for=\"1\">AWB</label> <label class=\"switch\"><input type=\"checkbox\" onchange=\"updateCheckBox(this)\" id=\"checkMenu_1\" " + checkState(1) + "><span class=\"slider\"></span></label></div></p>";
    //2 - white balance gain 
  boxes += "<p><div><label for=\"2\">AWB Gain</label> <label class=\"switch\"><input type=\"checkbox\" onchange=\"updateCheckBox(this)\" id=\"checkMenu_2\" " + checkState(2) + "><span class=\"slider\"></span></label></div></p>";
  
  return boxes; 
}

String drawCheckBoxes2(){
  String boxes = "";
   //3 - exposure control
  boxes += "<p><div><label for=\"3\">AEC SENSOR</label> <label class=\"switch\"><input type=\"checkbox\" onchange=\"updateCheckBox(this)\" id=\"checkMenu_3\" " + checkState(3) + "><span class=\"slider\"></span></label></div></p>";
    //4 - aec2
  boxes += "<p><div><label for=\"4\">AEC DSP</label> <label class=\"switch\"><input type=\"checkbox\" onchange=\"updateCheckBox(this)\" id=\"checkMenu_4\" " + checkState(4) + "><span class=\"slider\"></span></label></div></p>";
    
  return boxes;
}

String drawCheckBoxes3(){
  String boxes = "";
  //5 - gain control
  boxes = "<p><div><label for=\"5\">AGC</label> <label class=\"switch\"><input type=\"checkbox\" onchange=\"updateCheckBox(this)\" id=\"checkMenu_5\" " + checkState(5) + "><span class=\"slider\"></span></label></div></p>";
    
  return boxes;
}

String drawCheckBoxes4(){
  String boxes = "";
  //6 - bpc
  boxes += "<p><div><label for=\"6\">BPC</label> <label class=\"switch\"><input type=\"checkbox\" onchange=\"updateCheckBox(this)\" id=\"checkMenu_6\" " + checkState(6) + "><span class=\"slider\"></span></label></div></p>";
    //7 - wpc
  boxes += "<p><div><label for=\"7\">WPC</label> <label class=\"switch\"><input type=\"checkbox\" onchange=\"updateCheckBox(this)\" id=\"checkMenu_7\" " + checkState(7) + "><span class=\"slider\"></span></label></div></p>";
    //8 - raw gma
  boxes += "<p><div><label for=\"8\">Raw GMA</label> <label class=\"switch\"><input type=\"checkbox\" onchange=\"updateCheckBox(this)\" id=\"checkMenu_8\" " +checkState(8) + "><span class=\"slider\"></span></label></div></p>";
    //9 - lens correction
  boxes += "<p><div><label for=\"9\">Lens correction</label> <label class=\"switch\"><input type=\"checkbox\" onchange=\"updateCheckBox(this)\" id=\"checkMenu_9\" " + checkState(9) + "><span class=\"slider\"></span></label></div></p>";
    //10 - horizontal mirror
  boxes += "<p><div><label for=\"10\">H-Mirror</label> <label class=\"switch\"><input type=\"checkbox\" onchange=\"updateCheckBox(this)\" id=\"checkMenu_10\" " + checkState(10) + "><span class=\"slider\"></span></label></div></p>";
    //11 - vertical flip
  boxes += "<p><div><label for=\"11\">V-Flip</label> <label class=\"switch\"><input type=\"checkbox\" onchange=\"updateCheckBox(this)\" id=\"checkMenu_11\" " + checkState(11) + "><span class=\"slider\"></span></label></div></p>";
    //12 - dcw
  boxes += "<p><div><label for=\"12\">DCW</label> <label class=\"switch\"><input type=\"checkbox\" onchange=\"updateCheckBox(this)\" id=\"checkMenu_12\" " + checkState(12) + "><span class=\"slider\"></span></label></div></p>";
    //13 - colorbar
  boxes += "<p><div><label for=\"13\">Colour Bar</label> <label class=\"switch\"><input type=\"checkbox\" onchange=\"updateCheckBox(this)\" id=\"checkMenu_13\" " + checkState(13) + "><span class=\"slider\"></span></label></div></p>";
   // 0-sleep
  boxes += "<p><div><label for=\"0\">Sleep cycle</label> <label class=\"switch\"><input type=\"checkbox\" onchange=\"updateCheckBox(this)\" id=\"checkMenu_0\" " + checkState(0) + "><span class=\"slider\"></span></label></div></p>"; 
    //14 - led
  boxes += "<p><div><label for=\"14\">LED</label> <label class=\"switch\"><input type=\"checkbox\" onchange=\"updateCheckBox(this)\" id=\"checkMenu_14\" " + checkState(14) + "><span class=\"slider\"></span></label></div></p>";
  
  return boxes;
}

  String dropMenuEffectState(){
    String option = "";
    switch (dropMenuValue[1]) {
      case 0:
        option = "<option value=\"0\" selected hidden>No effect</option>";
      break;
      case 1:
        option = "<option value=\"1\" selected hidden>Negative</option>";
      break;
      case 2:
        option = "<option value=\"2\" selected hidden>Grayscale</option>";
      break;
      case 3:
        option = "<option value=\"3\" selected hidden>Red tint</option>";
      break;
      case 4:
        option = "<option value=\"4\" selected hidden>Green tint</option>";
      break;
      case 5:
        option = "<option value=\"4\" selected hidden>Blue tint</option>";
      break;
      case 6:
        option = "<option value=\"4\" selected hidden>Sepia</option>";
      break;
    }
    return option;
  }

  String dropMenuWhiteState(){
    String option = "";
    switch (dropMenuValue[2]) {
      case 0:
        option = "<option value=\"0\" selected hidden>Auto</option>";
      break;
      case 1:
        option = "<option value=\"1\" selected hidden>Sunny</option>";
      break;
      case 2:
        option = "<option value=\"2\" selected hidden>Cloudy</option>";
      break;
      case 3:
        option = "<option value=\"3\" selected hidden>Office</option>";
      break;
      case 4:
        option = "<option value=\"4\" selected hidden>Home</option>";
      break;      
    }
    return option;
  }

  String dropMenuQualityState(){
    String option = "";
    switch (dropMenuValue[3]) {
      case 0:
        option = "<option value=\"0\" selected hidden>Gasmeter</option>";
      break;
      case 1:
        option = "<option value=\"1\" selected hidden>Watermeter</option>";
      break;
      case 2:
        option = "<option value=\"2\" selected hidden>Electricitymeter</option>";
      break;
    }
    return option;
  }

  String dropMenuMeterState(){
    String option = "";
    switch (dropMenuValue[4]) {
      case 5:
        option = "<option value=\"5\" selected hidden>QVGA (320 x 240)</option>";
      break;
      case 6:
        option = "<option value=\"6\" selected hidden>CIF (352 x 288)</option>";
      break;
      case 8:
        option = "<option value=\"8\" selected hidden>VGA (640 x 480)</option>";
      break;
      case 9:
        option = "<option value=\"9\" selected hidden>SVGA (800 x 600)</option>";
      break;
      case 10:
        option = "<option value=\"10\" selected hidden>XGA (1024 x 768)</option>";
      break;      
      case 12:
        option = "<option value=\"12\" selected hidden>SXGA (1280 x 1024)</option>";
      break;   
      case 13:
        option = "<option value=\"13\" selected hidden>UXGA (1600 x 1200)</option>";
      break;
    }
    return option;
  }

String checkState(int number){
  if(checkMenus[number]){
    return "checked";
  }
  else {
    return "";
  }
}

void controlLED(bool shine, int level){
  if(shine){
    ledcWrite(LED_LEDC_CHANNEL, level);
    Serial.println(level);
  }else{
    ledcWrite(LED_LEDC_CHANNEL, 0);
  }

}

void writeCameraSettings(String data){
  int state;
    sensor_t * s = esp_camera_sensor_get();
  if(data.equals("dropMenu_3")){  
    s->pixformat == PIXFORMAT_JPEG ;
    s->set_framesize(s, (framesize_t)dropMenuValue[3]);
    state = dropMenuValue[3];
  }else if(data.equals("slider_0")){ 
    s->set_quality(s, sliderValue[0]);
    state = sliderValue[0];
  } 
  else if(data.equals("slider_1")){
    s->set_brightness(s,  sliderValue[1]);     // -2 to 2
    state = sliderValue[1];
  } 
  else if(data.equals("slider_2")){
    s->set_contrast(s,  sliderValue[2]);       // -2 to 2
    state = sliderValue[2];
  }  
  else if(data.equals("slider_3")){ 
    s->set_saturation(s,  sliderValue[3]);     // -2 to 2
    state = sliderValue[3];
  }  
  else if(data.equals("dropMenu_1")){ 
    s->set_special_effect(s, dropMenuValue[1]); // 0 to 6 (0 - No Effect, 1 - Negative, 2 - Grayscale, 3 - Red Tint, 4 - Green Tint, 5 - Blue Tint, 6 - Sepia)
    state = dropMenuValue[1];
  } 
  else if(data.equals("checkMenu_1")){ 
    s->set_whitebal(s, checkMenus[1]);       // 0 = disable , 1 = enable
    state = checkMenus[1];
  } 
  else if(data.equals("checkMenu_2")){
    s->set_awb_gain(s, checkMenus[2]);       // 0 = disable , 1 = enable
    state = checkMenus[2];
  }  
  else if(data.equals("dropMenu_2")){   
    s->set_wb_mode(s, dropMenuValue[2]);        // 0 to 4 - if awb_gain enabled (0 - Auto, 1 - Sunny, 2 - Cloudy, 3 - Office, 4 - Home)
    state = dropMenuValue[2];
  }  
  else if(data.equals("checkMenu_3")){  
    s->set_exposure_ctrl(s, checkMenus[3]);  // 0 = disable , 1 = enable
    state = checkMenus[3];
  }  
  else if(data.equals("checkMenu_4")){    
    s->set_aec2(s, checkMenus[4]);           // 0 = disable , 1 = enable
    state = checkMenus[4];
  }  
  else if(data.equals("slider_4")){   
    s->set_ae_level(s,  sliderValue[4]);       // -2 to 2
    state = sliderValue[4];
  }  
  else if(data.equals("slider_8")){   
    s->set_aec_value(s, sliderValue[8]);    // 0 to 1200
    state = sliderValue[8];
  }  
  else if(data.equals("checkMenu_5")){ 
    s->set_gain_ctrl(s, checkMenus[5]);      // 0 = disable , 1 = enable
    state = checkMenus[5];
  }  
  else if(data.equals("slider_5")){
    s->set_agc_gain(s,  sliderValue[5]);       // 0 to 30
    state = sliderValue[5];
  }  
  else if(data.equals("slider_6")){ 
    s->set_gainceiling(s, (gainceiling_t)sliderValue[6]);  // 0 to 6
    state = sliderValue[6];
  }  
  else if(data.equals("checkMenu_6")){  
    s->set_bpc(s, checkMenus[6]);            // 0 = disable , 1 = enable
    state = checkMenus[6];
  } 
  else if(data.equals("checkMenu_7")){ 
    s->set_wpc(s, checkMenus[7]);            // 0 = disable , 1 = enable
    state = checkMenus[7];
  } 
  else if(data.equals("checkMenu_8")){  
    s->set_raw_gma(s, checkMenus[8]);        // 0 = disable , 1 = enable
    state = checkMenus[8];
  } 
  else if(data.equals("checkMenu_9")){
    s->set_lenc(s, checkMenus[9]);           // 0 = disable , 1 = enable
    state = checkMenus[9];
  } 
  else if(data.equals("checkMenu_10")){
    s->set_hmirror(s, checkMenus[10]);        // 0 = disable , 1 = enable
    state = checkMenus[10];
  } 
  else if(data.equals("checkMenu_11")){
    s->set_vflip(s, checkMenus[11]);          // 0 = disable , 1 = enable
    state = checkMenus[11];
  } 
  else if(data.equals("checkMenu_12")){
    s->set_dcw(s, checkMenus[12]);            // 0 = disable , 1 = enable
    state = checkMenus[12];
  } 
  else if(data.equals("checkMenu_13")){
    s->set_colorbar(s, checkMenus[13]);       // 0 = disable , 1 = enable
    state = checkMenus[13];
  } 
  else{
    state = 0;
  } 
  Serial.println(data +" "+state);
}

void initCamera(void){
  config.ledc_channel = LEDC_CHANNEL_0;
  config.ledc_timer = LEDC_TIMER_0;
  config.pin_d0 = Y2_GPIO_NUM;
  config.pin_d1 = Y3_GPIO_NUM;
  config.pin_d2 = Y4_GPIO_NUM;
  config.pin_d3 = Y5_GPIO_NUM;
  config.pin_d4 = Y6_GPIO_NUM;
  config.pin_d5 = Y7_GPIO_NUM;
  config.pin_d6 = Y8_GPIO_NUM;
  config.pin_d7 = Y9_GPIO_NUM;
  config.pin_xclk = XCLK_GPIO_NUM;
  config.pin_pclk = PCLK_GPIO_NUM;
  config.pin_vsync = VSYNC_GPIO_NUM;
  config.pin_href = HREF_GPIO_NUM;
  config.pin_sscb_sda = SIOD_GPIO_NUM;
  config.pin_sscb_scl = SIOC_GPIO_NUM;
  config.pin_pwdn = PWDN_GPIO_NUM;
  config.pin_reset = RESET_GPIO_NUM;
  config.xclk_freq_hz = 10000000;//20000000;
  config.pixel_format = PIXFORMAT_JPEG;
  config.frame_size = (framesize_t)dropMenuValue[3];
  config.jpeg_quality = sliderValue[0];
  config.grab_mode = CAMERA_GRAB_LATEST;//CAMERA_GRAB_WHEN_EMPTY;
  config.fb_count = 2; 

  ///////////////////////////////////////
  ledcSetup(LED_LEDC_CHANNEL, 5000, 8);
  ledcAttachPin(4,LED_LEDC_CHANNEL);
  //////////////////////////////////////
  if(!SD_MMC.begin("/sdcard", true)){
    Serial.println("SD Card Mount Failed");
    deviceSleep(1);
  }
  
  uint8_t cardType = SD_MMC.cardType();
  if(cardType == CARD_NONE){
    Serial.println("No SD Card attached");
    return;
  }

  if(psramFound()){
    config.fb_location = CAMERA_FB_IN_PSRAM;
    config.fb_count = 2;
    Serial.println("PSRAM");
  } 
  else {
    // Limit the frame size when PSRAM is not available
    config.fb_count = 1;
    config.fb_location = CAMERA_FB_IN_DRAM;
    Serial.println("DRAM");
  }

  esp_err_t err = esp_camera_init(&config);
  if (err != ESP_OK) {
    Serial.printf("Camera init failed with error 0x%x", err);
    delay(1000);
    deviceSleep(1);
  }

  loadSettings();

  sensor_t * s = esp_camera_sensor_get();
    s->pixformat == PIXFORMAT_JPEG; 
    s->set_framesize(s, (framesize_t)dropMenuValue[3]);
    s->set_quality(s, sliderValue[0]);
    s->set_brightness(s,  sliderValue[1]);     // -2 to 2 
    s->set_contrast(s,  sliderValue[2]);       // -2 to 2
    s->set_saturation(s,  sliderValue[3]);     // -2 to 2
    s->set_special_effect(s, dropMenuValue[1]); // 0 to 6 (0 - No Effect, 1 - Negative, 2 - Grayscale, 3 - Red Tint, 4 - Green Tint, 5 - Blue Tint, 6 - Sepia)
    s->set_whitebal(s, checkMenus[1]);       // 0 = disable , 1 = enable    
    s->set_awb_gain(s, checkMenus[2]);       // 0 = disable , 1 = enable 
    s->set_wb_mode(s, dropMenuValue[2]);        // 0 to 4 - if awb_gain enabled (0 - Auto, 1 - Sunny, 2 - Cloudy, 3 - Office, 4 - Home)  
    s->set_exposure_ctrl(s, checkMenus[3]);  // 0 = disable , 1 = enable     
    s->set_aec2(s, checkMenus[4]);           // 0 = disable , 1 = enable  
    s->set_ae_level(s,  sliderValue[4]);       // -2 to 2 
    s->set_aec_value(s, sliderValue[8]);    // 0 to 1200
    s->set_gain_ctrl(s, checkMenus[5]);      // 0 = disable , 1 = enable 
    s->set_agc_gain(s,  sliderValue[5]);       // 0 to 30
    s->set_gainceiling(s, (gainceiling_t)sliderValue[6]);  // 0 to 6  
    s->set_bpc(s, checkMenus[6]);            // 0 = disable , 1 = enable 
    s->set_wpc(s, checkMenus[7]);            // 0 = disable , 1 = enable  
    s->set_raw_gma(s, checkMenus[8]);        // 0 = disable , 1 = enable 
    s->set_lenc(s, checkMenus[9]);           // 0 = disable , 1 = enable 
    s->set_hmirror(s, checkMenus[10]);        // 0 = disable , 1 = enable
    s->set_vflip(s, checkMenus[11]);          // 0 = disable , 1 = enable 
    s->set_dcw(s, checkMenus[12]);            // 0 = disable , 1 = enable
    s->set_colorbar(s, checkMenus[13]);  

  delay(6000);//apparently needed for camera to get used to surroundings  
}

bool checkPhoto( fs::FS &fs ) {
  File f_pic = fs.open(FILE_PHOTO );
  unsigned int pic_sz = f_pic.size();
  return ( pic_sz > 100 );
}
void capturePhoto(void) {
  camera_fb_t * fb = NULL; 
  bool ok = 0; 

    // Take a photo with the camera
  Serial.println("Taking a photo...");
  fb = esp_camera_fb_get();
  if (!fb) {
    Serial.println("Camera capture failed");
    deviceSleep(1);
  }
  Serial.println("Photo taken");

  sendImage(fb);

  fs::FS &fs = SD_MMC; 
  File file = fs.open(FILE_PHOTO, FILE_WRITE);
  if(!file){
    Serial.println("Failed to open file in writing mode");
  } 
  else {
    file.write(fb->buf, fb->len); // payload , payload length
    Serial.printf("Saved file to path: %s\n", FILE_PHOTO);
  }  
  file.close();
  esp_camera_fb_return(fb);
}
String determineDevice(){
  String device ="";
  switch (dropMenuValue[4]) {
    case 0:
      device = "gas";
    break;
    case 1:
      device = "water";
    break;
    case 2:
      device = "electricity";
    break;
  }
  return device;
}

String sendImage(camera_fb_t * fb){
  String getAll;
  String getBody;
  String device;
   Serial.println("Connecting to server: " + rpServer);

   device = determineDevice();

  if (client.connect(rpServer.c_str(), serverPort)) {
    Serial.println("Connection successful!");    
    String head = "--DataImage\r\nContent-Disposition: form-data; name=\"imageFile\"; filename=\""+ device +".jpg\"\r\nContent-Type: image/jpeg\r\n\r\n";
    String tail = "\r\n--DataImage--\r\n";

    uint32_t imageLen = fb->len;
    uint32_t extraLen = head.length() + tail.length();
    uint32_t totalLen = imageLen + extraLen;
  
    client.println("POST " + serverPath + " HTTP/1.1");
    client.println("Host: " + rpServer);
    client.println("Content-Length: " + String(totalLen));
    client.println("Content-Type: multipart/form-data; boundary=DataImage");
    client.println();
    client.print(head);
  
    uint8_t *fbBuf = fb->buf;
    size_t fbLen = fb->len;
    for (size_t n=0; n<fbLen; n=n+1024) {
      if (n+1024 < fbLen) {
        client.write(fbBuf, 1024);
        fbBuf += 1024;
      }
      else if (fbLen%1024>0) {
        size_t remainder = fbLen%1024;
        client.write(fbBuf, remainder);
      }
    }   
    client.print(tail);
       
    int timoutTimer = 10000;
    long startTimer = millis();
    boolean state = false;
    
    while ((startTimer + timoutTimer) > millis()) {
      Serial.print(".");
      delay(100);      
      while (client.available()) {
        char c = client.read();
        if (c == '\n') {
          if (getAll.length()==0) { state=true; }
          getAll = "";
        }
        else if (c != '\r') { getAll += String(c); }
        if (state==true) { getBody += String(c); }
        startTimer = millis();
      }
      if (getBody.length()>0) { break; }
    }
    Serial.println();
    client.stop();
    Serial.println(getBody);
  }
  else {
    getBody = "Connection to " + rpServer +  " failed.";
    Serial.println(getBody);
  }
  return getBody;
}

void saveSettings(void){
  camSettings.begin("settings", false);
  Serial.println("Saving permanent values");

  camSettings.putInt("frameSz",dropMenuValue[3]);
  camSettings.putInt("quality",sliderValue[0]);
  camSettings.putInt("brightness",sliderValue[1]);
  camSettings.putInt("contrast",sliderValue[2]);
  camSettings.putInt("saturation",sliderValue[3]);
  camSettings.putInt("device",sliderValue[4]);
  camSettings.putInt("sEffect",dropMenuValue[1]);
  camSettings.putBool("whitebal",checkMenus[1]);
  camSettings.putBool("awbGain",checkMenus[2]);
  camSettings.putInt("wbMode",dropMenuValue[2]);
  camSettings.putBool("exposure",checkMenus[3]);
  camSettings.putBool("aec2",checkMenus[4]);
  camSettings.putInt("ae",sliderValue[4]);
  camSettings.putInt("aec",sliderValue[8]);
  camSettings.putBool("gain",checkMenus[5]);
  camSettings.putInt("agcGain",sliderValue[5]);
  camSettings.putInt("ceiling",sliderValue[6]);
  camSettings.putBool("bpc",checkMenus[6]);
  camSettings.putBool("wpc",checkMenus[7]);
  camSettings.putBool("rawGma",checkMenus[8]);
  camSettings.putBool("lenc",checkMenus[9]);
  camSettings.putBool("hMirror",checkMenus[10]);
  camSettings.putBool("vFlip",checkMenus[11]);
  camSettings.putBool("dcw",checkMenus[12]);
  camSettings.putBool("colorbar",checkMenus[13]);
  camSettings.putBool("flash",checkMenus[14]);
  camSettings.putInt("intensity",sliderValue[7]);
  camSettings.putBool("sleep",checkMenus[0]);

  camSettings.end();
  Serial.println("Saved");
}
void loadSettings(void){
  camSettings.begin("settings", false);
  Serial.println("Loading permanent values");

  dropMenuValue[3] = camSettings.getInt("frameSz", 13);
  sliderValue[0] = camSettings.getInt("quality", 10);
  sliderValue[1] = camSettings.getInt("brightness", 2);
  sliderValue[2] = camSettings.getInt("contrast", 0);
  sliderValue[3] = camSettings.getInt("saturation", -2);
  sliderValue[4] = camSettings.getInt("device", 0);
  dropMenuValue[1] = camSettings.getInt("sEffect", 2);
  checkMenus[1] = camSettings.getBool("whitebal", 1);
  checkMenus[2] = camSettings.getBool("awbGain", 1);
  dropMenuValue[2] = camSettings.getInt("wbMode", 1);
  checkMenus[3] = camSettings.getBool("exposure",1);
  checkMenus[4] = camSettings.getBool("aec2", 0);
  sliderValue[4] = camSettings.getInt("ae",1);
  sliderValue[8] = camSettings.getInt("aec", 300);
  checkMenus[5] = camSettings.getBool("gain", 1);
  sliderValue[5] = camSettings.getInt("agcGain", 30);
  sliderValue[6] = camSettings.getInt("ceiling", 6);
  checkMenus[6] = camSettings.getBool("bpc", 1);
  checkMenus[7] = camSettings.getBool("wpc", 1);
  checkMenus[8] = camSettings.getBool("rawGma", 1);
  checkMenus[9] = camSettings.getBool("lenc", 0);
  checkMenus[10] = camSettings.getBool("hMirror", 0);
  checkMenus[11] = camSettings.getBool("vFlip", 0);
  checkMenus[12] = camSettings.getBool("dcw", 1);
  checkMenus[13] = camSettings.getBool("colorbar", 0);
  checkMenus[14] = camSettings.getBool("flash", 1);
  sliderValue[7] = camSettings.getInt("intensity", 250);
  checkMenus[0] = camSettings.getBool("sleep", 0);

  camSettings.end();
  Serial.println("Loaded");
}