#ifndef VRPN_CONNECTION_H
#define VRPN_CONNECTION_H
#include <stdio.h>  // for FILE

#ifndef VRPN_SHARED_H
#include "vrpn_Shared.h"
#endif

// NOTE: most users will want to use the vrpn_Synchronized_Connection
// class rather than the default connection class (either will work,
// a regular connection just has 0 as the client-server time offset)

class vrpn_File_Connection;  // Forward declaration for get_File_Connection()

/// This structure is what is passed to a vrpn_Connection message callback.
/// It is used by objects, but not normally by user code.
struct vrpn_HANDLERPARAM {
	vrpn_int32	type;
	vrpn_int32	sender;
	struct timeval	msg_time;
	vrpn_int32	payload_len;
	const char	*buffer;
};
/// Type of a message handler for vrpn_Connection messages.
typedef	int (*vrpn_MESSAGEHANDLER)(void *userdata, vrpn_HANDLERPARAM p);
/// Type of handler for filters on logfiles is the same as connection handler
typedef	vrpn_MESSAGEHANDLER vrpn_LOGFILTER;

/// VRPN buffers are aligned on 8 byte boundaries
const	unsigned    vrpn_ALIGN = 8;

// Types now have their storage dynamically allocated, so we can afford
// to have large tables.  We need at least 150-200 for the microscope
// project as of Jan 98, and will eventually need two to three times that
// number.
const	int   vrpn_CONNECTION_MAX_SENDERS = 2000;
const	int   vrpn_CONNECTION_MAX_TYPES = 2000;

// vrpn_ANY_SENDER can be used to register callbacks on a given message
// type from any sender.

const	int vrpn_ANY_SENDER = -1;

// vrpn_ANY_TYPE can be used to register callbacks for any USER type of
// message from a given sender.  System messages are handled separately.

const	int vrpn_ANY_TYPE = -1;

// Buffer lengths for TCP and UDP.
// TCP is an arbitrary number that can be changed by the user
// using vrpn_Connection::set_tcp_outbuf_size().
// UDP is set based on Ethernet maximum transmission size;  trying
// to send a message via UDP which is longer than the MTU of any
// intervening physical network may cause untraceable failures,
// so for now we do not expose any way to change the UDP output
// buffer size.  (MTU = 1500 bytes, - 28 bytes of IP+UDP header)

const	int vrpn_CONNECTION_TCP_BUFLEN = 64000;
const	int vrpn_CONNECTION_UDP_BUFLEN = 1472;

/// Number of endpoints that a server connection can have.  Arbitrary limit.

const	int vrpn_MAX_ENDPOINTS = 256;

// System message types

const	vrpn_int32  vrpn_CONNECTION_SENDER_DESCRIPTION	= (-1);
const	vrpn_int32  vrpn_CONNECTION_TYPE_DESCRIPTION	= (-2);
const	vrpn_int32  vrpn_CONNECTION_UDP_DESCRIPTION	= (-3);
const	vrpn_int32  vrpn_CONNECTION_LOG_DESCRIPTION	= (-4);
const	vrpn_int32  vrpn_CONNECTION_DISCONNECT_MESSAGE	= (-5);

// Classes of service for messages, specify multiple by ORing them together
// Priority of satisfying these should go from the top down (RELIABLE will
// override all others).
// Most of these flags may be ignored, but RELIABLE is guaranteed
// to be available.

const	vrpn_uint32 vrpn_CONNECTION_RELIABLE		= (1<<0);
const	vrpn_uint32 vrpn_CONNECTION_FIXED_LATENCY	= (1<<1);
const	vrpn_uint32 vrpn_CONNECTION_LOW_LATENCY		= (1<<2);
const	vrpn_uint32 vrpn_CONNECTION_FIXED_THROUGHPUT	= (1<<3);
const	vrpn_uint32 vrpn_CONNECTION_HIGH_THROUGHPUT	= (1<<4);

// What to log
const	long	vrpn_LOG_NONE		= (0);
const	long	vrpn_LOG_INCOMING	= (1<<0);
const	long	vrpn_LOG_OUTGOING	= (1<<1);

// If defined, will filter out messages:  if the remote side hasn't
// registered a type, messages of that type won't be sent over the
// link.  WARNING:  auto-type-registration breaks this.
//#define vrpn_FILTER_MESSAGES

