/*****************************************************************************\
  vrpn_Clock.h
  --
  Description : Note: for both types of clocks (server and client), users
                      need only to pass in the station which runs the 
		      clock server.
		      The vrpn_Clock class takes care of the actual clock
		      device name (since there is only one clock device).
		      (i.e., you just say vrpn_Clock("server_name") or
		      vrpn_Clock_Remote("server_name"), rather than
                      vrpn_Clock_Remote("myclockname@server_name");
  ----------------------------------------------------------------------------
  Author: weberh
  Created: Sat Dec 13 11:21:15 1997
  Revised: Mon Mar 23 11:34:26 1998 by weberh
\*****************************************************************************/
#ifndef _VRPN_CLOCK_H_
#define _VRPN_CLOCK_H_

#include "vrpn_Connection.h"
#include "vrpn_BaseClass.h"

class vrpn_Connection;

#define CLOCK_VERSION 0x10

// Base class for clock servers.  Definition of remote/client 
// clock class for the user is at the end.
class vrpn_Clock : public vrpn_BaseClass {
public:
  vrpn_Clock(const char *name, vrpn_Connection *c = NULL);
  
protected:
  vrpn_int32 queryMsg_id;	// ID of clockQuery message to connection
  vrpn_int32 replyMsg_id;	// ID of clockReply message to connection

  virtual int register_senders(void);
  virtual int register_types(void);
  virtual int encode_to(char *buf, const struct timeval& tvSRep, 
			const struct timeval& tvCReq, 
			vrpn_int32 cChars, const char* pch);
};


class vrpn_Clock_Server : public vrpn_Clock {
public:
  vrpn_Clock_Server(vrpn_Connection *c);
  virtual ~vrpn_Clock_Server();

  // Called once through each main loop iteration to handle
  // clock updates.
  // Report changes to connection
  virtual void mainloop ();
  static int clockQueryHandler( void *userdata, vrpn_HANDLERPARAM p );
};


//----------------------------------------------------------
// ************* Users deal with the following *************

// User routine to handle messages regarding the clock offset between 
// the server host and the client host.
// Right now this is called a short while after the mainloop is
// first called.  In a future version this might regularly re-sync the
// host and client whenever a new clock_sync_handler is registered.
// It is best to call this before the server is sending all kinds of
// other messages.
// The clock sync procedure takes 1 second to run.

typedef	struct {
	struct timeval	msg_time;	// Local time of this sync message
	struct timeval  tvClockOffset;  // Local time - remote time in msecs
	struct timeval  tvHalfRoundTrip;  // half of the roundtrip time
                                          // for the roundtrip used to calc offset
} vrpn_CLOCKCB;

typedef void (*vrpn_CLOCKSYNCHANDLER)(void *userdata,
				      const vrpn_CLOCKCB& info);

// Connect to a clock server that is on the other end of a connection
// and figure out the offset between the local and remote clocks
// This is the type of clock that user code will deal with.
// You need only supply the server name (no device name) and the 
// desired frequency (in hz) of clock sync updates.  IF THE FREQ IS
// NEGATIVE, THEN YOU WILL GET NO AUTOMATIC SYNCS. At any time you
// can get a very accurate sync by calling fullSync();

// special message identifiers for remote clock syncs
#define VRPN_CLOCK_FULL_SYNC 1
#define VRPN_CLOCK_QUICK_SYNC 2

class vrpn_Clock_Remote: public vrpn_Clock {
  public:

    // The name of the station which runs the clock server to connect to
    // (from sdi_devices), and the frequency at which to do quick re-syncs 
    // (1 hz by default).  If the user specifies a freq < 0, then there will
    // be no quick syncs -- they will have to request syncs with fullSync().
    // The final arg is the number of reports over which the user wants to
    // maintain a window from which the offset routine will choose the min
    // round trip report and use the offset from that trip.
    // A high setting (e.g., 40) works well for arrangements with little drift
    // while a low setting (e.g., 3) works well when drift is present.
    // See cMaxQuickRecords below for more detail.

    vrpn_Clock_Remote (const char * name, vrpn_float64 dFreq = 1,
		       vrpn_int32 cOffsetWindow = 3);
    virtual ~vrpn_Clock_Remote (void);


    // MANIPULATORS

    // This routine calls does the sync and calls the mainloop of the 
    // connection it's on.  The clock does not call client_mainloop()
    // or server_mainloop, because it does not need heartbeats or
    // initialization calls.
    virtual void mainloop (const struct timeval *timeout);
    virtual void mainloop (void) { mainloop(NULL); };

    // (un)Register a callback to handle a clock sync
    virtual int register_clock_sync_handler(void *userdata,
					    vrpn_CLOCKSYNCHANDLER handler);
    virtual int unregister_clock_sync_handler(void *userdata,
					      vrpn_CLOCKSYNCHANDLER handler);
    // request a high accuracy sync (and turn off quick syncs)
    void fullSync (void);


    // ACCESSORS

    // Returns the most recent RTT estimate.  TCH April 99
    // Returns 0 if the first RTT estimate is not yet completed
    // or if no quick syncs are being done.
    struct timeval currentRTT (void) const;

  protected:

    vrpn_int32 clockClient_id;             // vrpn id for this client

    // unique id for this particular client (to disambiguate replies)
    vrpn_int32 lUniqueID;

    // vars for stats on clock for quick sync
    int fDoQuickSyncs;
    int cQuickBounces;

    // CC does not like this, so ...
    // const int cMaxQuickDiffs=5
    // This is the number of reports the clock keeps track of and
    // chooses the offset from the min round trip from these.
    // This is a balance between drift compensation and
    // accuracy.  In the absence of drift, this should
    // be very large.  In the presence of drift it needs
    // to be very small. 5 seems to work well -- this means
    // that drift will accumulate for at most 5*1/freq secs,
    // and that a few consecutive long roundtrips will not
    // reduce the accuracy of the offset.
    int cMaxQuickRecords;
    struct timeval *rgtvHalfRoundTrip;
    struct timeval *rgtvClockOffset;
    int irgtvQuick;

    vrpn_float64 dQuickIntervalMsecs;
    struct timeval tvQuickLastSync;

    // vars for stats on clock for full sync
    int fDoFullSync;
    int cBounces;

#ifdef USE_REGRESSION
    vrpn_float64 *rgdOffsets;
    vrpn_float64 *rgdTimes;
#endif

    struct timeval tvMinHalfRoundTrip;

    // offset to report to user
    struct timeval tvFullClockOffset;

    // vars for user specified handlers
    typedef struct _vrpn_CLOCKSYNCLIST {
      void			*userdata;
      vrpn_CLOCKSYNCHANDLER	handler;
      struct _vrpn_CLOCKSYNCLIST	*next;
    } vrpn_CLOCKSYNCLIST;

    vrpn_CLOCKSYNCLIST	*change_list;

    static int quickSyncClockServerReplyHandler(void *userdata, 
					        vrpn_HANDLERPARAM p);
    static int fullSyncClockServerReplyHandler(void *userdata, 
					       vrpn_HANDLERPARAM p);
};

#endif // ifndef _VRPN_CLOCK_H_
