#include <Wire.h>
#include <PZEM004Tv30.h>
#include "RTClib.h"
#include <SPI.h>
#include <SD.h>
#include <ESP8266WiFi.h>
#include <WiFiClient.h>
#include <ESP8266WebServer.h>

// Echantillonage
int temps = 10; //en secondes

// Pzem004 variables
float v = 0;
float i = 0; 
float p =0;
float e = 0;
float freq;
float pf;
HardwareSerial hwSerial(UART0);     // Use hwSerial1 UART0 at pins GPIO1 (TX) and GPIO3 (RX)
PZEM004Tv30 pzem(&hwSerial);           // Attach PZEM to hwSerial1
bool pzemrdy = false;

// RTC variables
RTC_DS1307 rtc;
const int sclPin = D1;
const int sdaPin = D2;
char daysOfTheWeek[7][12] = {"Dimanche", "Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi", "Samedi"};

// SDcard variables
static bool hasSD = false;
const int chipSelect = D8;
File uploadFile;

// Wifi Variables
#ifndef APSSID
#define APSSID "ESPap"
#define APPSK  "thereisnospoon"
#endif
/* Set these to your desired credentials. */
int counter =0;
const char *ssid = APSSID;
const char *password = APPSK;
ESP8266WebServer server(80);
/* Just a little test message.  Go to http://192.168.4.1 in a web browser
   connected to this access point to see it.
*/

// Prototypes des fonctions
void MesureLectureEcriture(void);

void returnOK() {
  server.send(200, "text/plain", "");
}

void returnFail(String msg) {
  server.send(500, "text/plain", msg + "\r\n");
}

bool loadFromSdCard(String path) {

  Serial1.print(path);
  String dataType = "text/plain";
  if (path.endsWith("/")) {
    path += "index.htm";
  }

  if (path.endsWith(".src")) {
    path = path.substring(0, path.lastIndexOf("."));
  } else if (path.endsWith(".htm")) {
    dataType = "text/html";
  } else if (path.endsWith(".css")) {
    dataType = "text/css";
  } else if (path.endsWith(".js")) {
    dataType = "application/javascript";
  } else if (path.endsWith(".png")) {
    dataType = "image/png";
  } else if (path.endsWith(".gif")) {
    dataType = "image/gif";
  } else if (path.endsWith(".jpg")) {
    dataType = "image/jpeg";
  } else if (path.endsWith(".ico")) {
    dataType = "image/x-icon";
  } else if (path.endsWith(".xml")) {
    dataType = "text/xml";
  } else if (path.endsWith(".pdf")) {
    dataType = "application/pdf";
  } else if (path.endsWith(".zip")) {
    dataType = "application/zip";
  }

  File dataFile = SD.open(path.c_str());
  if (dataFile.isDirectory()) {
    path += "/index.htm";
    dataType = "text/html";
    Serial1.print(path.c_str());
    dataFile = SD.open(path.c_str());
  }

  if (!dataFile) {

    return false;
  }

  if (server.hasArg("download")) {
    dataType = "application/octet-stream";
  }

  if (server.streamFile(dataFile, dataType) != dataFile.size()) {
    Serial1.println("Sent less data than expected!");
  }

  dataFile.close();

  return true;
}

void handleFileUpload() {
  if (server.uri() != "/edit") {
    return;
  }
  HTTPUpload& upload = server.upload();
  if (upload.status == UPLOAD_FILE_START) {
    if (SD.exists((char *)upload.filename.c_str())) {
      SD.remove((char *)upload.filename.c_str());
    }
    uploadFile = SD.open(upload.filename.c_str(), FILE_WRITE);
    Serial1.print("Upload: START, filename: "); Serial1.println(upload.filename);
  } else if (upload.status == UPLOAD_FILE_WRITE) {
    if (uploadFile) {
      uploadFile.write(upload.buf, upload.currentSize);
    }
    Serial1.print("Upload: WRITE, Bytes: "); Serial1.println(upload.currentSize);
  } else if (upload.status == UPLOAD_FILE_END) {
    if (uploadFile) {
      uploadFile.close();
    }
    Serial1.print("Upload: END, Size: "); Serial1.println(upload.totalSize);
  }
}