// These are the strings that define the system-generated message
// types that tell when connections are received and dropped.
extern	const char *vrpn_got_first_connection;
extern	const char *vrpn_got_connection;
extern	const char *vrpn_dropped_connection;
extern	const char *vrpn_dropped_last_connection;

// vrpn_CONTROL is the sender used for notification messages sent to the user
// from the local VRPN implementation (got_first_connection, etc.)
// and for control messages sent by auxiliary services.  (Such as
// class vrpn_Controller, which will be introduced in a future revision.)

extern	const char *vrpn_CONTROL;

/// Length of names within VRPN
typedef char cName [100];

// Placed here so vrpn_FileConnection can use it too.
struct vrpn_LOGLIST {
  vrpn_HANDLERPARAM data;
  vrpn_LOGLIST * next;
  vrpn_LOGLIST * prev;
};

// HACK
// These structs must be declared outside of vrpn_Connection
// (although we'd like to make them protected/private members)
// because aCC on PixelFlow doesn't handle nested classes correctly.

// Description of a callback entry for a user type.
struct vrpnMsgCallbackEntry {
  vrpn_MESSAGEHANDLER	handler;	// Routine to call
  void			* userdata;	// Passed along
  vrpn_int32		sender;		// Only if from sender
  vrpnMsgCallbackEntry	* next;		// Next handler
};

struct vrpnLogFilterEntry {
  vrpn_LOGFILTER filter;   // routine to call
  void * userdata;         // passed along
  vrpnLogFilterEntry * next;
};

class vrpn_Connection;
class vrpn_Log;
class vrpn_TranslationTable;
class vrpn_TypeDispatcher;

// Encapsulation of the data and methods for a single connection
// to take care of one part of many clients talking to a single server.
// This will only be used from within the vrpn_Connection class;  it should
// not be instantiated by users or devices.
// Should not be visible!

class vrpn_Endpoint {

  public:

    vrpn_Endpoint (vrpn_TypeDispatcher * dispatcher,
                   vrpn_int32 * connectedEndpointCounter);
    virtual ~vrpn_Endpoint (void);

    // ACCESSORS

    /// True if the UDP outbound is open, False if not.
    vrpn_bool outbound_udp_open (void) const;

    /// Returns the local mapping for the remote type (-1 if none).
    int local_type_id (vrpn_int32 remote_type) const;

    /// Returns the local mapping for the remote sender (-1 if none).
    int local_sender_id (vrpn_int32 remote_sender) const;

    vrpn_int32 tcp_outbuf_size (void) const;
    vrpn_int32 udp_outbuf_size (void) const;

    // Has a clock sync occured yet (slight prob of false negative, but 
    // only for a brief period)
    vrpn_bool clockSynced (void) const;

    vrpn_bool doing_okay (void) const;

    // MANIPULATORS

    void init (void);

    int mainloop (timeval * timeout, const char * NICaddress);

    // Clear out the remote mapping list. This is done when a
    // connection is dropped and we want to try and re-establish
    // it.
    void clear_other_senders_and_types (void);

    // A new local sender or type has been established; set
    // the local type for it if the other side has declared it.
    // Return 1 if the other side has one, 0 if not.
    int newLocalSender (const char * name, vrpn_int32 which);
    int newLocalType (const char * name, vrpn_int32 which);

    // Adds a new remote type/sender and returns its index.
    // Returns -1 on error.
    int newRemoteType (cName type_name, vrpn_int32 remote_id,
                       vrpn_int32 local_id);
    int newRemoteSender (cName sender_name, vrpn_int32 remote_id,
                         vrpn_int32 local_id);

    // Pack a message that will be sent the next time mainloop() is called.
    // Turn off the RELIABLE flag if you want low-latency (UDP) send.
    int pack_message (vrpn_uint32 len, struct timeval time,
            vrpn_int32 type, vrpn_int32 sender, const char * buffer,
            vrpn_uint32 class_of_service);

    // send pending report, clear the buffer.
    // This function was protected, now is public, so we can use it
    // to send out intermediate results without calling mainloop
    virtual int send_pending_reports (void);

