/**
 * Copyright (c) 2011-2021 Bill Greiman
 * This file is part of the SdFat library for SD memory cards.
 *
 * MIT License
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 */
#ifndef FsDateTime_h
#define FsDateTime_h
#include <stdint.h>
#include "CompileDateTime.h"
#include "SysCall.h"

/** Backward compatible definition. */
#define FAT_DATE(y, m, d) FS_DATE(y, m, d)

/** Backward compatible definition. */
#define FAT_TIME(h, m, s) FS_TIME(h, m, s)

/** Date time callback */
namespace FsDateTime {
  /** Date time callback. */
  extern void (*callback)(uint16_t* date, uint16_t* time, uint8_t* ms10);
  /** Date time callback. */
  extern void (*callback2)(uint16_t* date, uint16_t* time);
  /** Cancel callback. */
  void clearCallback();
   /** Set the date/time callback function.
   *
   * \param[in] dateTime The user's call back function.  The callback.
   * function is of the form:
   *
   * \code
   * void dateTime(uint16_t* date, uint16_t* time) {
   *   uint16_t year;
   *   uint8_t month, day, hour, minute, second;
   *
   *   // User gets date and time from GPS or real-time clock here.
   *
   *   // Return date using FS_DATE macro to format fields.
   *   *date = FS_DATE(year, month, day);
   *
   *   // Return time using FS_TIME macro to format fields.
   *   *time = FS_TIME(hour, minute, second);
   * }
   * \endcode
   *
   * Sets the function that is called when a file is created or when
   * a file's directory entry is modified by sync(). All timestamps,
   * access, creation, and modify, are set when a file is created.
   * sync() maintains the last access date and last modify date/time.
   *
   */
  void setCallback(void (*dateTime)(uint16_t* date, uint16_t* time));
   /** Set the date/time callback function.
   *
   * \param[in] dateTime The user's call back function.  The callback
   * function is of the form:
   *
   * \code
   * void dateTime(uint16_t* date, uint16_t* time, uint8_t* ms10) {
   *   uint16_t year;
   *   uint8_t month, day, hour, minute, second;
   *
   *   // User gets date and time from GPS or real-time clock here.
   *
   *   // Return date using FS_DATE macro to format fields
   *   *date = FS_DATE(year, month, day);
   *
   *   // Return time using FS_TIME macro to format fields
   *   *time = FS_TIME(hour, minute, second);
   *
   *   // Return the time since the last even second in units of 10 ms.
   *   // The granularity of the seconds part of FS_TIME is 2 seconds so
   *   // this field is a count of hundredth of a second and its valid
   *   // range is 0-199 inclusive.
   *   // For a simple RTC return 100*(seconds & 1).
   *   *ms10 = <tens of ms since even second>
   * }
   * \endcode
   *
   * Sets the function that is called when a file is created or when
   * a file's directory entry is modified by sync(). All timestamps,
   * access, creation, and modify, are set when a file is created.
   * sync() maintains the last access date and last modify date/time.
   *
   */
  void setCallback(
    void (*dateTime)(uint16_t* date, uint16_t* time, uint8_t* ms10));
}  // namespace FsDateTime

/** date field for directory entry
 * \param[in] year [1980,2107]
 * \param[in] month [1,12]
 * \param[in] day [1,31]
 *
 * \return Packed date for directory entry.
 */
static inline uint16_t FS_DATE(uint16_t year, uint8_t month, uint8_t day) {
  year -= 1980;
  return year > 127 || month > 12 || day > 31 ? 0 :
         year << 9 | month << 5 | day;
}
/** year part of FAT directory date field
 * \param[in] fatDate Date in packed dir format.
 *
 * \return Extracted year [1980,2107]
 */
static inline uint16_t FS_YEAR(uint16_t fatDate) {
  return 1980 + (fatDate >> 9);
}
/** month part of FAT directory date field
 * \param[in] fatDate Date in packed dir format.
 *
 * \return Extracted month [1,12]
 */
static inline uint8_t FS_MONTH(uint16_t fatDate) {
  return (fatDate >> 5) & 0XF;
}
/** day part of FAT directory date field
 * \param[in] fatDate Date in packed dir format.
 *
 * \return Extracted day [1,31]
 */
static inline uint8_t FS_DAY(uint16_t fatDate) {
  return fatDate & 0X1F;
}
/** time field for directory entry
 * \param[in] hour [0,23]
 * \param[in] minute [0,59]
 * \param[in] second [0,59]
 *
 * \return Packed time for directory entry.
 */
static inline uint16_t FS_TIME(uint8_t hour, uint8_t minute, uint8_t second) {
  return hour > 23 || minute > 59 || second > 59 ? 0 :
         hour << 11 | minute << 5 | second >> 1;
}
/** hour part of FAT directory time field
 * \param[in] fatTime Time in packed dir format.
 *
 * \return Extracted hour [0,23]
 */
static inline uint8_t FS_HOUR(uint16_t fatTime) {
  return fatTime >> 11;
}
/** minute part of FAT directory time field
 * \param[in] fatTime Time in packed dir format.
 *
 * \return Extracted minute [0,59]
 */
static inline uint8_t FS_MINUTE(uint16_t fatTime) {
  return (fatTime >> 5) & 0X3F;
}
/** second part of FAT directory time field
 * N\note second/2 is stored in packed time.
 *
 * \param[in] fatTime Time in packed dir format.
 *
 * \return Extracted second [0,58]
 */
static inline uint8_t FS_SECOND(uint16_t fatTime) {
  return 2*(fatTime & 0X1F);
}
char* fsFmtDate(char* str, uint16_t date);
char* fsFmtTime(char* str, uint16_t time);
char* fsFmtTime(char* str, uint16_t time, uint8_t sec100);
char* fsFmtTimeZone(char* str, int8_t tz);
size_t fsPrintDate(print_t* pr, uint16_t date);
size_t fsPrintDateTime(print_t* pr, uint16_t date, uint16_t time);
size_t fsPrintDateTime(print_t* pr, uint32_t dateTime);
size_t fsPrintDateTime(print_t* pr, uint32_t dateTime, uint8_t s100, int8_t tz);
size_t fsPrintTime(print_t* pr, uint16_t time);
size_t fsPrintTime(print_t* pr, uint16_t time, uint8_t sec100);
size_t fsPrintTimeZone(print_t* pr, int8_t tz);
#endif  // FsDateTime_h
