// ============================================================================
// FICHIER      : NTPClient.h
// DESCRIPTION  : Client NTP pour Arduino - Declaration de classe
// Version      : 99.0.0 (base NTPClient 3.2.1 officielle - FIX v1.1)
// ============================================================================
//
// LIB D'ORIGINE : NTPClient v3.2.1 par Fabrice Weinberg (2015)
// Depot GitHub  : https://github.com/arduino-libraries/NTPClient
// Licence       : MIT
//
// Ce fichier est un fork local. La version 99.0.0 dans library.properties
// bloque toute mise a jour automatique par l'IDE Arduino.
// Voir __DOC_NTPClient.txt pour la documentation complete.
//
// ----------------------------------------------------------------------------
// PRINCIPE DE FONCTIONNEMENT NTP
// ----------------------------------------------------------------------------
//
//  [Arduino / ESP]                    [pool.ntp.org : port UDP 123]
//       |                                          |
//       |--- requete UDP 48 bytes ---------------> |
//       |                                          |
//       |<-- reponse UDP 48 bytes ----------------- |
//       |    bytes[40..43] = Transmit Timestamp    |
//       |    (secondes depuis 01/01/1900)          |
//
//  Conversion : epoch = Transmit Timestamp - 2208988800 (SEVENZYYEARS)
//  Affichage  : epoch + _timeOffset + (millis() - _lastUpdate) / 1000
//
// ----------------------------------------------------------------------------
// STRUCTURE PAQUET NTP (48 bytes)
// ----------------------------------------------------------------------------
//
//  Offset  Taille  Champ
//  ------  ------  -----------------------------------------------
//    0       1     LI(2b) | Version(3b) | Mode(3b)
//    1       1     Stratum (1..15 valide)
//    2       1     Poll Interval
//    3       1     Precision
//    4       4     Root Delay
//    8       4     Root Dispersion
//   12       4     Reference Identifier
//   16       8     Reference Timestamp  <-- verifie par isValid()
//   24       8     Originate Timestamp
//   32       8     Receive Timestamp
//   40       8     Transmit Timestamp   <-- UTILISE pour l'heure
//                  bytes[40..41]=highWord  bytes[42..43]=lowWord
//
// ----------------------------------------------------------------------------
// MACHINE D'ETAT update() / forceUpdate()
// ----------------------------------------------------------------------------
//
//  +----------+   begin()    +----------+
//  |   INIT   | -----------> |  READY   |
//  | _udpSetup|              |  UDP OK  |
//  |  = false |              +----+-----+
//  +----------+                   |
//                                 | forceUpdate()
//                                 v
//                    +-------------------------+
//                    | FLUSH file UDP          |  FIX v1.1
//                    | while(parsePacket)flush |
//                    +----------+--------------+
//                               |
//                    +----------+--------------+
//                    |   sendNTPPacket()        |
//                    |   requete 48 bytes       |
//                    +----------+--------------+
//                               |
//                    +----------+--------------+  <----------+
//                    |   delay(10ms)            |             |
//                    |   cb = parsePacket()     |  cb==0      |
//                    |   timeout++              +------------>+
//                    +----------+--------------+  timeout>100 → false
//                               | cb > 0
//                    +----------+--------------+
//                    |   read(_packetBuffer,48) |
//                    +----------+--------------+
//                               |
//                    +----------+--------------+
//                    |   isValid() ?            |
//                    |   NON → cb=0 ────────────+-->  (reboucle)
//                    |   OUI ↓                  |
//                    +----------+--------------+
//                               |
//                  _lastUpdate  = millis() - (10*(timeout+1))
//                  _currentEpoc = bytes[40..43] - SEVENZYYEARS
//                  return true
//
// ----------------------------------------------------------------------------
// EMPREINTE MEMOIRE STATIQUE (AVR - Uno/Nano)
// ----------------------------------------------------------------------------
//
//  _udp (ptr)            :  2 bytes
//  _udpSetup (bool)      :  1 byte
//  _poolServerName (ptr) :  2 bytes
//  _port (int)           :  2 bytes
//  _timeOffset (int)     :  2 bytes
//  _updateInterval (ul)  :  4 bytes
//  _currentEpoc (ul)     :  4 bytes
//  _lastUpdate (ul)      :  4 bytes
//  _packetBuffer[48]     : 48 bytes
//                          ---------
//  TOTAL                 : 69 bytes / instance
//  (upstream post-3.2.1  : 75 bytes → +6 bytes inutiles sur AVR)
//
// ----------------------------------------------------------------------------
// HISTORIQUE DES VERSIONS
// ----------------------------------------------------------------------------
//
//  v3.2.1 officielle (origine)
//  ---------------------------
//  Base de reference. Contient getFormattedDate(), setEpochTime(),
//  LEAP_YEAR, isValid(). Bug : isValid() compare byte[22] deux fois.
//
//  FIX v1.0
//  --------
//  - REINTEGRE : getFormattedDate(unsigned long secs = 0)
//  - REINTEGRE : setEpochTime(unsigned long secs)
//  - REINTEGRE : macro LEAP_YEAR(Y)
//  - REINTEGRE : isValid() version upstream (plus complete que v3.2.1)
//  - SUPPRIME  : identifiants upstream → bloque auto-update Arduino IDE
//
//  FIX v1.1
//  --------
//  - CORRIGE : bug isValid() ntpPacket[22]==0 && ntpPacket[22]==0
//              → corrige en ntpPacket[22]==0 && ntpPacket[23]==0
//  - AJOUTE  : flush UDP avant sendNTPPacket() dans forceUpdate()
//  - AJOUTE  : isTimeSet() const
//  - AJOUTE  : setPoolServerName(const char*)
//  - CONSERVE: update() return true si pas de maj (API stable)
//  - CONSERVE: getFormattedTime(secs=0) avec parametre (flexibilite)
//
// ============================================================================

