#pragma once

#include <Arduino.h>
#include <Wire.h>
#include "GnssTypes.h"

namespace field_qa {

struct ClockDateTime {
  uint16_t year = 0;
  uint8_t month = 0;
  uint8_t day = 0;
  uint8_t hour = 0;
  uint8_t minute = 0;
  uint8_t second = 0;
};

using WaitForPpsCallback = bool (*)(void* context, uint32_t timeoutMs);

class ClockDiscipline {
 public:
  explicit ClockDiscipline(TwoWire& wire = Wire1);

  bool readRtc(ClockDateTime& out, bool& lowVoltageFlag) const;
  bool readValidRtc(ClockDateTime& out, int64_t* epochOut = nullptr) const;
  bool writeRtc(const ClockDateTime& dt) const;

  bool disciplineFromGnss(const GnssSample& sample,
                          WaitForPpsCallback waitForPps,
                          void* context,
                          ClockDateTime& disciplinedUtc,
                          bool& hadPriorRtc,
                          int64_t& driftSeconds) const;

  static bool isValidDateTime(const ClockDateTime& dt);
  static int64_t toEpochSeconds(const ClockDateTime& dt);
  static bool fromEpochSeconds(int64_t seconds, ClockDateTime& out);
  static void formatIsoUtc(const ClockDateTime& dt, char* out, size_t outSize);
  static void formatCompactUtc(const ClockDateTime& dt, char* out, size_t outSize);
  static void makeRunId(const ClockDateTime& dt, const char* boardId, char* out, size_t outSize);
  static bool fromGnssSample(const GnssSample& sample, ClockDateTime& out);

 private:
  static uint8_t toBcd(uint8_t value);
  static uint8_t fromBcd(uint8_t value);
  static bool isLeapYear(uint16_t year);
  static uint8_t daysInMonth(uint16_t year, uint8_t month);
  static int64_t daysFromCivil(int year, unsigned month, unsigned day);

  TwoWire& m_wire;
};

}  // namespace field_qa
