// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

#include <stdlib.h>
#include "azure_c_shared_utility/xlogging.h"
#include "linux_time.h"

#ifdef __MACH__
#include <mach/clock.h>
#include <mach/mach.h>
#endif


#ifndef __MACH__
clockid_t time_basis = -1;
#endif

void set_time_basis(void)
{
// The time basis depends on what clock is available. Prefer CLOCK_MONOTONIC,
// then CLOCK_REALTIME, otherwise query the default pthread_condattr_t value
// and use that. Note the time basis stuff requires _POSIX_TIMERS [TMR] at a
// minimum; querying pthread_condattr_t requires _POSIX_CLOCK_SELECTION [CS].
// OSX has neither so we use a platform-specific clock.
#ifndef __MACH__
#if defined(CLOCK_MONOTONIC)
    time_basis = CLOCK_MONOTONIC;
#elif defined(CLOCK_REALTIME)
    time_basis = CLOCK_REALTIME;
#else
    pthread_condattr_t cattr;
    pthread_condattr_init(&cattr);
    pthread_condattr_getclock(&cattr, &time_basis);
    pthread_condattr_destroy(&cattr);
#endif
#endif
}

int get_time_ns(struct timespec* ts)
{
    int err;

#ifdef __MACH__
    clock_serv_t cclock;
    mach_timespec_t mts;
    err = host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock);
    if (!err)
    {
        err = clock_get_time(cclock, &mts);
        mach_port_deallocate(mach_task_self(), cclock);

        if (!err)
        {
            ts->tv_sec = mts.tv_sec;
            ts->tv_nsec = mts.tv_nsec;
        }
    }
#else
    err = clock_gettime(time_basis, ts);
#endif
    return err;
}

int64_t get_time_ms()
{
    struct timespec ts;
    if (get_time_ns(&ts) != 0)
    {
        LogError("Failed to get the current time");
        return INVALID_TIME_VALUE;
    }

    return (int64_t)((int64_t)ts.tv_sec * MILLISECONDS_IN_1_SECOND + (int64_t)ts.tv_nsec / NANOSECONDS_IN_1_MILLISECOND);
}

