#include <Wire.h>
#include "MS5837.h"
#include "SPIFFS.h"
#include <FS.h>
#include <WiFi.h>
#include <AsyncTCP.h>
#include <ESPAsyncWebServer.h>

//NETWORK CREDENTIALS
//
const char* ssid = "Maaya's iPhone";
const char* password = "beepb0p!";
//
//const char* ssid = "EECS_Labs";
//const char* password = "";

//INITIALIZE SERVER

AsyncWebServer server(80);
  
//INITIALIZE DATA ARRAYS

const int bufferSize = 60;

String xArray[bufferSize];  //an array capable of holding 20 entries numbered 0 to 19
String yArray[bufferSize];  //an array capable of holding 20 entries numbered 0 to 19
String zArray[bufferSize];  //an array capable of holding 20 entries numbered 0 to 19
String tempArray[bufferSize];  //an array capable of holding 20 entries numbered 0 to 19
String presArray[bufferSize];  //an array capable of holding 20 entries numbered 0 to 19
String lightArray[bufferSize];  //an array capable of holding 20 entries numbered 0 to 19
String depthArray[bufferSize];  //an array capable of holding 20 entries numbered 0 to 19
String pHArray[bufferSize];  //an array capable of holding 20 entries numbered 0 to 19
String altitudeArray[bufferSize];  //an array capable of holding 20 entries numbered 0 to 19

byte arrayIndex = 0;

byte xIndex = 0;
byte yIndex = 0;
byte zIndex = 0;
byte tempIndex = 0;
byte presIndex = 0;
byte lightIndex = 0;
byte depthIndex = 0;
byte pHIndex = 0;
byte altitudeIndex = 0;


//INITIALIZE SENSORS

//LED
const int ledPin = 33;

//Depth, Temp, Pressure, Altitude Sensor
MS5837 sensor;

//Accelerometer Variables
int ADXL345 = 0x53; // The ADXL345 sensor I2C address
float X_out, Y_out, Z_out;  // Outputs

//Light Sensor
int photoPin = 36; //Input pin from phototransistor.
float lightread;

//PH Sensor
int phPin = 39;
float phlevel;

// the setup function runs once when you press reset or power the board
void setup() {
  Serial.begin(115200);
  
  Serial.println("Hello Oceans!");
  
  // initialize digital pin ledPin as an output.
  pinMode(ledPin, OUTPUT);

  // initialize pin photoPin as an input.
  pinMode(photoPin, INPUT);

  //Inititalize Accelerometer
  Wire.begin(); // Initiate the Wire library
  Wire.beginTransmission(ADXL345); // Start communicating with the device 
  Wire.write(0x2D); // Access/ talk to POWER_CTL Register - 0x2D
  Wire.write(8); // (8dec -> 0000 1000 binary) Bit D3 High for measuring enable 
  Wire.endTransmission();
  delay(10);
  Serial.println("Accelerometer initialized!");
  
  Wire.begin(); // Initiate the Wire library
  //Depth and Temperature Sensor
  while (!sensor.init()) {
    Serial.println("Init failed!");
    Serial.println("Are SDA/SCL connected correctly?");
    Serial.println("Blue Robotics Bar30: White=SDA, Green=SCL");
    return;
  }
  sensor.setModel(MS5837::MS5837_30BA);
  sensor.setFluidDensity(1029); // kg/m^3 (997 freshwater, 1029 for seawater)
  Serial.println("Depth and Temperature Sensor Initialized");
}

// the loop function runs over and over again forever
void loop() {

  getAcceleration();
  getLight();
  getPH();
  getDepthTemp();
  if (arrayIndex >=bufferSize-1) {  // buffer is full
    initializeWifi();
    while (dataTransferred() == false) {
    Serial.println("Data Transferring...");
    digitalWrite(ledPin, HIGH);   // turn the LED on (HIGH is the voltage level)
    delay(10);                  // wait for a second
    digitalWrite(ledPin, LOW);    // turn the LED off by making the voltage LOW
    delay(10);                  // wait for a second
    }
    Serial.println("killing void loop");
    exit(0); 
    
  } else {
    arrayIndex++;
    delay(3000);
  }
  
}

//SENSOR FUNCTIONS

void getLight() {
  float accumulated = 0;
  for (int i = 0 ; i < 35; i++) {
    accumulated = accumulated + analogRead(photoPin);
  }
  lightread = accumulated / 35; //Oversample light readings. 
  lightArray[arrayIndex] = String(lightread);
  Serial.print(" light= ");
  Serial.print(lightread);
}

void getPH() {
  float accumulated = 0;
  for (int i = 0 ; i < 35; i++) {
    accumulated = accumulated + analogRead(phPin);
//    Serial.println(accumulated);
  }
  float phvoltage = (accumulated * 3.3) / 1024; //Oversample light readings. 
  phlevel = 7 - (2.5 - (phvoltage / 35)) * -5;
  pHArray[arrayIndex] = String(phlevel);
  Serial.print(" ph= ");
  Serial.print(phlevel);
}