    int pack_udp_description (int portno);
    int pack_log_description (void);
      ///< Packs the log description set by setup_new_connection().

    int handle_tcp_messages (const timeval * timeout);
    int handle_udp_messages (const timeval * timeout);


    int connect_tcp_to (const char * msg);
    int connect_tcp_to (const char * addr, int port);
      ///< Connects d_tcpSocket to the specified address (msg = "IP port");
      ///< sets status to COOKIE_PENDING;  returns 0 on success, -1 on failure
    int connect_udp_to (const char * addr, int port);
      ///< Connects d_udpSocket to the specified address and port;
      ///< returns 0 on success, sets status to BROKEN and returns -1
      ///< on failure.

    vrpn_int32 set_tcp_outbuf_size (vrpn_int32 bytecount);

    int setup_new_connection (void);
      ///< Sends the magic cookie and other information to its
      ///< peer.  It is called by both the client and server setup routines.

    void poll_for_cookie (const timeval * timeout = NULL);

    int finish_new_connection_setup (void);

    void drop_connection (void);
      ///< Should only be called by vrpn_Connection::drop_connection(),
      ///< since there's more housecleaning to do at that level.  I suppose
      ///< that argues against separating this function out.

    void clearBuffers (void);
      ///< Empties out the TCP and UDP send buffers.
      ///< Needed by vrpn_FileConnection to get at {udp,tcp}NumOut.

    void setNICaddress (const char *);

    int pack_sender_description (vrpn_int32 which);
      ///< Packs a sender description over our socket.

    int pack_type_description (vrpn_int32 which);
      ///< Packs a type description.

    int setControlMsgTimeOffset(const timeval * offset);
      ///< Offset endpoint should apply to timestamps of system messages,
      ///< allows us to construct a log with the timestamps we want.

    int status;

//XXX These should be protected; making them so will lead to making
//    the code split the functions between Endpoint and Connection
//    protected:

    SOCKET d_tcpSocket;

    // This section deals with when a client connection is trying to
    // establish (or re-establish) a connection with its server. It
    // keeps track of what we need to know to make this happen.

    SOCKET d_tcpListenSocket;
    int	d_tcpListenPort;
      ///< Socket and port that the client listens on
      ///< when lobbing datagrams at the server and
      ///< waiting for it to call back.


    char * remote_machine_name;	// Machine to call
    int	remote_UDP_port;	// UDP port on remote machine
    timeval last_UDP_lob;	// When the last lob occured
    long d_remoteLogMode;	// Mode to put the remote logging in
    char * d_remoteInLogName;	// Name of the remote log file
    char * d_remoteOutLogName;	// Name of the remote log file

    // offset of clocks on connected machines -- local - remote
    // (this should really not be here, it should be in adjusted time
    // connection, but this makes it a lot easier
    timeval tvClockOffset;

    // Name of the remote host we are connected to.  This is kept for
    // informational purposes.  It is printed by the ceiling server,
    // for example.
    char rhostname [150];

	vrpn_bool	d_tcp_only;
	  ///< For connections made through firewalls or NAT with the
	  ///< tcp: URL, we do not want to allow the endpoints on either
	  ///< end to open a UDP link to their counterparts.  If this is
	  ///< the case, then this flag should be set to true.

    // Logging - TCH 19 April 00;  changed into two logs 16 Feb 01

    vrpn_Log * d_inLog;
    vrpn_Log * d_outLog;

    void setLogNames (const char * inName, const char * outName);
    int openLogs (void);

    // Routines that handle system messages
    // Visible so that vrpn_Connection can pass them to the Dispatcher
    static int handle_sender_message (void * userdata, vrpn_HANDLERPARAM p);
    static int handle_type_message (void * userdata, vrpn_HANDLERPARAM p);
	
	// Routines to inform the endpoing of the connection of 
	// which it is a part.
	void setConnection( vrpn_Connection* conn ) {  d_parent = conn;  }
	vrpn_Connection* getConnection( ) {  return d_parent;  }

  protected:

    int getOneTCPMessage (int fd, char * buf, int buflen);
    int getOneUDPMessage (char * buf, int buflen);
      ///< These two functions are ALMOST identical, but hard to combine.
    virtual int dispatch (vrpn_int32 type, vrpn_int32 sender,
                  timeval time, vrpn_uint32 payload_len,
                  char * bufptr);