#pragma once

#include "Arduino.h"

#include <Udp.h>

#define SEVENZYYEARS          2208988800UL
#define NTP_PACKET_SIZE       48
#define NTP_DEFAULT_LOCAL_PORT 1337
#define LEAP_YEAR(Y)          ( (Y>0) && !(Y%4) && ( (Y%100) || !(Y%400) ) )  //FIX v1.0 : macro reintegree, requise par getFormattedDate()


class NTPClient {
  private:
    UDP*          _udp;
    bool          _udpSetup       = false;

    const char*   _poolServerName = "pool.ntp.org"; // Default time server
    int           _port           = NTP_DEFAULT_LOCAL_PORT;
    int           _timeOffset     = 0;

    unsigned long _updateInterval = 60000;  // In ms

    unsigned long _currentEpoc    = 0;      // In s
    unsigned long _lastUpdate     = 0;      // In ms

    byte          _packetBuffer[NTP_PACKET_SIZE];

    void          sendNTPPacket();
    bool          isValid(byte * ntpPacket);

  public:
    NTPClient(UDP& udp);
    NTPClient(UDP& udp, int timeOffset);
    NTPClient(UDP& udp, const char* poolServerName);
    NTPClient(UDP& udp, const char* poolServerName, int timeOffset);
    NTPClient(UDP& udp, const char* poolServerName, int timeOffset, unsigned long updateInterval);

    /**
     * Starts the underlying UDP client with the default local port
     */
    void begin();

    /**
     * Starts the underlying UDP client with the specified local port
     */
    void begin(int port);

    /**
     * This should be called in the main loop of your application. By default an update from the NTP Server is only
     * made every 60 seconds. This can be configured in the NTPClient constructor.
     *
     * @return true on success or if no update was needed, false on failure
     */
    bool update();

    /**
     * This will force the update from the NTP Server.
     *
     * @return true on success, false on failure (timeout)
     */
    bool forceUpdate();

    /**
     * Returns true if the time has been successfully synced at least once.
     * Check this before displaying time on first boot.
     *
     * @return true if synced, false if never synced since power-on
     */
    bool isTimeSet() const;  //FIX v1.1 : ajout

    int getDay();
    int getHours();
    int getMinutes();
    int getSeconds();

    /**
     * Changes the time offset in seconds. Useful for timezones.
     * Example : 3600 = UTC+1 (France winter), 7200 = UTC+2 (France summer)
     */
    void setTimeOffset(int timeOffset);

    /**
     * Set the update interval to another frequency in milliseconds.
     * Default : 60000 ms. Do not go below 15000 ms (NTP server policy).
     */
    void setUpdateInterval(unsigned long updateInterval);

    /**
     * Set NTP server name dynamically without reconstructing the object.
     * Useful to switch to a backup server if forceUpdate() fails.
     *
     * @param poolServerName  ex: "fr.pool.ntp.org", "time.google.com"
     */
    void setPoolServerName(const char* poolServerName);  //FIX v1.1 : ajout

    /**
     * @return secs argument (or 0 for current time) formatted like `hh:mm:ss`
     */
    String getFormattedTime(unsigned long secs = 0);

    /**
     * @return time in seconds since Jan. 1, 1970 (Unix epoch)
     */
    unsigned long getEpochTime();

    /**
     * @return secs argument (or 0 for current date) formatted as `DD/MM/YYYY`
     *         Uses UTC time - does not apply _timeOffset.
     *         Handles leap years via LEAP_YEAR macro.
     */
    String getFormattedDate(unsigned long secs = 0);  //FIX v1.0 : reintegre

    /**
     * Stops the underlying UDP client
     */
    void end();

    /**
     * Manually inject an epoch timestamp (seconds since Jan. 1, 1970).
     * Useful to restore time from RTC (DS1307, DS3231) after reset.
     * Note : does not update _lastUpdate, isTimeSet() stays false.
     */
    void setEpochTime(unsigned long secs);  //FIX v1.0 : reintegre
};