void deleteRecursive(String path) {
  File file = SD.open((char *)path.c_str());
  if (!file.isDirectory()) {
    file.close();
    SD.remove((char *)path.c_str());
    return;
  }

  file.rewindDirectory();
  while (true) {
    File entry = file.openNextFile();
    if (!entry) {
      break;
    }
    String entryPath = path + "/" + entry.name();
    if (entry.isDirectory()) {
      entry.close();
      deleteRecursive(entryPath);
    } else {
      entry.close();
      SD.remove((char *)entryPath.c_str());
    }
    yield();
  }

  SD.rmdir((char *)path.c_str());
  file.close();
}

void handleDelete() {
  if (server.args() == 0) {
    return returnFail("BAD ARGS");
  }
  String path = server.arg(0);
  if (path == "/" || !SD.exists((char *)path.c_str())) {
    returnFail("BAD PATH");
    return;
  }
  deleteRecursive(path);
  returnOK();
}

void handleCreate() {
  if (server.args() == 0) {
    return returnFail("BAD ARGS");
  }
  String path = server.arg(0);
  if (path == "/" || SD.exists((char *)path.c_str())) {
    returnFail("BAD PATH");
    return;
  }

  if (path.indexOf('.') > 0) {
    File file = SD.open((char *)path.c_str(), FILE_WRITE);
    if (file) {
      file.write((const char *)0);
      file.close();
    }
  } else {
    SD.mkdir((char *)path.c_str());
  }
  returnOK();
}

void printDirectory() {
  if (!server.hasArg("dir")) {
    return returnFail("BAD ARGS");
  }
  String path = server.arg("dir");
  if (path != "/" && !SD.exists((char *)path.c_str())) {
    return returnFail("BAD PATH");
  }
  File dir = SD.open((char *)path.c_str());
  path = String();
  if (!dir.isDirectory()) {
    dir.close();
    return returnFail("NOT DIR");
  }
  dir.rewindDirectory();
  server.setContentLength(CONTENT_LENGTH_UNKNOWN);
  server.send(200, "text/json", "");
  WiFiClient client = server.client();

  server.sendContent("[");
  for (int cnt = 0; true; ++cnt) {
    File entry = dir.openNextFile();
    if (!entry) {
      break;
    }

    String output;
    if (cnt > 0) {
      output = ',';
    }

    output += "{\"type\":\"";
    output += (entry.isDirectory()) ? "dir" : "file";
    output += "\",\"name\":\"";
    output += entry.name();
    output += "\"";
    output += "}";
    server.sendContent(output);
    entry.close();
  }
  server.sendContent("]");
  dir.close();
}

void handleNotFound() {
  if (hasSD && loadFromSdCard(server.uri())) {
    return;
  }
  String message = "SDCARD Not Detected\n\n";
  message += "URI: ";
  message += server.uri();
  message += "\nMethod: ";
  message += (server.method() == HTTP_GET) ? "GET" : "POST";
  message += "\nArguments: ";
  message += server.args();
  message += "\n";
  for (uint8_t i = 0; i < server.args(); i++) {
    message += " NAME:" + server.argName(i) + "\n VALUE:" + server.arg(i) + "\n";
  }
  server.send(404, "text/plain", message);
  Serial1.print(message);
}
// WEBSERVER declaration end



void setup () {
// Pinout Setup
  Wire.begin(sdaPin, sclPin);
  pinMode(16,INPUT);

//Communication Setup
  Serial1.begin(57600);
/*
// PZEM004T Setup
  while (!pzemrdy) {
      Serial1.println("Connecting to PZEM...");
      pzemrdy = pzem.setAddress(ip);
      delay(1000);
   }
*/
pzem.setAddress(0x42);
   
// RTC Setup 
  if (! rtc.begin()) {
    Serial1.println("Couldn't find RTC");
    while (1);
  }
  if (! rtc.isrunning()) {
    Serial1.println("RTC is NOT running!");
    // following line sets the RTC to the date & time this sketch was compiled
    //rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
    // This line sets the RTC with an explicit date & time, for example to set
    // January 21, 2014 at 3am you would call:
    // rtc.adjust(DateTime(2014, 1, 21, 3, 0, 0));
    rtc.adjust(DateTime(2019, 8, 23, 10, 11, 0));
  }
  Serial1.print("Initializing SD card...");

//SDcard Setup
  // see if the card is present and can be initialized:
  if (!SD.begin(chipSelect)) {
    Serial1.println("Card failed, or not present");
    // don't do anything more:
    return;
  }
  Serial1.println("card initialized.");
  hasSD = true;


// Wifi setup    
  Serial1.print("Configuring access point...");
  /* You can remove the password parameter if you want the AP to be open. */
  WiFi.softAP(ssid, password);

  IPAddress myIP = WiFi.softAPIP();
  Serial1.print("AP IP address: ");
  Serial1.println(myIP);
  server.on("/list", HTTP_GET, printDirectory);
  server.on("/edit", HTTP_DELETE, handleDelete);
  server.on("/edit", HTTP_PUT, handleCreate);
  server.on("/edit", HTTP_POST, []() {
    returnOK();
  }, handleFileUpload);
  server.onNotFound(handleNotFound);  
  
  server.begin();
  Serial1.println("HTTP server started");
  
}

