#
# Module vtime - Keep virtual time between two nodes.
#
# We try for synchronised clocks by sending a packet of the for
# (1,mytime,0) to the other side, and waiting (at most) a second for
# a reply. This reply has the form (2,mytime,histime), and we can
# estimate the time difference by defining histime to be exactly half-way
# between the time we sent our message and got our reply. We send a
# final (3,mynewtime,histime) message to allow the other side to do the
# same computations.
#
# Note that the protocol suffers heavily from the 2-army problem.
# It'll have to do until I can read up on time-sync protocols, though.
#
from socket import *
import time

MSGSIZE = 100
MSGTIMEOUT = 1000

recv_timeout = 'receive timeout'
bad_connect = 'Bad connection'

def timeavg(a,b):
    return int((long(a)+b)/2L)
def tryrecv(s):
    cnt = 0
    while 1:
	if s.avail():
	    return s.recvfrom(MSGSIZE)
	time.millisleep(100)
	cnt = cnt + 100
	if cnt > MSGTIMEOUT:
	    raise recv_timeout

class VTime():
    def init(self,(client,host,port)):
	s = socket(AF_INET, SOCK_DGRAM)
	host = gethostbyname(host)
	localhost = gethostbyname(gethostname())
	raddr = (host,port)
	s.bind((localhost,port))
	if client:
	    #
	    # We loop here because we want the *second* measurement
	    # for accuracy
	    for loopct in (0,2):
		curtijd = time.millitimer()
		check = `(loopct,curtijd,0)`
		s.sendto(check,raddr)
		while 1:
		    try:
			if loopct:
			    data, other = s.recvfrom(MSGSIZE)
			else:
			    data, other = tryrecv(s)
			newtijd = time.millitimer()
			if other <> raddr:
			    print 'Someone else syncing to us: ', other
			    raise bad_connect
			data = eval(data)
			if data[:2] == (loopct+1,curtijd):
			    break
			if data[0] <> 2:
			    print 'Illegal sync reply: ', data
			    raise bad_connect
		    except recv_timeout:
			curtijd = time.millitimer()
			check = `(loopct,curtijd,0)`
			s.sendto(check,raddr)
	    histime = data[2]
	    s.sendto(`(4,newtijd,histime)`,raddr)
	    mytime = timeavg(curtijd,newtijd)
	    #mytime = curtijd
	    self.timediff = histime - mytime
	else:
	    while 1:
		data,other = s.recvfrom(MSGSIZE)
		if other <> raddr:
		    print 'Someone else syncing to us: ', other, ' Wanted ', raddr
		    raise bad_connect
		data = eval(data)
		if data[0] in (0,2):
		    curtijd = time.millitimer()
		    s.sendto(`(data[0]+1,data[1],curtijd)`,raddr)
		elif data[0] == 4:
		    newtijd = time.millitimer()
		    histime = data[1]
		    mytime = timeavg(curtijd,newtijd)
		    #mytime = curtijd
		    self.timediff = histime-mytime
		    break
		else:
		    print 'Funny data: ', data
		    raise bad_connect
	return self
	#
    def his2mine(self,tijd):
	return tijd - self.timediff
    #
    def mine2his(self, tijd):
	return tijd + self.timediff

def test(clt, host, port):
    xx = VTime().init(clt,host,port)
    print 'Time diff: ', xx.his2mine(0)
