// (c) 2000 by Zachary Booth Simpson
// This code may be freely used, modified, and distributed without any license
// as long as the original author is noted in the source file and all
// changes are made clear disclaimer using a "MODIFIED VERSION" disclaimer.
// There is NO warranty and the original author may not be held liable for any damages.
// http://www.totempole.net

/*
********************************** MODIFIED VERSION *************************************
Modification History:
01.03.2006 - Tamas Schlagl: Made it confortable with VC2005
********************************** MODIFIED VERSION *************************************
*/

#ifndef DTIME_H
#define DTIME_H

//
// INTRODUCTION
//
// This is a fully portable time class that stores time
// in seconds which as much accurarcy possible.
// The time is in seconds since 1 Jan 1970, a.k.a. Universal Coordinated Time (UTC)
//
// Under Win32 there are some major complications to getting
// a time which is both UTC and milliscond accurate.  This code uses 
// two seperate clocks which have to be synchronized during the 
// ztimeInit() call.
//
// This system has the following features:
//  1. Portable Win32, Unix
//  2. Millesecond accurace in UTC using a double type
//  3. Allows for master/slave clock synchronization.
//     (Syncronization code not included)
//  4. One call to zTimeLatchFrame() sets a set of
//     useful global variables such as master frame time, 
//     real frame time, frames per second, average frames per second, etc.
//  5. Pausing and resuming the master clock.
//  6. Creating Timer classes that act like stop watches.
//
//  UNDERSTANDING THE DIFFERENCE BETWEEN MASTER AND LOCAL TIME
// 
//    This system makes a distinction between master and local time.
//    The master clock incorporates two possible corrections.  One is
//  a server difference which might be set by a time syncronization
//  routine (not included).  The other is allows the time on the local
//  machine to be paused.  These two corrections are mututally exclusive.
//    The local clock incorporates neither correction and always
//  represents the real time coming of the clock.  It is not pausable.


struct DTime {
	double dTime;

	DTime() { dTime = 0.0; }
	DTime( double _dTime ) { dTime = _dTime; }
	operator double() { return dTime; }

	// Not all machines use the same floating point encodings
	// so it may sometimes be necessary for portability to
	// convert a full time to fixed point before transmitting
	unsigned long getFixedPoint() {
		unsigned long t = (unsigned long)dTime;
		return( unsigned long((dTime-(double)t)*2.0*2.0*2.0) | (t<<3) );
	}
	
	void setFixedPoint(unsigned long t) {
		dTime = double(t>>3) + double(t&0x7)/8.0;
	}
};

// zTimeLatchFrame Global Variables
//------------------------------------------------------------------------

// All of the following global variables are configured
// during each call to zTimeLatchFrame()

extern int dTimeFrameCount;
	// Incremented each time setFrameTime is called 
	// which should be once per frame

extern DTime dTimeMasterFrameTime;
	// The time during the last call to zTimeSetFrameTime(),
	// corrected to master time if appropriate.

extern DTime dTimeLocalFrameTime;
	// The time during the last call to zTimeSetFrameTime(),
	// always uses the local time, regardless of the syncronization
	// settings and pausing.

extern DTime dTimeMasterDT;
	// Delta time on the last frame.

extern float dTimeMasterDTF;
	// Delta time in float on the last frame.

extern DTime dTimeLocalDT;
	// Delta time on the last frame.

extern float dTimeLocalDTF;
	// Delta time in float on the last frame.

extern double dTimeFPS;
	// The last calculated frames per second.
	// Always based on local time as opposed to the master time

#ifndef DTIME_FRAMES_TO_AVG
	#define DTIME_FRAMES_TO_AVG 10
#endif
extern double dTimeAvgFPS;
	// The running average of the last DTIME_FRAMES_TO_AVG 
	// Always based on local time as opposed to the master time

// Misc. Globals
//------------------------------------------------------------------------

extern DTime dTimeStart;
	// Time that the dTime system was initialized in local time