    int tryToMarshall (char * outbuf, vrpn_int32 &buflen, vrpn_int32 &numOut,
                       vrpn_uint32 len, timeval time,
                       vrpn_int32 type, vrpn_int32 sender,
                       const char * buffer, vrpn_uint32 classOfService);
      ///< Calls marshall_message();  if that fails, calls
      ///< send_pending_reports() and then marshalls again.
      ///< Returns the number of characters successfully marshalled.

    int marshall_message (char * outbuf,vrpn_uint32 outbuf_size,
                          vrpn_uint32 initial_out,
                          vrpn_uint32 len, struct timeval time,
                          vrpn_int32 type, vrpn_int32 sender,
                          const char * buffer,
                          vrpn_uint32 sequenceNumber);

    SOCKET d_udpOutboundSocket;
    SOCKET d_udpInboundSocket;
      ///< Inbound unreliable messages come here.
      ///< Need one for each due to different
      ///< clock synchronization for each; we
      ///< need to know which server each message is from.

    char * d_tcpOutbuf;
    char * d_udpOutbuf;
    vrpn_int32 d_tcpBuflen;
    vrpn_int32 d_udpBuflen;
    vrpn_int32 d_tcpNumOut;
    vrpn_int32 d_udpNumOut;

    vrpn_int32 d_tcpSequenceNumber;
    vrpn_int32 d_udpSequenceNumber;

    vrpn_float64 d_tcpAlignedInbuf
         [vrpn_CONNECTION_TCP_BUFLEN / sizeof(vrpn_float64) + 1];
    vrpn_float64 d_udpAlignedInbuf
         [vrpn_CONNECTION_UDP_BUFLEN / sizeof(vrpn_float64) + 1];
    char * d_tcpInbuf;
    char * d_udpInbuf;

    char * d_NICaddress;

    timeval d_controlMsgTimeOffset;

    // The senders and types we know about that have been described by
    // the other end of the connection.  Also, record the local mapping
    // for ones that have been described with the same name locally.
    // The arrays are indexed by the ID from the other side, and store
    // the name and local ID that corresponds to each.

    vrpn_TranslationTable * d_senders;
    vrpn_TranslationTable * d_types;

    vrpn_TypeDispatcher * d_dispatcher;
    vrpn_int32 * d_connectionCounter;

    vrpn_Connection * d_parent;
};


class vrpn_Connection {

  //friend class vrpn_Endpoint;

  public:

	// Users should not create vrpn_Connection directly -- use 
	// vrpn_Synchronized_Connection (for servers) or 
	// vrpn_get_connection_by_name (for clients).  This is here
	// only for those who KNOW that they want to create
	// non-time-synchronized connections for servers.
	vrpn_Connection (unsigned short listen_port_no =
		         vrpn_DEFAULT_LISTEN_PORT_NO,
                         const char * local_in_logfile_name = NULL,
                         const char * local_out_logfile_name = NULL,
                         const char * NIC_IPaddress = NULL,
                         vrpn_Endpoint * (* epa) (vrpn_Connection *,
                           vrpn_int32 *) = allocateEndpoint);

	virtual ~vrpn_Connection (void);

	// Returns 1 if the connection is okay, 0 if not
	vrpn_bool doing_okay (void) const;
	virtual	vrpn_bool connected (void) const;

	// This is similar to check connection except that it can be
	// used to receive requests from before a server starts up
	virtual int connect_to_client (const char * machine, int port);

	// Returns the name of the service that the connection was first
	// constructed to talk to, or NULL if it was built as a server.
	//inline const char * name (void) const { return my_name; }

	// Call each time through program main loop to handle receiving any
	// incoming messages and sending any packed messages.
	// Returns -1 when connection dropped due to error, 0 otherwise.
	// (only returns -1 once per connection drop).
        // Optional argument is TOTAL time to block on select() calls;
        // there may be multiple calls to select() per call to mainloop(),
        // and this timeout will be divided evenly between them.
	virtual int mainloop (const struct timeval * timeout = NULL);

	// Get a token to use for the string name of the sender or type.
	// Remember to check for -1 meaning failure.
	virtual vrpn_int32 register_sender (const char * name);
	virtual vrpn_int32 register_message_type (const char * name);

