/*
    time functions
    Copyright (C) 2011-2012 Ladislav Vaiz <ok1zia@nagano.cz>

    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
    version 2 as published by the Free Software Foundation.

*/

#include <libziaint.h>
#include <ztime.h>

#include <zdebug.h>

#ifndef Z_HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif

#ifdef Z_HAVE_SYS_TIMEB_H
#include <sys/timeb.h>
#endif

#include <stdio.h>

#ifdef Z_HAVE_TIME_H
#include <time.h>
#endif

#ifdef Z_HAVE_SYS_TIME_H
#include <sys/time.h>
#endif

#ifdef Z_HAVE_WINSOCK2_H
#include <winsock2.h>
#endif

#ifndef Z_HAVE_SYS_TIME_H
// todo GetSystemTimeAsFileTime on vista+
int gettimeofday(struct timeval *tv, void *dummy){
        struct _timeb t;
        _ftime_s(&t);
        tv->tv_sec = (long)t.time;
        tv->tv_usec = t.millitm * 1000;
        return 0;
}
#endif

#ifdef Z_MSC
struct tm *gmtime_r(time_t *timep, struct tm *result){
        gmtime_s(result, timep);
        return result;
}
#endif

#ifdef Z_MINGW
struct tm *gmtime_r(time_t *timep, struct tm *result){
	struct tm *tm;
	GStaticMutex gmtime_r_mutex = G_STATIC_MUTEX_INIT;

	g_static_mutex_lock(&gmtime_r_mutex);
	tm = gmtime(timep);
	memcpy(result, tm, sizeof(struct tm));
	g_static_mutex_unlock(&gmtime_r_mutex);
	return result;
}
#endif


#ifdef Z_MSC
struct tm *localtime_r(time_t *timep, struct tm *result){
    localtime_s(result, timep);
    return result;
}
#endif

#ifdef Z_MINGW
struct tm *localtime_r(time_t *timep, struct tm *result){
    static GStaticMutex localtime_r_mutex = G_STATIC_MUTEX_INIT;
    g_static_mutex_lock(&localtime_r_mutex);
    struct tm *x = localtime(timep);
    memcpy(result, x, sizeof(struct tm));
    g_static_mutex_unlock(&localtime_r_mutex);
    return result;
}
#endif						 

#ifdef Z_MSC
time_t timegm(struct tm *tm){
	time_t ret;

	tm->tm_isdst = 0;
	ret = _mkgmtime(tm);
	return ret;
}
#endif

#if defined(Z_MINGW) || defined(Z_ANDROID)
time_t timegm(struct tm *tm){
	time_t ret, tmp;
	struct tm tm2;

	tm->tm_isdst = 0;
 	tmp = mktime(tm);
	gmtime_r(&tmp, &tm2);
	tm2.tm_hour += tm->tm_hour - tm2.tm_hour;
	ret = mktime(&tm2);
	return ret;
}
#endif

#ifdef Z_MSC_MINGW
int settimeofday(const struct timeval *tv, const void *zone){
	return 0;
}
#endif



static int z_relative_time(void){
    struct timeval tv;
    gettimeofday(&tv, NULL);
    return (tv.tv_sec % 10000) * 1000 + tv.tv_usec / 1000; 
}

int ztimeout_init(int timeout_ms){
    int tmo;

    tmo = z_relative_time();
    tmo += timeout_ms;
    return tmo;
}

int ztimeout_occured(int tmo){
    int i;
    int ret = 0;

    i = z_relative_time();
    if (i < tmo && tmo >= ZTIMEOUT_MOD && i < ZTIMEOUT_MOD / 2) i += ZTIMEOUT_MOD;
    if (i >= tmo) ret = 1;
    return ret;
}

int ztimeout_diff_ms(int *tmo){
    int i, d, ret;

    i = z_relative_time();
    d = i - *tmo;
    if (i < *tmo && *tmo >= ZTIMEOUT_MOD && i < ZTIMEOUT_MOD / 2) d += ZTIMEOUT_MOD;
    ret = d;
    *tmo = i;
    return ret;
}

int ztimeout_test(int tmo, int i, int res){
    int ret = 0;

//    printf("%d %d\n", i, tmo);
    if (i < tmo && tmo >= ZTIMEOUT_MOD && i < ZTIMEOUT_MOD / 2) i += ZTIMEOUT_MOD;
//    printf("%d %d\n", i, tmo);
    if (i >= tmo) ret=1;

    if (ret == res)
        printf("OK");
    else
        printf("FAIL");
    return ret;
}

static int z_global_start;

int zst_start(void){
    return z_relative_time();
}

int zst_stop(int reltime, const char *text){
    int i, d;

    i = z_relative_time();
    d = i - reltime;
    if (i < reltime && reltime >= ZTIMEOUT_MOD && i < ZTIMEOUT_MOD / 2) d += ZTIMEOUT_MOD;

    dbg("%s:%3d.%03d\n", text, d/1000, d%1000);
    return z_relative_time();
}

void ST_START(){
	z_global_start = zst_start();
}

void ST_STOP(const char *text){
	z_global_start = zst_stop(z_global_start, text);
}

double z_difftimeval_double(struct timeval *stop, struct timeval *start){
	long sec, usec; 

    usec = stop->tv_usec - start->tv_usec;
    sec = stop->tv_sec - start->tv_sec;
    if (usec < 0){
        usec += 1000000;
        sec --;
    } 
	return sec + usec / 1000000.0;
}

char *z_format_hms(char *s, int len, time_t t){
    g_snprintf(s, len, "%02d:%02d:%02d", (int)(t / 3600), (int)((t / 60) % 60), (int)(t % 60));
    return s;
}