extern int dTimeMasterDiffMils;
	// Millisecond difference between the master and the local time
	// This should only set by calls to zTimeSetMasterDiff( int mils );

// Interface Functions
//------------------------------------------------------------------------

DTime dTimeGetMaster();
	// The time at this instant in the master's clock.  This time
	// may be paused using the dTimePause() and dTimeResume()
	// functions, but only if there is no dTimeMasterDiffMils
	// meaning that this computer IS the master

DTime dTimeGetLocal();
	// The time at this instant in the local clock.  Never paused nor corrected

void dTimeInit();
	// This function must be called to initialize the dTime system.
	// Under Windows it does evil calibrartion for up to a second to
	// synchronize the UTC and millisecond timers.

void dTimeLatchFrame();
	// Call this once a frame to set the globals "dTimeMasterFrameTime"
	// and "zTimeRealFrameTime"
	// The two globals should be used in most time related code
	// to avoid unnecessary calls to kernel timing code

void dTimeProcessSleepSecs( int secs );
	// Platform independent sleep secs

void dTimeProcessSleepMils( int mils );
	// Platform independent sleep milliseconds

void dTimePause();
	// Stops time and uses an accumulator to collect
	// the "stopped time".  Useful for non-synchronized modes.
	// This only modifies the "master" time calls such as
	// zTimeGetMasterTime() and dTimeMasterFrameTime

void dTimeResume();
	// Resumes time

int  dTimeIsPaused();
	// Query time status of paused time

void dTimeResetPaused();
	// Clears the time stop accumulator.  YOU MUST CALL THIS
	// before trying to use time synchronization!
	// Time sync will assert that the stopTimeAccumulator is 0

void dTimeSetMasterDiff( int mils );
	// A syncronization service would call this function
	// to inform the dTime system that there is a mils
	// time difference between master and slave.

// Timer Class
//------------------------------------------------------------------------

struct MasterTimer {
	// A Timer class based on the master, corrected and pausable time.

	DTime timeDone;

	void start(DTime    countDown) { timeDone = dTimeMasterFrameTime + countDown; }
	void start(float    countDown) { timeDone = dTimeMasterFrameTime + (double)countDown; }
	void start(double   countDown) { timeDone = dTimeMasterFrameTime + countDown; }
	void start(int      countDown) { timeDone = dTimeMasterFrameTime + (double)countDown; }

	MasterTimer() { stop(); }
	MasterTimer(DTime  countDown) { start(countDown); }
	MasterTimer(float  countDown) { start(countDown); }
	MasterTimer(double countDown) { start(countDown); }
	MasterTimer(int    countDown) { start(countDown); }

	int isDone() { return timeDone > 0.0 && dTimeMasterFrameTime >= timeDone; }
	int isRunning() { return timeDone != 0.0; }
	void stop() { timeDone = 0.0; }
	DTime remains() { if( timeDone == 0.0 ) return 0.0; else return( timeDone - dTimeMasterFrameTime ); }
};


struct LocalTimer {
	// A Timer class based on the local, uncorrected time.

	DTime timeDone;

	void start(DTime    countDown) { timeDone = dTimeGetLocal() + countDown; }
	void start(float    countDown) { timeDone = dTimeGetLocal() + (double)countDown; }
	void start(double   countDown) { timeDone = dTimeGetLocal() + countDown; }
	void start(int      countDown) { timeDone = dTimeGetLocal() + (double)countDown; }

	LocalTimer() { stop(); }
	LocalTimer(DTime    countDown) { start(countDown); }
	LocalTimer(float    countDown) { start(countDown); }
	LocalTimer(double   countDown) { start(countDown); }
	LocalTimer(int      countDown) { start(countDown); }

	int isDone() { return timeDone > 0.0 && dTimeGetLocal() >= timeDone; }
	int isRunning() { return timeDone != 0.0; }
	void stop() { timeDone = 0.0; }
	DTime remains() { if( timeDone == 0.0 ) return 0.0; else return( timeDone - dTimeGetLocal() ); }
};

#endif
