#include <SPI.h>
#include <SD.h>
//#include <SdFat.h>
//#include <sd_defines.h>
//#include <sd_diskio.h>
#include <Wire.h>
#include "SD.h"
#include "SPI.h"
#include <EEPROM.h>
#include <math.h>
 
 
 
#include <WiFi.h>             
 
#define ServerVersion "1.0"
String  webpage = "";
bool    SPIFFS_present = false;
 
const char ssid[]     = "******";
const char password[] = "******";
 

void append_page_header(){}
void append_page_footer(){}
 
 
 
//Input pin definition
//int const TMP36Pin = 4;  //the analog pin the TMP36's Vout (sense) pin is connected to the resolution is 10 mV/degree centigrade with a 500 mV offset to allow for negative temperatures
int const buttonPin = 27; //logger's press button attached to this pin
 
 
 
/* 
// Let's compare an average of 100
const byte averageCount = 100;  //max. 255, byte variable is used
 
// Variables for the Modified Moving Average
  double movingAverage = 0;
  double movingAverageSum = 0;
*/
 
// Timer variables
  unsigned long previousAutoLogMillis = 0;
  unsigned long currentAutoLogMillis = millis();
            int printInterval = 1000;  // print out measured results every 1000 msec
  unsigned long lastDebounceTime = 0;  // the last time the logger input button was toggled
            int debounceDelay = 10;    // the debounce time; increase if the output flickers
           
//Drunstate monitoring
  int DrunState = 0;
  unsigned int DrunTime = 0;
  byte DrunHour = 0;
  byte DrunMinutes = 0;
  byte DrunSecound = 0;
  unsigned int lastDrunTime = 0;
  unsigned int startTime = 0;
  //int thresholdTemp = 25;
  int thresholdDepth = 1;
  unsigned long previousdepthAreaMillis = 0;
  String formDrunTime;
  String formDate;
 
 
//surface area of the depth profile and avarege depth over time
double depthArea = 0;
double avgDepth = 0;
 
 
//magnetic sensor variables
  int x = 0;
  int y = 0;
  int z = 0;
  int azimuth = 0;
 
//magnetic sensor array for avarage calculation, stores 10 values
  const byte arraySize = 10;
  double myX[arraySize];
  double myY[arraySize];
  double myZ[arraySize];
  double myAzimuth[arraySize];
  double avgX = 0;
  double avgY = 0;
  double avgZ = 0;
  double avgAzimuth = 0;
 
 
//the current address in the EEPROM where you write the number of starts of the device
int addr = 0;
 
//number of device starts
byte starts = 0;
 
//chip pin for the datalogger shield, generally 4,10 or SS
  const byte chip = SS;
 
// logger button state definition
int buttonState = LOW;        // the current reading  the input button
int lastButtonState = LOW;   // the previous reading  the input button
 
 
void setup(void) {
 delay(1000);
 
// Open serial communications and wait for port to open:
  Serial.begin(115200);
  while (!Serial) ; // wait for serial port to connect. Needed for native USB port only
  Serial.println(F("Serial port connected"));
 
 
 
//define pin modes
 // pinMode(TMP36Pin, INPUT);
  pinMode(buttonPin, INPUT);
 
//Join I2C bus
   Wire.begin();
   Serial.println(F("I2C bus connected"));
 
 
//QMC 5853 magnetic sensor initialization
    Serial.println(F("Magnetic sensor initialization..."));
 
//5837 pressure and temperature sensor initialization
 
 
// read a byte  the current address of the EEPROM
  starts = EEPROM.read(addr);
  starts = starts +1;
  EEPROM.write(addr, starts);
  Serial.println(F("EEPROM data updated"));
  Serial.println(starts);
 
 
//SD card initialization
    Serial.println("SD card initialization...");
 if(!SD.begin()){
        Serial.println("Card Mount Failed");
        return;
    }
 
 
    Serial.print("SD Card Type: ");
   
 
 
//create log files on SD card
    File dataFile = SD.open("/Log_" + String(starts) + ".csv", FILE_WRITE);
    dataFile.println("Date,Time,Depth,Temperature,X,Y,Z,Azimuth,AVG");
    Serial.println("Log_" + String(starts) + ".csv is created");
    File customFile = SD.open("/Dlog_" + String(starts) + ".csv", FILE_WRITE);
    customFile.println("Date,Time,Depth,Temperature,X,Y,Z,Azimuth,AVG");
    Serial.println("Dlog_" + String(starts) + ".csv is created");
 
/* 
  // Pre-load MMA
  for (int x=0; x < averageCount; x++)
  movingAverageSum = movingAverageSum + analogRead(TMP36Pin);
  //Serial.print(movingAverageSum);
  //Serial.println(" movingAverageSum ");
 
// Calculate inital average
  movingAverage = movingAverageSum / averageCount;
  //Serial.print(movingAverage);
  //Serial.println(" movingAverage ");
  //Serial.println("MMA preloaded");
*/
 
// Check if RTC is present
  Serial.println("RTC initialization...");
 
 
  //Serial.println (F("Date,Time,Temperature,X,Y,Z,Azimuth, AVG"));
 
 
          // transfer internal memory to the display
  //----------------------------------------------------------------------   
  ///////////////////////////// Server Commands
 
  ///////////////////////////// End of Request commands
  Serial.println("HTTP server started");
 
//  delay(1000);
}
 
