#include <Wire.h>
#include <SPI.h>
#include <Ethernet.h>
#include "WebServer.h"
#include "BMP085.h"
#include "DHT.h"
#include "TSL2561.h"
#include "Chronodot.h"

#define TIMEZONE 7200 // 2 hours
#define DHTPIN  2
#define DHTTYPE DHT22
#define PREFIX  ""
#define NTP_PACKET_SIZE 48

static uint8_t   mac[]      = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
static IPAddress ip         = IPAddress(192, 168, 1, 210);
static IPAddress timeserver = IPAddress(192, 43, 244, 18);

P(indexHtml) = "<!DOCTYPE html><html><head><meta charset='utf-8' /><title>Station Météo</title>"
        "<style>html, body{background: #000;color: #fff;}body{font-family: 'DejaVu Sans', Verdana, Arial, sans-serif;font-size: 100%;margin: 0;padding: 0;}#home{background: #000;color: #fff;margin: 1em;padding: 0;}#home h1{color: #f7c000;font-size: 2em;margin: 1em;text-align: center;}#home p{background: #111;border: 1px solid #444;font-size: 1.5em;list-style-type: none;padding: 0;width: 100%;text-align: center;}#home table{background: #111;border: 1px solid #444;font-size: 1.5em;list-style-type: none;padding: 0;width: 100%;text-align: center;}#home ul#links li{font-weight: bold;margin: 0;padding: 0.8em;text-align: center;}#footer{background: #000;color: #888;font-size: 0.8em;margin: 1em;text-align: center;padding: 0;}</style>"
        "</head><body>"
        "<div id='home'><h1>Station Météo</h1>"
        "<table><tr><th>Temps</th><th>T [°C]</th><th>P [Pa]</th><th>Alt [m]</th><th>Hr [%]</th><th>Lx [lux]</th></tr>"
        "<tr><td>0</td><td>0</td><td>0</td><td>0</td><td>0</td><td>0</td></tr></table>"
        "<p><label>Intervale de mise à jour (en secondes) : </label><input id='interval' type='number' value=2 /></p></div><div id='footer'>par Nathanal Restori</div>"
        "<script>function updateInfo(){var xhr=new XMLHttpRequest();xhr.open('GET', '/sensors.json');xhr.onreadystatechange=function(){if (xhr.readyState==4 && xhr.status==200){var response=JSON.parse(xhr.responseText);var rows=document.getElementsByTagName('tr');var cell=rows[1].firstChild;var index=0;while (cell){cell.firstChild.nodeValue=response.sensors[index].data;cell=cell.nextSibling;index++;}}};xhr.send(null);}updateInfo();var updateID=setInterval(updateInfo, 2000);var interval=document.getElementById('interval');interval.addEventListener('change', function(e){clearInterval(updateID);updateID=setInterval(updateInfo, e.target.value*1000);}, true);</script>"
        "</body></html>";

// no-cost stream operator as described at
// http://sundial.org/arduino/?page_id=119
    template<class T>
inline Print &operator <<(Print &obj, T arg)
{ obj.print(arg); return obj; }

BMP085    bmp;
DHT       dht(DHTPIN, DHTTYPE);
TSL2561   tsl(TSL2561_ADDR_FLOAT);
Chronodot chronodot;
WebServer webserver(PREFIX, 80);

byte packetBuffer[ NTP_PACKET_SIZE];
EthernetUDP Udp;

void defaultCmd(WebServer &server, WebServer::ConnectionType type, char *url_tail, bool tail_complete) {
    server.httpSuccess();
    server.printP(indexHtml);
}

