/*
 * Time_NTP.pde
 * Example showing time sync to NTP time source
 *
 * Also shows how to handle DST automatically.
 *
 * This sketch uses the EtherCard library:
 * http://jeelabs.org/pub/docs/ethercard/
 */
 
#include <TimeLib.h>
#include <EtherCard.h>

byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; 

// NTP Server
const char timeServer[] PROGMEM = "pool.ntp.org";

const int utcOffset = 1;     // Central European Time
//const int utcOffset = -5;  // Eastern Standard Time (USA)
//const int utcOffset = -4;  // Eastern Daylight Time (USA)
//const int utcOffset = -8;  // Pacific Standard Time (USA)
//const int utcOffset = -7;  // Pacific Daylight Time (USA)

// Packet buffer, must be big enough to packet and payload
#define BUFFER_SIZE 550
byte Ethernet::buffer[BUFFER_SIZE];

const unsigned int remotePort = 123;

void setup() 
{
  Serial.begin(9600);
  
  while (!Serial)    // Needed for Leonardo only
    ;
  delay(250);
  
  Serial.println("TimeNTP_ENC28J60 Example");
  
  if (ether.begin(BUFFER_SIZE, mac) == 0) {
     // no point in carrying on, so do nothing forevermore:
    while (1) {
      Serial.println("Failed to access Ethernet controller");
      delay(10000);
    }
  }
  
  if (!ether.dhcpSetup()) {
    // no point in carrying on, so do nothing forevermore:
    while (1) {
      Serial.println("Failed to configure Ethernet using DHCP");
      delay(10000);
    }
  }

  ether.printIp("IP number assigned by DHCP is ", ether.myip);

  Serial.println("waiting for sync");
  //setSyncProvider(getNtpTime);            // Use this for GMT time
  setSyncProvider(getDstCorrectedTime);     // Use this for local, DST-corrected time
}

time_t prevDisplay = 0; // when the digital clock was displayed

void loop()
{
  if (timeStatus() != timeNotSet) {
    if (now() != prevDisplay) { //update the display only if time has changed
      prevDisplay = now();
      digitalClockDisplay();  
    }
  }
}

void digitalClockDisplay(){
  // digital clock display of the time
  Serial.print(hour());
  printDigits(minute());
  printDigits(second());
  Serial.print(" ");
  Serial.print(day());
  Serial.print(" ");
  Serial.print(month());
  Serial.print(" ");
  Serial.print(year()); 
  Serial.println(); 
}

void printDigits(int digits){
  // utility for digital clock display: prints preceding colon and leading 0
  Serial.print(":");
  if(digits < 10)
    Serial.print('0');
  Serial.print(digits);
}

/*-------- NTP code ----------*/

// SyncProvider that returns UTC time
time_t getNtpTime()
{
  // Send request
  Serial.println("Transmit NTP Request");
  if (!ether.dnsLookup(timeServer)) {
    Serial.println("DNS failed");
    return 0; // return 0 if unable to get the time
  } else {
    //ether.printIp("SRV: ", ether.hisip);
    ether.ntpRequest(ether.hisip, remotePort);
  
    // Wait for reply
    uint32_t beginWait = millis();
    while (millis() - beginWait < 1500) {
      word len = ether.packetReceive();
      ether.packetLoop(len);

      unsigned long secsSince1900 = 0L;
      if (len > 0 && ether.ntpProcessAnswer(&secsSince1900, remotePort)) {
        Serial.println("Receive NTP Response");
        return secsSince1900 - 2208988800UL;
      }
    }
    
    Serial.println("No NTP Response :-(");
    return 0;
  }
}

/* Alternative SyncProvider that automatically handles Daylight Saving Time (DST) periods,
 * at least in Europe, see below.
 */
time_t getDstCorrectedTime (void) {
  time_t t = getNtpTime ();

  if (t > 0) {
    TimeElements tm;
    breakTime (t, tm);
    t += (utcOffset + dstOffset (tm.Day, tm.Month, tm.Year + 1970, tm.Hour)) * SECS_PER_HOUR;
  }

  return t;
}

/* This function returns the DST offset for the current UTC time.
 * This is valid for the EU, for other places see
 * http://www.webexhibits.org/daylightsaving/i.html
 * 
 * Results have been checked for 2012-2030 (but should work since
 * 1996 to 2099) against the following references:
 * - http://www.uniquevisitor.it/magazine/ora-legale-italia.php
 * - http://www.calendario-365.it/ora-legale-orario-invernale.html
 */
byte dstOffset (byte d, byte m, unsigned int y, byte h) {
  // Day in March that DST starts on, at 1 am
  byte dstOn = (31 - (5 * y / 4 + 4) % 7);

  // Day in October that DST ends  on, at 2 am
  byte dstOff = (31 - (5 * y / 4 + 1) % 7);

  if ((m > 3 && m < 10) ||
      (m == 3 && (d > dstOn || (d == dstOn && h >= 1))) ||
      (m == 10 && (d < dstOff || (d == dstOff && h <= 1))))
    return 1;
  else
    return 0;
}