/* Copyright (c) 2012, David Goulet <dgoulet@ev0ke.net>
 *                     Jacob Appelbaum <jacob@torproject.org>
 * Copyright (c) 2012, The Tor Project, Inc. */
/* See LICENSE for licensing information */

/**
  * \file clock-darwin.c
  * \brief Contains clock primitives for Mac OS X (Tested on 10.8.2)
  **/

#include "config.h"
#include "clock.h"
#include <stdio.h>
#include <sys/time.h>
#include <time.h>

#ifdef __APPLE__
#include <mach/clock.h>
#include <mach/clock_priv.h>
#include <mach/mach.h>
#include <mach/clock_types.h>
#include <mach/mach_traps.h>
#include <mach/clock_reply.h>
#include <mach/mach_time.h>
#include <mach/mach_error.h>
#endif

#include <assert.h>

/**
 * Get current real time value and store it into time.
 *
 * @param time where the current time is stored
 * @return clock_gettime syscall return value
 */
int clock_get_real_time(struct tlsdate_time *time)
{
  /* Safety net */
  assert(time);

  kern_return_t r;
  clock_serv_t cclock;
  mach_timespec_t mts;

  r = host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock);
  if (r != KERN_SUCCESS)
  {
    fprintf(stderr, "host_get_clock_service failed!\n"); 
    return -1;
  }

  r = clock_get_time(cclock, &mts);
  if (r != KERN_SUCCESS)
  {
    fprintf(stderr, "clock_get_time failed!\n"); 
    return -1;
  }

  r = mach_port_deallocate(mach_task_self(), cclock);
  if (r != KERN_SUCCESS)
  {
    fprintf(stderr, "mach_port_deallocate failed!\n"); 
    return -1;
  }

  time->tp.tv_sec = mts.tv_sec;
  time->tp.tv_nsec = mts.tv_nsec;
  return r;
}

/**
 * Set current real time clock using time.
 *
 * @param time where the current time to set is stored
 * @return clock_settime syscall return value
 */
int clock_set_real_time(const struct tlsdate_time *time)
{
  /* Safety net */
  assert(time);

  //printf ("V: server time %u\n", (unsigned int) time->tp.tv_sec);
  int r;
  struct timeval tv = {time->tp.tv_sec, 0};

  r = settimeofday(&tv, NULL);
  if (r != 0)
  {
    fprintf(stderr, "setimeofday failed!\n"); 
    return -1;
  }

  return r;
}

/**
 * Init a tlsdate_time structure.
 *
 * @param sec is the seconds
 * @param nsec is the nanoseconds
 */
void clock_init_time(struct tlsdate_time *time, time_t sec,
                           long nsec)
{
  /* Safety net */
  assert(time);

  time->tp.tv_sec = sec;
  time->tp.tv_nsec = nsec;
}