void loop(void) {
   
  // make a string for assembling the data to log:
  double dataDepth = 0;
  double dataAVG = 0;
   
  int dataYear = 0;
  int dataMonth = 0;
  int dataDay = 0;
  int dataHour = 0;
  int dataMinute = 0;
  int dataSecond = 0;
  int dataTemp = 0;
 
  int dataX = 0;
  int dataY = 0;
  int dataZ = 0;
  int dataAzimuth = 0;
 
   
//getting the voltage reading  the temperature sensor and append to the string:
//  int TMP36_Read = analogRead(TMP36Pin);
 
// read 5837 sensor data
 
// read the state of the logger button into a local variable:
  int reading = digitalRead(buttonPin);
 
 
// If the switch changed, due to noise or pressing:
  if (reading != lastButtonState) {
    // reset the debouncing timer
    lastDebounceTime = millis();
  }
 
/*
//Modified Moving Avarege method used to smooth sensor data
 
// each interation of loop, update moving average
// Get a new sample
// unsigned int currentValue = analogRead(analogPin);
 
// Remove previous movingAverage  the sum
  movingAverageSum = movingAverageSum - movingAverage;
 
// Replace it with the current sample
  movingAverageSum = movingAverageSum + TMP36_Read;
 
// Recalculate movingAverage
  movingAverage = movingAverageSum / averageCount;
  //Serial.print(movingAverage);
  //Serial.println(" calculated movingAverage ");
 
// analogRead values go  0 to 1023, analogWrite values  0 to 255
double voltage = movingAverage / 1023; // Gets you mV
  //Serial.print(voltage);
  //Serial.println(" voltage ");
 
// calculate the temperature
double temperatureC = (voltage - 0.5) * 100 ;  //ing  10 mv per degree wit 500 mV offset to degrees ((voltage - 500mV) / 10)
*/
 
// check Drun state according to the current temperature
 
//calculate Drun time
    if (DrunState == HIGH) {
        DrunTime = lastDrunTime + millis()/1000 - startTime; 
    }else{
      DrunTime;
      lastDrunTime = DrunTime;
    }
 
    DrunHour = DrunTime/3600;
    DrunMinutes = (DrunTime - (DrunHour * 3600))/60;
    DrunSecound = DrunTime - DrunHour * 3600 - DrunMinutes * 60;
    formDrunTime = String(DrunHour) + ":" + String(DrunMinutes) + ":" + String(DrunSecound);
 
//calculate average temperature based on Druntime. tempArea is the are below the temperature  vs time function
  if (millis() - previousdepthAreaMillis > printInterval){
 
  if(depthArea>0){
    avgDepth = depthArea/DrunTime;
  }
  else{
    avgDepth = 0;
  }
 
  /*Serial.print(DrunTime);
  Serial.print(",");
  Serial.print(temperatureC, 1);
  Serial.print(",");
  Serial.print(tempArea);
  Serial.print(",");
  Serial.println(avgTemp,1);*/
   
// reset the millis clock
 previousdepthAreaMillis = millis();
}
 
 
//azimuth calculation  //is supporting float too
 
 
//append temperature an ddepth data to the string:
  dataAVG = avgDepth,1;
 
//append date and time data to the string:
   
//append compass data to the string:
  dataX = x;
  dataY = y;
  dataZ = z;
  dataAzimuth = azimuth;
 
  // open the file. note that only one file can be open at a time,
  // so you have to close this one before opening another.
 
//    SdFile::dateTimeCallback(dateTime);
 
 
    currentAutoLogMillis = millis();
 
if (currentAutoLogMillis-previousAutoLogMillis >= printInterval){
     // reset the timer
    previousAutoLogMillis = millis();
 
    //open the log file for writing
  //writeFile(SD, "/GenLog.csv", "Date,Time,X,Y,Z,Azimuth,Druntime,AVG\n");
 
 
       
  // if the file is opened write data into it:

 
 
 
// whatever the reading is at, it's been there for longer than the debounce delay, so take it as the actual current state:
 if (millis() - lastDebounceTime > debounceDelay) {
 
  // if the button state has changed:
    if (reading != buttonState) {
      buttonState = reading;
           
    // only toggle the LED if the new button state is HIGH
      if (buttonState == HIGH) {
 
 
    Serial.println(F("Custom log data: "));
   
     for (int i=0; i <= arraySize-1; i++){
            myX[i] = x;
     Serial.print(myX[i]);
     Serial.print(F(" "));
      }
     Serial.println(F(""));
       
      for (int i=0; i <= arraySize-1; i++){
            myY[i] = y;
     Serial.print(myY[i]);
     Serial.print(F(" "));
      }
     Serial.println(F(""));
 
      for (int i=0; i <= arraySize-1; i++){
            myZ[i] = z;
     Serial.print(myZ[i]);
     Serial.print(F(" "));
      }
     Serial.println(F(""));
     
      for (int i=0; i <= arraySize-1; i++){
            myAzimuth[i] = azimuth;
     Serial.print(myAzimuth[i]);
     Serial.print(F(" "));
      }
     Serial.println(F(""));
 
       
        avgX = 0;
        avgY = 0;
        avgZ = 0;
        avgAzimuth = 0;
        for (int i=0; i <= arraySize-1; i++){
            avgX = avgX + myX[i];
            avgY = avgY + myY[i];
            avgZ = avgZ + myZ[i];
            avgAzimuth = avgAzimuth + myAzimuth[i];
      }
 
        Serial.println(F("Avarage values: "));
        avgX = avgX/arraySize;
        avgY = avgY/arraySize;
        avgZ = avgZ/arraySize;
        avgAzimuth = avgAzimuth/arraySize;
       
        Serial.print(F("Avarage X value: "));
        Serial.println(avgX);
        Serial.print(F("Avarage Y value: "));
        Serial.println(avgY);
        Serial.print(F("Avarage Z value: "));
        Serial.println(avgZ);
        Serial.print(F("Avarage Azimuth value: "));
        Serial.println(avgAzimuth);
       
    //open the log file for writing

        Serial.println(F("Custom log written"));
 
    }
  }
 }
 
// save the reading. Next time through the loop, it'll be the lastButtonState
 lastButtonState = reading;   
 
}
 
// Define UI
 
  //Draw UI frames
  /*u8g2.drawRFrame(0,0,41,24,3);
  u8g2.drawRFrame(43,0,42,24,3);
  u8g2.drawRFrame(87,0,41,24,3);
  u8g2.drawRFrame(0,26,41,24,3);
  u8g2.drawRFrame(43,26,42,24,3);
  u8g2.drawRFrame(87,26,41,24,3);
  u8g2.drawRFrame(0,52,85,12,3); */
  /*Serial.print("x   ");
  Serial.println(15*sin(dataAzimuth*2*PI/360));
  Serial.print("y   ");
  Serial.println(15*cos(dataAzimuth*2*PI/360));*/
 
  //Set menu font style and data
 
}
 
 
 
