// SPDX-License-Identifier: MPL-2.0

//! \file impl_time_utils.h
//! \brief This is an internal header to handle the internal definitions for time_utils.h
//!        Do NOT include this and use these functions directly. Include time_utils.h and use the calls from there
//!        The functions with "impl" in the name expect __FILE__, __func__, __LINE__, and an expression when called
//!        which get wrapped by time_utils.h to make this easier for you to use.
//! \copyright
//! Do NOT modify or remove this copyright and license
//!
//! Copyright (c) 2024-2026 Seagate Technology LLC and/or its Affiliates, All Rights Reserved
//!
//! This software is subject to the terms of the Mozilla Public License, v. 2.0.
//! If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.

#pragma once

#include "code_attributes.h"
#include "common_types.h"

#include <time.h>

#if defined(__cplusplus)
extern "C"
{
#endif

#if defined(HAVE_CONSTEXPR)
    M_ATTR_UNUSED constexpr const time_t* nulltimet = M_NULLPTR;
#    define M_IS_NULL_TIMET(ptr) ((ptr) == nulltimet)
    M_ATTR_UNUSED constexpr struct tm* nullstructtm = M_NULLPTR;
#    define M_IS_NULL_STRUCT_TM(ptr) ((ptr) == nullstructtm)
    M_ATTR_UNUSED constexpr const struct tm* nullconststructtm = M_NULLPTR;
#    define M_IS_NULL_CONST_STRUCT_TM(ptr) ((ptr) == nullconststructtm)
    M_ATTR_UNUSED constexpr char* nulltimestr = M_NULLPTR;
#    define M_IS_NULL_TIME_STR(ptr) ((ptr) == nulltimestr)
#else
#    define M_IS_NULL_TIMET(ptr)           (!(ptr))
#    define M_IS_NULL_STRUCT_TM(ptr)       (!(ptr))
#    define M_IS_NULL_CONST_STRUCT_TM(ptr) (!(ptr))
#    define M_IS_NULL_TIME_STR(ptr)        (!(ptr))
#endif

    //! \fn struct tm* impl_safe_gmtime(const time_t* M_RESTRICT timer,
    //!                                 struct tm* M_RESTRICT    buf,
    //!                                 const char*              file,
    //!                                 const char*              function,
    //!                                 int                      line,
    //!                                 const char*              expression)
    //! \brief Internal implementation of safe_gmtime.
    //!
    //! Use in place of gmtime(). Calls gmtime_r or gmtime_s internally.
    //! gmtime() uses an internal buffer and is not thread safe. This uses thread safe
    //! versions to read this time and handles platform differences.
    //! \param[in] timer pointer to time_t object to convert
    //! \param[in] buf pointer to user allocated struct tm to store the conversion result
    //! \param[in] file The source file name where this function is called.
    //! \param[in] function The function name where this function is called.
    //! \param[in] line The line number where this function is called.
    //! \param[in] expression The expression being evaluated.
    //! \return NULL on error or pointer to user provided \a buf
    //! \note The following errors are detected at runtime and call the installed constraint handler:
    //!
    //! - \a timer is a null pointer
    //!
    //! - \a buf is a null pointer
    M_PARAM_RO(1)
    M_PARAM_RW(2)
    struct tm* M_NULLABLE impl_safe_gmtime(const time_t* M_RESTRICT M_NONNULL timer,
                                           struct tm* M_RESTRICT M_NONNULL    buf,
                                           const char* M_NULLABLE             file,
                                           const char* M_NULLABLE             function,
                                           int                                line,
                                           const char* M_NULLABLE             expression)
        // clang-format off
        M_DIAG_ERROR(M_IS_NULL_TIMET(timer), "timer is NULL")
        M_DIAG_ERROR(M_IS_NULL_STRUCT_TM(buf), "buf is NULL")
        // clang-format on
        ;

    //! \fn struct tm* impl_safe_localtime(const time_t* M_RESTRICT timer,
    //!                                   struct tm* M_RESTRICT    buf,
    //!                                   const char*              file,
    //!                                   const char*              function,
    //!                                   int                      line,
    //!                                   const char*              expression)
    //! \brief Internal implementation of safe_localtime.
    //!
    //! Use in place of localtime(). Calls localtime_r or localtime_s internally.
    //! localtime() uses an internal buffer and is not thread safe. This uses thread safe
    //! versions to read this time and handles platform differences.
    //! \param[in] timer pointer to time_t object to convert
    //! \param[in] buf pointer to user allocated struct tm to store the conversion result
    //! \param[in] file The source file name where this function is called.
    //! \param[in] function The function name where this function is called.
    //! \param[in] line The line number where this function is called.
    //! \param[in] expression The expression being evaluated.
    //! \return NULL on error or pointer to user provided \a buf
    //! \note The following errors are detected at runtime and call the installed constraint handler:
    //!
    //! - \a timer is a null pointer
    //!
    //! - \a buf is a null pointer
    M_PARAM_RO(1)
    M_PARAM_RW(2)
    struct tm* M_NULLABLE impl_safe_localtime(const time_t* M_RESTRICT M_NONNULL timer,
                                              struct tm* M_RESTRICT M_NONNULL    buf,
                                              const char* M_NULLABLE             file,
                                              const char* M_NULLABLE             function,
                                              int                                line,
                                              const char* M_NULLABLE             expression)
        // clang-format off
        M_DIAG_ERROR(M_IS_NULL_TIMET(timer), "timer is NULL")
        M_DIAG_ERROR(M_IS_NULL_STRUCT_TM(buf), "buf is NULL")
        // clang-format on
        ;

    //! \fn errno_t impl_safe_asctime(char*            buf,
    //!                               rsize_t          bufsz,
    //!                               const struct tm* time_ptr,
    //!                               bool             ctime,
    //!                               const char*      file,
    //!                               const char*      function,
    //!                               int              line,
    //!                               const char*      expression)
    //! \brief Internal implementation of safe_asctime with flag for when used as safe_ctime
    //!
    //! Use in place of asctime(). Performs conversion using asctime_s or strftime to behave
    //! as close as possible to the same as the standard.
    //! \param[in] buf pointer to char buffer to hold output string. Must be at least 26 characters in size
    //! \param[in] bufsz size of \a buf. Must be at least 26 and less than RSIZE_MAX
    //! \param[in] time_ptr pointer to struct tm to use for conversion
    //! \param[in] ctime true when calling this from safe_ctime, false for safe_asctime
    //! \param[in] file The source file name where this function is called.
    //! \param[in] function The function name where this function is called.
    //! \param[in] line The line number where this function is called.
    //! \param[in] expression The expression being evaluated.
    //! \return error code describing the failure or zero for no error.
    //! \note The following errors are detected at runtime and call the installed constraint handler:
    //!
    //! - \a buf is a null pointer
    //!
    //! - \a time_ptr is a null pointer
    //!
    //! - \a bufsz < 26
    //!
    //! - \a bufsz > RSIZE_MAX
    //!
    //! - member of \a time_ptr is out of normal range
    M_PARAM_RW_SIZE(1, 2)
    M_PARAM_RO(3)
    errno_t impl_safe_asctime(char* M_NONNULL            buf,
                              rsize_t                    bufsz,
                              const struct tm* M_NONNULL time_ptr,
                              bool                       ctime,
                              const char* M_NULLABLE     file,
                              const char* M_NULLABLE     function,
                              int                        line,
                              const char* M_NULLABLE     expression)
        // clang-format off
        M_DIAG_ERROR(M_IS_NULL_TIME_STR(buf), "buf is NULL")
        M_DIAG_ERROR(M_IS_NULL_CONST_STRUCT_TM(time_ptr), "time_ptr is NULL")
        M_DIAG_ERROR(bufsz < 26, "bufsz less than 26")
        M_DIAG_ERROR(bufsz > RSIZE_MAX, "bufsz greater than RSIZE_MAX")
        M_DIAG_ERROR(time_ptr->tm_sec < 0, "tm_sec is negative")
#if defined(USING_C99)
        M_DIAG_ERROR(time_ptr->tm_sec > 60, "tm_sec > 60")
#else
        M_DIAG_ERROR(time_ptr->tm_sec > 61, "tm_sec > 61")
#endif
        M_DIAG_ERROR(time_ptr->tm_min < 0, "tm_min is negative")
        M_DIAG_ERROR(time_ptr->tm_min > 59, "tm_min > 59")
        M_DIAG_ERROR(time_ptr->tm_hour < 0, "tm_hour is negative")
        M_DIAG_ERROR(time_ptr->tm_hour > 23, "tm_hour > 23")
        M_DIAG_ERROR(time_ptr->tm_mday < 1, "tm_mday < 1")
        M_DIAG_ERROR(time_ptr->tm_mday > 31, "tm_mday > 31")
        M_DIAG_ERROR(time_ptr->tm_mon < 0, "tm_mon is negative")
        M_DIAG_ERROR(time_ptr->tm_mon > 11, "tm_mon > 11")
        M_DIAG_ERROR(time_ptr->tm_wday < 0, "tm_wday is negative")
        M_DIAG_ERROR(time_ptr->tm_wday > 6, "tm_wday > 6")
        M_DIAG_ERROR(time_ptr->tm_yday < 0, "tm_yday is negative")
        M_DIAG_ERROR(time_ptr->tm_yday > 365, "tm_yday > 365")
        M_DIAG_ERROR(time_ptr->tm_year < 0, "tm_year is negative")
        M_DIAG_ERROR(time_ptr->tm_year > 9999, "tm_year > 9999")
        // clang-format on
        ;

    //! \fn errno_t impl_safe_ctime(char*            buf,
    //!                               rsize_t          bufsz,
    //!                               const time_t* timer,
    //!                               const char*      file,
    //!                               const char*      function,
    //!                               int              line,
    //!                               const char*      expression)
    //! \brief Internal implementation of safe_ctime that calls impl_safe_asctime
    //!
    //! Use in place of ctime(). Performs conversion using asctime_s or strftime to behave
    //! as close as possible to the same as the standard.
    //! \param[in] buf pointer to char buffer to hold output string. Must be at least 26 characters in size
    //! \param[in] bufsz size of \a buf. Must be at least 26 and less than RSIZE_MAX
    //! \param[in] timer pointer to time_t to use for conversion
    //! \param[in] file The source file name where this function is called.
    //! \param[in] function The function name where this function is called.
    //! \param[in] line The line number where this function is called.
    //! \param[in] expression The expression being evaluated.
    //! \return error code describing the failure or zero for no error.
    //! \note The following errors are detected at runtime and call the installed constraint handler:
    //!
    //! - \a buf is a null pointer
    //!
    //! - \a timer is a null pointer
    //!
    //! - \a bufsz < 26
    //!
    //! - \a bufsz > RSIZE_MAX
    M_PARAM_RW_SIZE(1, 2)
    M_PARAM_RO(3)
    errno_t impl_safe_ctime(char* M_NONNULL         buf,
                            rsize_t                 bufsz,
                            const time_t* M_NONNULL timer,
                            const char* M_NULLABLE  file,
                            const char* M_NULLABLE  function,
                            int                     line,
                            const char* M_NULLABLE  expression)
        // clang-format off
        M_DIAG_ERROR(M_IS_NULL_TIME_STR(buf), "buf is NULL")
        M_DIAG_ERROR(M_IS_NULL_TIMET(timer), "timer is NULL")
        M_DIAG_ERROR(bufsz < 26, "bufsz less than 26")
        M_DIAG_ERROR(bufsz > RSIZE_MAX, "bufsz greater than RSIZE_MAX")
        // clang-format on
        ;

#if defined(__cplusplus)
}
#endif
