/*
    Raydium - CQFD Corp.
    http://raydium.org/
    Released under both BSD license and Lesser GPL library license.
    See "license.txt" file.
*/

#ifndef DONT_INCLUDE_HEADERS
#include "index.h"
#else
#include "headers/timecall.h"
#endif

#ifdef WIN32
#define __GETTIMEOFDAY_USEC 1000
#else
#define __GETTIMEOFDAY_USEC 1000000
#endif

// needed proto
int raydium_timecall_add(void *funct, GLint hz);


void raydium_timecall_raydium(GLfloat step)
{
raydium_frame_time=step;
}

#ifdef WIN32
float raydium_timecall_internal_w32_detect_modulo(int div)
{
 LARGE_INTEGER t;
 unsigned long mx;

 QueryPerformanceFrequency(&t);
 t.QuadPart >>= div;
 if (t.HighPart>0)
    {
    //Handle to high clock frequency
    //Raydium can't handle this.
    return 0.0f;
    }
 mx=(0xFFFFFFFF / t.LowPart); // Max 32 bit time period
 return mx/60.f; // Return max time count in minutes
}


int raydium_timecall_internal_w32_divmodulo_find(void)
{
float modulo_time;
int div;

// Find a correct divide to have at least RAYDIUM_TIMECALL_W32_MODULO_MIN minutes
// before an overflow occurs.
div=-1;
do{
div++;
modulo_time=raydium_timecall_internal_w32_detect_modulo(div);
}while(modulo_time<RAYDIUM_TIMECALL_W32_MODULO_MIN);

raydium_log("timecall: win32 modulo every %.2f minutes, modulodiv is 2^%i",modulo_time,div);
return div;
}
#endif


unsigned long raydium_timecall_devrtc_clock(void)
{
#ifndef WIN32
struct timeval tv={0, 0};
fd_set readfds;
int ret;
unsigned long data,missed;

FD_ZERO(&readfds);
FD_SET(raydium_timecall_devrtc_handle, &readfds);
if( (ret=select(raydium_timecall_devrtc_handle+1, &readfds, NULL, NULL, &tv)) == -1)
    {
    raydium_log("timecall: ERROR: selecting /dev/rtc failed at runtime");
    perror("system");
    }

// IRQ fired !
if(ret>0)
 {
        if( read(raydium_timecall_devrtc_handle, &data, sizeof(unsigned long)) == -1)
        {
        raydium_log("timecall: ERROR: reading /dev/rtc failed at runtime");
        perror("system");
        }
        else
        {
        // read first 3 bytes only
        missed=(data & 0xffffff00UL)>>8;
        raydium_timecall_devrtc_clocks+=missed;
//      raydium_log("%i",raydium_timecall_devrtc_clocks);
        }
 }

return raydium_timecall_devrtc_clocks;
#else
return 0;
#endif
}

unsigned long raydium_timecall_clock_internal(void)
{
#ifndef WIN32
struct timeval tv;
#endif

#ifdef DEBUG_MOVIE
    return (float)(raydium_timecall_debug_movie*100);
#endif


if(raydium_timecall_method==RAYDIUM_TIMECALL_METHOD_CLOCK)
 {
 #ifdef WIN32
  {
// return GetTickCount();
    LARGE_INTEGER t;
    if(!QueryPerformanceCounter(&t))raydium_log("TIMECALL ERROR: Your system can not provide data(high resolution timer) to QueryPerformanceCounter function. Please tell us about this in the raydium forum.Thanks");
    t.QuadPart>>=raydium_timecall_w32_divmodulo;
// Originaly return t.LowPart<<1; Why does this is multiplied by 2 ?
    return t.LowPart;
  }
 #else
 gettimeofday(&tv,NULL);
 return (tv.tv_sec*1000000 + tv.tv_usec);
 #endif
 //return clock();
 }
//else if(raydium_timecall_method==RAYDIUM_TIMECALL_METHOD_DEVRTC)
 return raydium_timecall_devrtc_clock();
}

unsigned long raydium_timecall_clock(void)
{
long long now;

if(raydium_timecall_stopped_stamp) // time is stopped
    return raydium_timecall_stopped_stamp;

now=raydium_timecall_clock_internal();
now-=raydium_timecall_offset;

return (unsigned long)now; // should deal with modulo :)
}

signed char raydium_timecall_devrtc_rate_change(unsigned long new)
{
#ifndef APPLE
#ifndef WIN32
        if(ioctl(raydium_timecall_devrtc_handle, RTC_IRQP_SET, new)==-1)
        {
                raydium_log("timecall: ERROR: changing /dev/rtc rate to %lu Hz failed !",new);
                raydium_log("timecall: is /proc/sys/dev/rtc/max-user-freq correct for this value ?");
                perror("system");
//                exit(errno);
                return 0;
        }
        raydium_log("timecall: /dev/rtc rate changed to %lu Hz",new);
        return 1;
#else
return 0;
#endif
#endif
}

void raydium_timecall_devrtc_close(void)
{
#ifndef APPLE
#ifndef WIN32