/* Additional functions */
/**************************************/
 
void print2digits(int number) {
  if (number >= 0 && number < 10) {
    Serial.write('0');
  }
    Serial.print(number);
}
 
 
/*
void dateTime(uint16_t* date, uint16_t* time) {
    DateTime now = rtc.now();
  // return date using FAT_DATE macro to format fields
  *date = FAT_DATE(now.year(), now.month(), now.day());
 
  // return time using FAT_TIME macro to format fields
  *time = FAT_TIME(now.hour(), now.minute(), now.second());
}
*/
 
// All supporting functions  here...
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void HomePage(){
  SendHTML_Header();
  webpage += F("<a href='/download'><button>Download</button></a>");
  webpage += F("<a href='/upload'><button>Upload</button></a>");
  //webpage += F("<a href='/stream'><button>Stream</button></a>");
  webpage += F("<a href='/delete'><button>Delete</button></a>");
  webpage += F("<a href='/dir'><button>Directory</button></a>");
  append_page_footer();
  SendHTML_Content();
  SendHTML_Stop(); // Stop is needed because no content length was sent
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void File_Download(){ // This gets called twice, the first pass s the input, the second pass then processes the command line arguments
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void DownloadFile(String filename){
 
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void File_Upload(){
  append_page_header();
  webpage += F("<h3> File to Upload</h3>");
  webpage += F("<FORM action='/fupload' method='post' enctype='multipart/form-data'>");
  webpage += F("<input class='buttons' style='width:40%' type='file' name='fupload' id = 'fupload' value=''><br>");
  webpage += F("<br><button class='buttons' style='width:10%' type='submit'>Upload File</button><br>");
  webpage += F("<a href='/'>[Back]</a><br><br>");
  append_page_footer();
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
File UploadFile;
void handleFileUpload(){ // upload a new file to the Filing system
  
      webpage = "";
      append_page_header();
      webpage += F("<h3>File was successfully uploaded</h3>");
      webpage += F("<h2>Uploaded File Name: "); webpage += "</h2>";
      webpage += F("<h2>File Size: "); webpage +=  "</h2><br>";
      append_page_footer();
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#ifdef ESP32
void SPIFFS_dir(){
  if (SPIFFS_present) {
    File root = SPIFFS.open("/");
    if (root) {
      root.rewindDirectory();
      SendHTML_Header();
      //webpage += F("<h3 class='rcorners_m'>SD Card Contents</h3><br>");
      webpage += F("<table align='center'>");
      webpage += F("<tr><th>Name/Type</th><th style='width:20%'>Type File/Dir</th><th>File Size</th></tr>");
      webpage += F("<a href='/'>[Back]</a><br><br>");
      printDirectory("/",0);
      webpage += F("</table>");
      SendHTML_Content();
      root.close();
    }
    else
    {
      SendHTML_Header();
      webpage += F("<h3>No Files Found</h3>");
    }
    append_page_footer();
    SendHTML_Content();
    SendHTML_Stop();   // Stop is needed because no content length was sent
  } else ReportSPIFFSNotPresent();
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void printDirectory(const char * dirname, uint8_t levels){
  File root = SPIFFS.open(dirname);
  if(!root){
    return;
  }
  if(!root.isDirectory()){
    return;
  }
  File file = root.openNextFile();
  while(file){
    if (webpage.length() > 1000) {
      SendHTML_Content();
    }
    if(file.isDirectory()){
      webpage += "<tr><td>"+String(file.isDirectory()?"Dir":"File")+"</td><td>"+String(file.name())+"</td><td></td></tr>";
      printDirectory(file.name(), levels-1);
    }
    else
    {
      webpage += "<tr><td>"+String(file.name())+"</td>";
      webpage += "<td>"+String(file.isDirectory()?"Dir":"File")+"</td>";
      int bytes = file.size();
      String fsize = "";
      if (bytes < 1024)                     fsize = String(bytes)+" B";
      else if(bytes < (1024 * 1024))        fsize = String(bytes/1024.0,3)+" KB";
      else if(bytes < (1024 * 1024 * 1024)) fsize = String(bytes/1024.0/1024.0,3)+" MB";
      else                                  fsize = String(bytes/1024.0/1024.0/1024.0,3)+" GB";
      webpage += "<td>"+fsize+"</td></tr>";
    }
    file = root.openNextFile();
  }
  file.close();
}
#endif
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#ifdef ESP8266
void SPIFFS_dir(){
  String str;
  if (SPIFFS_present) {
    Dir dir = SPIFFS.openDir("/");
    SendHTML_Header();
    webpage += F("<h3 class='rcorners_m'>SPIFFS Card Contents</h3><br>");
    webpage += F("<table align='center'>");
    webpage += F("<tr><th>Name/Type</th><th style='width:40%'>File Size</th></tr>");
    while (dir.next()) {
      Serial.print(dir.fileName());
      webpage += "<tr><td>"+String(dir.fileName())+"</td>";
      str  = dir.fileName();
      str += " / ";
      if(dir.fileSize()) {
        File f = dir.openFile("r");
        Serial.println(f.size());
        int bytes = f.size();
        String fsize = "";
        if (bytes < 1024)                     fsize = String(bytes)+" B";
        else if(bytes < (1024 * 1024))        fsize = String(bytes/1024.0,3)+" KB";
        else if(bytes < (1024 * 1024 * 1024)) fsize = String(bytes/1024.0/1024.0,3)+" MB";
        else                                  fsize = String(bytes/1024.0/1024.0/1024.0,3)+" GB";
        webpage += "<td>"+fsize+"</td></tr>";
        f.close();
      }
      str += String(dir.fileSize());
      str += "\r\n";
      Serial.println(str);
    }
    webpage += F("</table>");
    SendHTML_Content();
    append_page_footer();
    SendHTML_Content();
    SendHTML_Stop();   // Stop is needed because no content length was sent
  } else ReportSPIFFSNotPresent();
}
#endif
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void File_Stream(){
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void SPIFFS_file_stream(String filename) {
  if (SPIFFS_present) {
  } else ReportSPIFFSNotPresent();
}   
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void File_Delete(){
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void SPIFFS_file_delete(String filename) { // Delete the file
  if (SPIFFS_present) {
    SendHTML_Header();
    append_page_footer();
    SendHTML_Content();
    SendHTML_Stop();
  } else ReportSPIFFSNotPresent();
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void SendHTML_Header(){
  append_page_header();
  webpage = "";
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void SendHTML_Content(){
  webpage = "";
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void SendHTML_Stop(){
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void Input(String heading1, String command, String arg_calling_name){
  SendHTML_Header();
  webpage += F("<h3>"); webpage += heading1 + "</h3>";
  webpage += F("<FORM action='/"); webpage += command + "' method='post'>"; // Must match the calling argument e.g. '/chart' calls '/chart' after ion but with arguments!
  webpage += F("<input type='text' name='"); webpage += arg_calling_name; webpage += F("' value=''><br>");
  webpage += F("<type='submit' name='"); webpage += arg_calling_name; webpage += F("' value=''><br><br>");
  webpage += F("<a href='/'>[Back]</a><br><br>");
  append_page_footer();
  SendHTML_Content();
  SendHTML_Stop();
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void ReportSPIFFSNotPresent(){
  SendHTML_Header();
  webpage += F("<h3>No SPIFFS Card present</h3>");
  webpage += F("<a href='/'>[Back]</a><br><br>");
  append_page_footer();
  SendHTML_Content();
  SendHTML_Stop();
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void ReportFileNotPresent(String target){
  SendHTML_Header();
  webpage += F("<h3>File does not exist</h3>");
  webpage += F("<a href='/"); webpage += target + "'>[Back]</a><br><br>";
  append_page_footer();
  SendHTML_Content();
  SendHTML_Stop();
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void ReportCouldNotCreateFile(String target){
  SendHTML_Header();
  webpage += F("<h3>Could Not Create Uploaded File (write-protected?)</h3>");
  webpage += F("<a href='/"); webpage += target + "'>[Back]</a><br><br>";
  append_page_footer();
  SendHTML_Content();
  SendHTML_Stop();
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
String file_size(int bytes){
  String fsize = "";
  if (bytes < 1024)                 fsize = String(bytes)+" B";
  else if(bytes < (1024*1024))      fsize = String(bytes/1024.0,3)+" KB";
  else if(bytes < (1024*1024*1024)) fsize = String(bytes/1024.0/1024.0,3)+" MB";
  else                              fsize = String(bytes/1024.0/1024.0/1024.0,3)+" GB";
  return fsize;
}