	// Set up (or remove) a handler for a message of a given type.
	// Optionally, specify which sender to handle messages from.
	// Handlers will be called during mainloop().
	// Your handler should return 0 or a communication error is assumed
	// and the connection will be shut down.
	virtual int register_handler(vrpn_int32 type,
		vrpn_MESSAGEHANDLER handler, void *userdata,
		vrpn_int32 sender = vrpn_ANY_SENDER);
	virtual	int unregister_handler(vrpn_int32 type,
		vrpn_MESSAGEHANDLER handler, void *userdata,
		vrpn_int32 sender = vrpn_ANY_SENDER);

	// Pack a message that will be sent the next time mainloop() is called.
	// Turn off the RELIABLE flag if you want low-latency (UDP) send.
	virtual int pack_message(vrpn_uint32 len, struct timeval time,
		vrpn_int32 type, vrpn_int32 sender, const char * buffer,
		vrpn_uint32 class_of_service);

	// send pending report, clear the buffer.
	// This function was protected, now is public, so we can use it
	// to send out intermediate results without calling mainloop
	virtual int send_pending_reports (void);

	// Returns the time since the connection opened.
	// Some subclasses may redefine time.
	virtual int time_since_connection_open
	                        (struct timeval * elapsed_time);

	// Returns the name of the specified sender/type, or NULL
	// if the parameter is invalid.  Only works for user
	// messages (type >= 0).
	virtual const char * sender_name (vrpn_int32 sender);
	virtual const char * message_type_name (vrpn_int32 type);

        // Sets up a filter function for logging.
        // Any user message to be logged is first passed to this function,
        // and will only be logged if the function returns zero (XXX).
        // NOTE:  this only affects local logging - remote logging
        // is unfiltered!  Only user messages are filtered;  all system
        // messages are logged.
        // Returns nonzero on failure.
        virtual int register_log_filter (vrpn_LOGFILTER filter,
                                         void * userdata);

    int setControlMsgTimeOffset(const timeval * offset);
      ///< Offset endpoint should apply to timestamps of system messages,
      ///< allows us to construct a log with the timestamps we want.

        // Save any messages on any endpoints which have been logged so far.
        virtual int save_log_so_far();


    // vrpn_File_Connection implements this as "return this" so it
    // can be used to detect a File_Connection and get the pointer for it
    virtual vrpn_File_Connection * get_File_Connection (void);

  protected:

    static vrpn_Endpoint * allocateEndpoint (vrpn_Connection *,
                                             vrpn_int32 * connectedEC);

      ///< Redefining this and passing it to constructors
      ///< allows a subclass to use a different subclass of Endpoint.
      ///< It should do NOTHING but return an endpoint
      ///< of the appropriate class;  it may not access subclass data,
      ///< since it'll be called from a constructor

      //   Create a connection -  if server_name is not a file: name,
      // makes an SDI-like connection to the named remote server
      // (otherwise functions as a non-networked messaging hub).
      // Port less than zero forces default.
      //   Currently, server_name is an extended URL that defaults
      // to VRPN connections at the port, but can be file:: to read
      // from a file.  Other extensions should maintain this, so
      // that VRPN uses URLs to name things that are to be connected
      // to.
      vrpn_Connection (const char * server_name,
                       int port = vrpn_DEFAULT_LISTEN_PORT_NO,
                       const char * local_in_logfile_name = NULL,
                       const char * local_out_logfile_name = NULL,
                       const char * remote_in_logfile_name = NULL,
                       const char * remote_out_logfile_name = NULL,
                       const char * NIC_IPaddress = NULL,
                       vrpn_Endpoint * (* epa) (vrpn_Connection *,
                         vrpn_int32 *) = allocateEndpoint);

    int connectionStatus;		// Status of the connection

    // Sockets used to talk to remote Connection(s)
    // and other information needed on a per-connection basis
    vrpn_Endpoint * d_endpoints [vrpn_MAX_ENDPOINTS];
    vrpn_int32 d_numEndpoints;

    vrpn_int32 d_numConnectedEndpoints;
      ///< We need to track the number of connected endpoints separately
      ///< to properly send out got-first-connection/dropped-last-connection
      ///< messages.  This value is *managed* by the Endpoints, but we
      ///< need exactly one copy per Connection, so it's on the Connection.