void sensorsJsonCmd(WebServer &server, WebServer::ConnectionType type, char *url_tail, bool tail_complete) {
    float   temperature = bmp.readTemperature();
    int32_t pressure    = bmp.readPressure();
    float   altitude    = bmp.readAltitude();

    float humidity = dht.readHumidity();

    uint32_t lum = tsl.getFullLuminosity();
    uint16_t lightIr   = lum >> 16;
    uint16_t lightFull = lum & 0xFFFF;

    DateTime time = chronodot.now();

    server.httpSuccess("application/json");

    server << "{ \"sensors\": ["
        << "{\"data\":\"" << time.hour() << ":" << time.minute() << ":" << time.second() << " " << time.day() << "/" << time.month() << "/" << time.year() << "\"},"
        << "{\"data\":" << temperature << "},"
        << "{\"data\":" << pressure << "},"
        << "{\"data\":" << altitude << "},"
        << "{\"data\":" << humidity << "},"
        << "{\"data\":" << tsl.calculateLux(lightFull, lightIr) << "}"
        << "] }";
}

void sendNTPpacket() {
  // set all bytes in the buffer to 0
  memset(packetBuffer, 0, NTP_PACKET_SIZE);
  // Initialize values needed to form NTP request
  // (see URL above for details on the packets)
  packetBuffer[0] = 0b11100011;   // LI, Version, Mode
  packetBuffer[1] = 0;     // Stratum, or type of clock
  packetBuffer[2] = 6;     // Polling Interval
  packetBuffer[3] = 0xEC;  // Peer Clock Precision
  // 8 bytes of zero for Root Delay & Root Dispersion
  packetBuffer[12]  = 49;
  packetBuffer[13]  = 0x4E;
  packetBuffer[14]  = 49;
  packetBuffer[15]  = 52;

  // all NTP fields have been given values, now
  // you can send a packet requesting a timestamp:
  Udp.beginPacket(timeserver, 123); //NTP requests are to port 123
  Udp.write(packetBuffer,NTP_PACKET_SIZE);
  Udp.endPacket();
  Serial.println("Sending ntp packet");

  delay(1000);
  if ( Udp.parsePacket() ) {
    // We've received a packet, read the data from it
    Udp.read(packetBuffer,NTP_PACKET_SIZE);  // read the packet into the buffer

    //the timestamp starts at byte 40 of the received packet and is four bytes,
    // or two words, long. First, esxtract the two words:

    unsigned long highWord = word(packetBuffer[40], packetBuffer[41]);
    unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]);
    // combine the four bytes (two words) into a long integer
    // this is NTP time (seconds since Jan 1 1900):
    unsigned long secsSince1900 = highWord << 16 | lowWord;
    // Unix time starts on Jan 1 1970. In seconds, that's 2208988800:
    const unsigned long seventyYears = 2208988800UL;
    // subtract seventy years:
    unsigned long epoch = secsSince1900 - seventyYears + TIMEZONE;

    Serial.println("Adjusting time");
    chronodot.adjust(DateTime(epoch));
  }
}

void setup() {
    Serial.begin(9600);
    pinMode(5, OUTPUT);
    SPI.begin();

    //----- Ethernet/WebServer
    Serial.println("INIT Ethernet");

    Ethernet.begin(mac, ip);
    webserver.begin();

    webserver.setDefaultCommand(&defaultCmd);
    webserver.addCommand("sensors.json", &sensorsJsonCmd);

    //----- Sensors
    Serial.println("INIT Sensors");

    bmp.begin();
    dht.begin();
    tsl.begin();
    chronodot.begin();

    // You can change the gain on the fly, to adapt to brighter/dimmer light situations
    //tsl.setGain(TSL2561_GAIN_0X);         // set no gain (for bright situtations)
    tsl.setGain(TSL2561_GAIN_16X);      // set 16x gain (for dim situations)

    // Changing the integration time gives you a longer time over which to sense light
    // longer timelines are slower, but are good in very low light situtations!
    tsl.setTiming(TSL2561_INTEGRATIONTIME_13MS);  // shortest integration time (bright light)
    //tsl.setTiming(TSL2561_INTEGRATIONTIME_101MS);  // medium integration time (medium light)
    //tsl.setTiming(TSL2561_INTEGRATIONTIME_402MS);  // longest integration time (dim light)

    Udp.begin(8888);
    sendNTPpacket();
}

void loop() {
    webserver.processConnection();

    delay(500);
}
