#include "NoiseSensor.h"

#include <SPI.h>    // For WiFi, I think
#include <WiFiClientSecure.h>
#include "time.h"

#define SNTP_UPDATE_DELAY 300000

// ---> Fill in your WiFi name (SSID) and password here. <---
#define SECRET_SSID "CHANGEME"
#define SECRET_PASSWORD "CHANGEME"

// Keep these values like this unless you know what you're doing.
const char server[] = "api.opensensemap.org";
const uint8_t tick_count = 30;
const bool verbose = 1;

// ---> Fill this in yourself. <---
// SenseBox IDs from the web interface in order 0, 1, 2, 3, 4. Master is ID 0!
const char *sensebox_ids[] = {"CHANGEME", "", "CHANGEME", "", ""};
// Sensor IDs from the web interface in the same order as above.
const char *sensor_ids[] = {"CHANGEME", "", "CHANGEME", "", ""};

// =================

WiFiClientSecure client;
uint32_t last_reading;

WiFiUDP udp;

void setup() {
  delay(2000);
  Serial.begin(115200);
  Serial.println("\nStarting wifi connection...");


  // Retry WiFi connection until it succeeds.
  do {
    WiFi.begin(SECRET_SSID, SECRET_PASSWORD);
    uint32_t wifi_start_time = millis();
    while (WiFi.status() != WL_CONNECTED) {
      delay(500);
      Serial.print(".");
      if (millis() - wifi_start_time >= 5000) {
        Serial.println("\nCouldn't connect to Wifi. Try again.");
        //WiFi.end();
        break;
      }
    } 
  } 
  while (WiFi.status() != WL_CONNECTED);

  Serial.print("\nConnected to AP ");
  Serial.println(SECRET_SSID);
  client.setInsecure();

  // Let the sensors know we're ready and start measuring.
  if (!NoiseSensor.begin(tick_count)) {
    Serial.println("Something didn't work. Please restart the system.");
    while (1) {
      Serial.println("Error, please restart.");
    }
  }
  NoiseSensor.setIds(sensebox_ids, sensor_ids);

  delay(1000);
}

void loop() {
  // Shows the openSenseMap's response.
  if (verbose) {
    while (client.available()) {
        char c = client.read();
        Serial.write(c);
    }
  }

  // This check is clumsy but will be replaced by a proper timer interrupt in the future.
  if (NoiseSensor.beaconReady()) {
    NoiseSensor.sendSyncBeacon();
  }

  // This check is clumsy but will be replaced by a proper timer interrupt in the future.
  if (millis() - last_reading >= 2000 && NoiseSensor.requestReady()) {

    Serial.println();
    NoiseSensor.read();

    // ---> Choose which sensor's data you want to upload by calling upload(device_id).
    upload(0, server);
    upload(2, server);

    // Record the last upload time to make sure we don't read and upload multiple times per second.
    last_reading = millis();
  }
}

void upload(uint8_t device_id, const char* server) {
  Serial.println("Start upload");

  if (client.connected()) {
    client.stop();
    delay(500);
  }
  
  // If there's a successful connection:
  if (client.connect(server, 443)) {
    String uploadbuf;
    String json = NoiseSensor.buildSenseBoxJSON(device_id);
    uint16_t content_length = json.length();
    Serial.println("connecting...");

    uploadbuf = NoiseSensor.buildHTTPHeader(device_id, server, content_length);

    client.println(uploadbuf);   
    if (verbose) { Serial.println(uploadbuf); }

    client.println();
    if (verbose) { Serial.println(); }

    // For some reason that nobody knows, client.print has an arbitrary input string limit. 
    // Therefore, we need to split it at some arbitrary length and provide chunks.
    for (uint16_t idx = 0; idx < json.length(); idx += 1000) {
      client.print(json.substring(idx, idx + 1000));
      if (verbose) { Serial.print(json.substring(idx, idx + 1000)); }
    }
    
    client.println();
    if (verbose) { Serial.println(); }
    
    Serial.println("done!");
        
  } else {
    // If we couldn't make a connection:
    Serial.println("connection failed. Restart System.");
    delay(1000);
  }

  Serial.println("Upload done");
}