void loop () {
 server.handleClient();


 delay(100);
 counter = counter+1;

 if(counter==(10*temps)){
 MesureLectureEcriture();
 counter =0;
 }
}


void MesureLectureEcriture(){
//PZEM Part
  v = pzem.voltage();
  if (v < 0.0) v = 0.0;
   Serial1.print(v);Serial1.print("V; ");
   i = pzem.current();
   if(i >= 0.0){ Serial1.print(i);Serial1.print("A; "); }
  p = pzem.power();
   if(p >= 0.0){ Serial1.print(p);Serial1.print("W; "); }
   e = pzem.energy();
   if(e >= 0.0){ Serial1.print(e);Serial1.print("Wh; "); } 
  freq = pzem.frequency();
  Serial1.print("Frequency: ");
  Serial1.print(freq);
  Serial1.print("Hz");
  pf = pzem.pf();
  Serial1.print("PF: ");
  Serial1.println(pf);
   
// RTC Part
   DateTime now = rtc.now();
    Serial1.print(daysOfTheWeek[now.dayOfTheWeek()]);
    Serial1.print(",  ");
   
    if (now.day() < 10)    {Serial1.print('0');}
    Serial1.print(now.day(), DEC);
    Serial1.print('.');
    if (now.month() < 10)  {Serial1.print('0');}
    Serial1.print(now.month(), DEC);
    Serial1.print('.');
    Serial1.print(now.year(), DEC);
    Serial1.print(",  ");
   
    if (now.hour() < 10)   {Serial1.print('0');}
    Serial1.print(now.hour(), DEC);
    Serial1.print(':');
    if (now.minute() < 10) {Serial1.print('0');}
    Serial1.print(now.minute(), DEC);
    Serial1.print(':');
    if (now.second() < 10) {Serial1.print('0');}
    Serial1.print(now.second(), DEC);
    Serial1.print(",  ");
    Serial1.println(); 

//SDCARD Part
    File dataFile = SD.open("datalog.csv", FILE_WRITE);
  // if the file is available, write to it:
  if (dataFile) {
    if (now.day() < 10)    {dataFile.print('0');}
    dataFile.print(now.day(), DEC);
    dataFile.print('-');
    if (now.month() < 10)  {dataFile.print('0');}
    dataFile.print(now.month(), DEC);
    dataFile.print('-');
    dataFile.print(now.year(), DEC);
    dataFile.print("  ");
    if (now.hour() < 10)   {dataFile.print('0');}
    dataFile.print(now.hour(), DEC);
    dataFile.print(':');
    if (now.minute() < 10) {dataFile.print('0');}
    dataFile.print(now.minute(), DEC);
    dataFile.print(':');
    if (now.second() < 10) {dataFile.print('0');}
    dataFile.print(now.second(), DEC);   
    dataFile.print(";");
    dataFile.print(v);
    dataFile.print(';');
    dataFile.print(i);
    dataFile.print(';');
    dataFile.print(p);
    dataFile.print(';');
    dataFile.print(e);
    dataFile.print(';');
    dataFile.print(pf);
    dataFile.print(';');
    dataFile.print(freq);
    dataFile.println();
    dataFile.close();
  }
  // if the file isn't open, pop up an error:
  else {
    Serial1.println("error opening datalog.csv");
  }
}