    // Only used for a vrpn_Connection that awaits incoming connections
    int listen_udp_sock;	// UDP Connect requests come here
    int listen_tcp_sock;	// TCP Connection requests come here

    // Routines that handle system messages
    static int handle_UDP_message (void * userdata, vrpn_HANDLERPARAM p);
    static int handle_log_message (void * userdata, vrpn_HANDLERPARAM p);
    static int handle_disconnect_message (void * userdata,
		vrpn_HANDLERPARAM p);

    virtual void init (void);	// Called by all constructors

    /// This is called by a server-side process to see if there have
    /// been any UDP packets come in asking for a connection. If there
    /// are, it connects the TCP port and then calls handle_connection().
    virtual void server_check_for_incoming_connections
                  (const struct timeval * timeout = NULL);

    // This routine is called by a server-side connection when a
    // new connection has just been established, and the tcp port
    // has been connected to it.
    virtual void handle_connection (int whichEndpoint);

    // set up network
    virtual void drop_connection (int whichEndpoint);

    int delete_endpoint (int whichEndpoint);
    int compact_endpoints (void);

    virtual int pack_sender_description (vrpn_int32 which);
      ///< Send the sender description to ALL endpoints.

    virtual int pack_type_description (vrpn_int32 which);
      ///< Send the type description to ALL endpoints.

    virtual int do_callbacks_for (vrpn_int32 type, vrpn_int32 sender,
				struct timeval time, vrpn_uint32 len,
	                        const char * buffer);

    // Returns message type ID, or -1 if unregistered
    int message_type_is_registered (const char *) const;

    // Thread safety modifications - TCH 19 May 98

    // Timekeeping - TCH 30 June 98
    timeval start_time;

    const char * d_NIC_IP;

  public:

    // Derived classes need access to d_dispatcher in their
    // allocateEndpoint() routine.  Several compilers won't give it to
    // them, even if they do inherit publically.  Until we figure that
    // out, d_dispatcher needs to be public.

    vrpn_TypeDispatcher * d_dispatcher;

  protected:

    int doSystemCallbacksFor (vrpn_HANDLERPARAM, void *);

    // Server logging w. multiconnection - TCH July 00
    // Use one "hidden" endpoint for outgoing logs (?),
    // standard per-endpoint logs with augmented names for incoming.
    // To make a hidden endpoint we create d_endpoints[0] and increment
    // the d_numEndpoints, but DON'T pass it d_numConnectedEndpoints
    // (although it should be safe to do so, since it should never truly
    // become connected, but we might have to "fake" it to get it to log
    // correctly).

    //vrpn_Endpoint * d_serverLogEndpoint;
    int d_serverLogCount;
    vrpn_int32 d_serverLogMode;
    char * d_serverLogName;

    vrpn_Endpoint * (* d_endpointAllocator) (vrpn_Connection *,
                                             vrpn_int32 *);
    vrpn_bool d_updateEndpoint;

    virtual void updateEndpoints (void);
      ///< This function will be called on the mainloop() iteration
      ///< after *d_endpointAllocator is called, which lets subclasses
      ///< do initialization.  (They can't do so during allocateEndpoint
      ///< because it's called during the Connection constructor when
      ///< their constructors haven't executed yet.)
};


// forward decls
class vrpn_Clock_Server;
class vrpn_Clock_Remote;

// NOTE: the clock offset is calculated only if the freq is >= 0
// if it is -1, then the offset is only calced and used if the
// client specifically calls fullSync on the vrpn_Clock_Remote.
// if the freq is -2, then the offset is calced immediately using
// full sync.
// Time stamp for messages on the connection server are never
// adjusted (only the clients get adjusted time stamps).

// NOTE: eventually this should just be another system message type
//       rather than a separate class, but right now i don't have time
//       for that.

