/*******************************************************************************/
/* Permission is hereby granted, free of charge, to any person or organization */
/* obtaining a copy of the software and accompanying documentation covered by  */
/* this license (the "Software") to use, reproduce, display, distribute,       */
/* execute, and transmit the Software, and to prepare derivative works of the  */
/* Software, and to permit third-parties to whom the Software is furnished to  */
/* do so, all subject to the following:                                        */
/*                                                                             */
/* The copyright notices in the Software and this entire statement, including  */
/* the above license grant, this restriction and the following disclaimer,     */
/* must be included in all copies of the Software, in whole or in part, and    */
/* all derivative works of the Software, unless such copies or derivative      */
/* works are solely in the form of machine-executable object code generated by */
/* a source language processor.                                                */
/*                                                                             */
/* 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, TITLE AND NON-INFRINGEMENT. IN NO EVENT   */
/* SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE   */
/* FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, */
/* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER */
/* DEALINGS IN THE SOFTWARE.                                                   */
/*******************************************************************************/

#include <config.h>

#include <lfp/time.h>
#include <lfp/errno.h>
#include <lfp/unistd.h>

#if defined(__APPLE__)
# include <mach/mach.h>
# include <mach/clock.h>
#endif

#include "utils.h"

int lfp_clock_getres(lfp_clockid_t clk_id, struct timespec *res)
{
#if defined(_POSIX_MONOTONIC_CLOCK) && _POSIX_MONOTONIC_CLOCK >= 0
    return clock_getres((clockid_t)clk_id & 0xFFFFFFFF, res);
#else
    switch (clk_id) {
    case CLOCK_REALTIME:
    case CLOCK_MONOTONIC:
        // FIXME: it's probably safe to assume that it's 10ms
        //        or lower(OS scheduler running at 100Hz or more)
        res->tv_sec = 0;
        res->tv_nsec = 10^7;
        return 0;
    default:
        SYSERR(EINVAL);
    }
#endif
}

#if !defined(_POSIX_TIMERS) || _POSIX_TIMERS < 0
static inline
int _lfp_clock_gettime_realtime(struct timespec *tp)
{
    struct timeval tv;
    if (gettimeofday(&tv, NULL) < 0) { 
        return -1;
    } else {
        _lfp_timeval_to_timespec(&tv, tp);
        return 0;
    }
}
#endif

#if !defined(_POSIX_MONOTONIC_CLOCK) || _POSIX_MONOTONIC_CLOCK < 0
static inline
int _lfp_clock_gettime_monotonic(struct timespec *tp)
{
# if defined(__APPLE__)
    clock_serv_t clk_serv;
    mach_timespec_t mtp;
    _lfp_timespec_to_mach_timespec_t(tp, &mtp);

    SYSGUARD(host_get_clock_service(mach_host_self(), 0, &clk_serv));
    SYSGUARD(clock_get_time(clk_serv, &mtp));
    return 0;
# else
    SYSERR(EINVAL);
# endif
}
#endif

int lfp_clock_gettime(lfp_clockid_t clk_id, struct timespec *tp)
{
#if defined(_POSIX_MONOTONIC_CLOCK) && _POSIX_MONOTONIC_CLOCK >= 0
    return clock_gettime((clockid_t)clk_id & 0xFFFFFFFF, tp);
#else
    switch (clk_id) {
    case CLOCK_REALTIME:
        return _lfp_clock_gettime_realtime(tp);
    case CLOCK_MONOTONIC:
        return _lfp_clock_gettime_monotonic(tp);
    default:
        SYSERR(EINVAL);
    }
#endif
}

int lfp_clock_settime(lfp_clockid_t clk_id, struct timespec *tp)
{
#if defined(_POSIX_MONOTONIC_CLOCK) && _POSIX_MONOTONIC_CLOCK >= 0
    return clock_settime((clockid_t)clk_id & 0xFFFFFFFF, tp);
#else
    int ret;
    struct timeval tv;
    _lfp_timespec_to_timeval(tp, &tv);

    switch (clk_id) {
    case CLOCK_REALTIME:
        ret = settimeofday(&tv, NULL);
        if (ret < 0) { return -1; }
        return 0;
    case CLOCK_MONOTONIC:
        SYSERR(EPERM);
    default:
        SYSERR(EINVAL);
    }
#endif
}