void getDepthTemp() {
  
  sensor.read();

  Serial.print(" Pressure= "); 
  Serial.print(sensor.pressure()); //In mbar
  //Serial.print(" mbar");
  presArray[arrayIndex] = String(sensor.pressure());
  
  Serial.print(" Temperature= "); 
  Serial.print(sensor.temperature()); // In Celcius
  //Serial.println(" deg C");
  tempArray[arrayIndex] = String(sensor.temperature());
  
  Serial.print(" Depth= "); 
  Serial.print(sensor.depth()); //In meters
  //Serial.println(" m");
  depthArray[arrayIndex] = String(sensor.depth());
  
  Serial.print(" Altitude= "); 
  Serial.println(sensor.altitude()); //In meters
  altitudeArray[arrayIndex] = String(sensor.altitude());

}

void getAcceleration() {
  
  Wire.beginTransmission(ADXL345);
  Wire.write(0x32); // Start with register 0x32 (ACCEL_XOUT_H)
  Wire.endTransmission(false);
  Wire.requestFrom(ADXL345, 6, true); // Read 6 registers total, each axis value is stored in 2 registers
  X_out = ( Wire.read()| Wire.read() << 8); // X-axis value
  X_out = X_out/256; //For a range of +-2g, we need to divide the raw values by 256, according to the datasheet
  Y_out = ( Wire.read()| Wire.read() << 8); // Y-axis value
  Y_out = Y_out/256;
  Z_out = ( Wire.read()| Wire.read() << 8); // Z-axis value
  Z_out = Z_out/256;
  Serial.print(" Xa= ");
  Serial.print(X_out);
  xArray[arrayIndex] = String(X_out);
  Serial.print(" Ya= ");
  Serial.print(Y_out);
  yArray[arrayIndex] = String(Y_out);
  Serial.print(" Za= ");
  Serial.print(Z_out);
  zArray[arrayIndex] = String(Z_out);
  
}

String readPressure() {
  String returnValue = presArray[presIndex];
  presIndex++;
  return returnValue;
}

String readDepth() {
  String returnValue = depthArray[depthIndex];
  depthIndex++;
  return returnValue;
}

String readTemperature() {
  String returnValue = tempArray[tempIndex];
  tempIndex++;
  return returnValue;
}

String readAltitude() {
  String returnValue = altitudeArray[altitudeIndex];
  altitudeIndex++;
  return returnValue;
}

String readpH() {
  String returnValue = pHArray[pHIndex];
  pHIndex++;
  return returnValue;
}

String readLight() {
  String returnValue = lightArray[lightIndex];
  lightIndex++;
  return returnValue;
}

String readX() {
  String returnValue = xArray[xIndex];
  xIndex++;
  return returnValue;
}

String readY() {
  String returnValue = yArray[yIndex];
  yIndex++;
  return returnValue;
}

String readZ() {
  String returnValue = zArray[zIndex];
  zIndex++;
  return returnValue;
}


void initializeWifi() {

    if(!SPIFFS.begin(true)){
    Serial.println("An Error has occurred while mounting SPIFFS");
    return;
    }

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

    Serial.println(ssid);
  

  // Print ESP32 Local IP Address
  Serial.println(WiFi.localIP());
 
  // Establish async server requests

  server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send(SPIFFS, "/index.html");
  });

  server.on("/pressure", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send_P(200, "text/plain", readPressure().c_str());
   });

  server.on("/temperature", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send_P(200, "text/plain", readTemperature().c_str());
   });

  server.on("/depth", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send_P(200, "text/plain", readDepth().c_str());
   });

  server.on("/altitude", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send_P(200, "text/plain", readAltitude().c_str());
   });

   server.on("/light", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send_P(200, "text/plain", readLight().c_str());
   });

   server.on("/ph", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send_P(200, "text/plain", readpH().c_str());
   });

   server.on("/x", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send_P(200, "text/plain", readX().c_str());
   });

   server.on("/y", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send_P(200, "text/plain", readY().c_str());
   });

   server.on("/z", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send_P(200, "text/plain", readZ().c_str());
   });
   
  server.begin();
    
}

bool dataTransferred() {
  if(xIndex >=bufferSize && yIndex >=bufferSize && zIndex >=bufferSize && presIndex >=bufferSize && tempIndex >=bufferSize && pHIndex >=bufferSize && altitudeIndex >=bufferSize && lightIndex >=bufferSize && depthIndex >=bufferSize) {
    Serial.println("data has been transferred!");
    server.end();
    return true;
  } else {
    return false;
  }
}