class vrpn_Synchronized_Connection : public vrpn_Connection
{
  public:
    // Create a connection to listen for incoming connections on a port
    // server call.  If no IP address for the NIC to use is specified,
    // uses the default NIC.
    vrpn_Synchronized_Connection (unsigned short listen_port_no =
		         vrpn_DEFAULT_LISTEN_PORT_NO,
                         const char * local_in_logfile_name = NULL,
                         const char * local_out_logfile_name = NULL,
                         const char * NIC_IPaddress = NULL,
                         vrpn_Endpoint * (* epa) (vrpn_Connection *,
                             vrpn_int32 *) = allocateEndpoint);
    vrpn_Clock_Server * pClockServer;

    // Makes a connection to a remote server or to a file, depending on
    // the portion of the server_name argument after the "@" sign.
    // freq is the frequency of clock syncs (<0 means only on user request)
    // cOffsetWindow is how many syncs to include in search window for min
    // roundtrip.  Higher values are more accurate but result in a sync
    // which accumulates drift error more quickly.
    vrpn_Synchronized_Connection
	 (const char * server_name,
          int port = vrpn_DEFAULT_LISTEN_PORT_NO,
          const char * local_in_logfile_name = NULL,
          const char * local_out_logfile_name = NULL,
          const char * remote_in_logfile_name = NULL,
          const char * remote_out_logfile_name = NULL,
	  double dFreq = 4.0, 
	  int cOffsetWindow = 2,
          const char * NIC_IPaddress = NULL,
          vrpn_Endpoint * (* epa) (vrpn_Connection *,
            vrpn_int32 *) = allocateEndpoint);

    // fullSync will perform an accurate sync on the connection for the
    // user and return the current offset
	~vrpn_Synchronized_Connection();
	struct timeval fullSync();
    vrpn_Clock_Remote * pClockRemote;
    virtual int mainloop (const struct timeval * timeout = NULL );
};

// 1hz sync connection by default, windowed over last three bounces 
// WARNING:  vrpn_get_connection_by_name() may not be thread safe.
// If no IP address for the NIC to use is specified, uses the default
// NIC.
vrpn_Connection * vrpn_get_connection_by_name (
    const char * cname,
    const char * local_in_logfile_name = NULL,
    const char * local_out_logfile_name = NULL,
    const char * remote_in_logfile_name = NULL,
    const char * remote_out_logfile_name = NULL,
    double       dFreq               = 1.0,
    int          cSyncWindow         = 3,
    const char * NIC_IPaddress       = NULL);


// Utility routines to parse names (<service>@<location specifier>)
// Both return new char [], and it is the caller's responsibility
// to delete this memory!
char * vrpn_copy_service_name (const char * fullname);
char * vrpn_copy_service_location (const char * fullname);

// Utility routines to parse file specifiers FROM service locations
//   file:<filename>
//   file://<hostname>/<filename>
//   file:///<filename>
char * vrpn_copy_file_name (const char * filespecifier);

// Utility routines to parse host specifiers FROM service locations
//   <hostname>
//   <hostname>:<port number>
//   x-vrpn://<hostname>
//   x-vrpn://<hostname>:<port number>
//   x-vrsh://<hostname>/<server program>,<comma-separated server arguments>
char * vrpn_copy_machine_name (const char * hostspecifier);
int vrpn_get_port_number (const char * hostspecifier);
char * vrpn_copy_rsh_program (const char * hostspecifier);
char * vrpn_copy_rsh_arguments (const char * hostspecifier);

// Checks the buffer to see if it is a valid VRPN header cookie.
// Returns -1 on total mismatch,
// 1 on minor version mismatch or other acceptable difference,
// and 0 on exact match.
int check_vrpn_cookie (const char * buffer);
int check_vrpn_file_cookie (const char * buffer);

// Returns the size of the magic cookie buffer, plus any alignment overhead.
int vrpn_cookie_size (void);

int write_vrpn_cookie (char * buffer, int length, long remote_log_mode);

// Utility routines for reading from and writing to sockets/file descriptors
#ifndef VRPN_USE_WINSOCK_SOCKETS
 int vrpn_noint_block_write (int outfile, const char buffer[], int length);
 int vrpn_noint_block_read(int infile, char buffer[], int length);
#else /* winsock sockets */
 int vrpn_noint_block_write(SOCKET outsock, char *buffer, int length);
 int vrpn_noint_block_read(SOCKET insock, char *buffer, int length);
#endif /* VRPN_USE_WINSOCK_SOCKETS */

#endif // VRPN_CONNECTION_H
