###############################################################################
#                                                                             #
#   Copyright 2005 University of Cambridge Computer Laboratory.               #
#                                                                             #
#   This file is part of Nprobe.                                              #
#                                                                             #
#   Nprobe is free software; you can redistribute it and/or modify            #
#   it under the terms of the GNU General Public License as published by      #
#   the Free Software Foundation; either version 2 of the License, or         #
#   (at your option) any later version.                                       #
#                                                                             #
#   Nprobe is distributed in the hope that it will be useful,                 #
#   but WITHOUT ANY WARRANTY; without even the implied warranty of            #
#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the             #
#   GNU General Public License for more details.                              #
#                                                                             #
#   You should have received a copy of the GNU General Public License         #
#   along with Nprobe; if not, write to the Free Software                     #
#   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA #
#                                                                             #
###############################################################################


from Tkinter import *
#from np_obnode import *

from minmax import MIN, MAX, MAX3, BIGNUMBER
from nprobe import SERVER, CLIENT, http_client_method_string
from np_packet_markers import *
from np_lookups import np_hostname
from np_widgets import Can, NOSCROLL
from np_drawutil import inside, Nodraw, endpoints
from np_seq import SEQ_MAX, seq_add, seq_sub, seq_diff, seq_gt, seq_gte, \
     seq_lt, seq_lte
from np_longutil import tsDatestring
from np_TCP import trig_str

from math import modf

from np_bitmaps import ccross, med_dash

##############################################################################
##############################################################################

## TCP_WDW_HT = 900
## TCP_WDW_WI = 750

## TCP_WDW_HT = 1100
## TCP_WDW_WI = 950

TCP_WDW_HT = 768
TCP_WDW_WI = 1024

TCP_HMARGIN_PROP = 8

# % of whole window
#TCP_CANVAS_HT_PROP = 70
TCP_CANVAS_HT_PROP = 80
TCP_FLIGHT_HT_PROP = 10
TCP_RTT_HT_PROP = 10

# % of section
TCP_CANV_VMARGIN_PROP = 5
TCP_FLIGHT_VMARGIN_PROP = 10
TCP_RTT_VMARGIN_PROP = 10

MIN_XTIC_PROP = 12
MIN_YTIC_PROP = 7

RTT_ZOOM_FACT = 2
RTT_SCROLL_FACT = 1.1

##############################################################################
##############################################################################

## BG_COL = 'black'
## FG_COL1 = 'white'
## FG_COL2 = 'green'
## FG_COL3 = 'red'
## FG_COL4 = 'blue'
## FG_COL5 = 'OrangeRed1'
## FG_COL6 = 'chocolate4'
## FG_COL7 = 'chartreuse4'

BG_COL = 'white'
FG_COL1 = 'black'
FG_COL2 = 'green'
FG_COL3 = 'red'
FG_COL4 = 'blue4'
FG_COL5 = 'OrangeRed1'
FG_COL6 = 'chocolate4'
FG_COL7 = 'chartreuse4'

AXES_COL = FG_COL1
ATTENTION_COL = FG_COL3
LOW_ATTENTION_COL = FG_COL6

S_SEG_COL = 'DodgerBlue3'
S_ACK_COL = 'dodger blue'
S_WDW_COL_SS = 'MediumPurple'
S_WDW_COL_CA = 'red'
S_WDW_COL_SC = 'SteelBlue2'

C_SEG_COL = 'chartreuse3'
C_ACK_COL = 'PaleGreen1'
C_WDW_COL_SS = '#aaca00'
C_WDW_COL_CA = 'red'
C_WDW_COL_SC = 'green'

TOT_RTT_COL =  FG_COL4

## BG_COL = 'white'
## FG_COL1 = 'black'

INFO_FONT = ("7x13")
TIC_FONT = ("7x13")
PKT_LABEL_FONT = ("6x9")

##############################################################################
##############################################################################

#
# Canvas on which it is all drawn
#

class TCP_canv:

    def __init__(self, master, conn, pktlist, indx1, indx2, w, h, 
		 xpos, ypos, bgcol=BG_COL, fgcol=FG_COL1):

	global S_SEG_COL
	self.Display = master.Display
	self.pktlist = pktlist
        self.modelled = conn.modelled

	self.indx1 = indx1
	self.indx2 = indx2

	self.fgcol = fgcol
	self.bgcol = bgcol

	self.wi = wi = int(w)
	self.ht = ht = int(h)

	#S_SEG_COL = fgcol

## 	# all proportionate
	self.hmargin = (wi*TCP_HMARGIN_PROP)/100
	self.xorg = self.hmargin
	self.xwi = wi-(2*self.hmargin)
	self.xrmargin = self.xorg+self.xwi
	
	self.canv = canv = Can(master, wi, ht, 
				       0, 0, NOSCROLL)
	canv.config(bg=bgcol, 
		    cursor=(ccross, 'orange'))
	canv.place(x=xpos, y=ypos, width=wi, height=ht)
	canv.fgcol = fgcol
	canv.bgcol = bgcol
	canv.scaleline = None
	canv.scaletxt = None
	canv.Canv = self
	self.rboxing = 0
	canv.rbox = None

	#self.make_rbuttons()

	self.pkt_info_canv = None

	self.conn = conn
	self.pktlist = pktlist
	self.indx1 = indx1
	self.indx2 = indx2
	self.wdw = master
	self.xpos = xpos
	self.ypos = ypos

	self.rttinfo = []
	self.phandles = []
	self.rtthandles = [] 
	self.move_rel = 0

	Widget.bind(self.canv, "<1>", self.mouse_1_down)
	Widget.bind(self.canv, "<B1-Motion>", self.mouse_1_drag)
	Widget.bind(self.canv, "<ButtonRelease-1>", self.mouse_1_up)
	#Widget.bind(self.canv, "<Double-Button-1>", self.mouse_1_dblclick)

	Widget.bind(self.canv, "<2>", self.mouse_2_down)
	Widget.bind(self.canv, "<B2-Motion>", self.mouse_2_drag)
	Widget.bind(self.canv, "<Shift-B2-Motion>", self.mouse_2_shift_drag)
	Widget.bind(self.canv, "<Control-B2-Motion>", self.mouse_2_control_drag)
	Widget.bind(self.canv, "<ButtonRelease-2>", self.mouse_2_up)


	Widget.bind(self.canv, "<3>", self.mouse_3_down)
	Widget.bind(self.canv, "<B3-Motion>", self.mouse_3_drag)
	Widget.bind(self.canv, "<ButtonRelease-3>", self.mouse_3_up)
	Widget.bind(self.canv, "<Double-Button-3>", self.wdw.mouse_3_dblclick)

	Widget.bind(self.canv, "<4>", self.mouse_4)
	Widget.bind(self.canv, "<5>", self.mouse_5)

	self.draw()

	

##############################################################################

    def rtt_scaleup(self, fact):
 
	self.canv.delete('rtt') 
	self.canv.delete('rtt_tic')

	self.srscalef = self.srscalef*fact
	self.crscalef = self.crscalef*fact

	self.draw_rtts()

##############################################################################

    def rtt_scaledown(self, fact):

	self.canv.delete('rtt') 
	self.canv.delete('rtt_tic')

	self.srscalef = self.srscalef/fact
	self.crscalef = self.crscalef/fact

	self.draw_rtts()

##############################################################################


    def mouse_1_down(self, event):
	# set origin of selection box
	canv = event.widget
	x = canv.canvasx(event.x)
	y = canv.canvasy(event.y)

	#print 'B1 %d, %d' % (x, y)

	#if y < self.yorg+self.canv_vmargin:
	if inside(x, y, self.mouse_bounds):
	    self.rboxing = 1
	    canv.startx = canv.lastx = x
	    canv.starty = canv.lasty = y
	    canv.rbox = None

	elif inside(x, y, self.rtt_bounds):
	    self.rtt_scaleup(RTT_ZOOM_FACT)

##############################################################################

    def mouse_1_drag(self, event):
	# draw selection box
	canv = event.widget
	x = canv.canvasx(event.x)
	y = canv.canvasy(event.y)

	#if y < self.yorg+self.canv_vmargin:
	if inside(x, y, self.mouse_bounds):    
	    if self.rboxing and canv.startx != x  and canv.starty != y :
		canv.delete(canv.rbox)
		self.rboxing = 1
		canv.rbox = canv.create_rectangle(
		    canv.startx, canv.starty, x, y, outline=self.fgcol)
		canv.lastx = x
		canv.lasty = y
	    else:
		# just entered
		self.rboxing = 1
		canv.startx=x
		canv.starty=y
		canv.rbox = None
		
	    

##############################################################################
		
    def mouse_1_up(self, event):
	
	def sort_pkt_by_time(a, b):
	    if a.tm > b.tm:
		return 1
	    elif a.tm < b.tm:
		return -1
	    else:
		return 0

	def pktlists_same(a, b): # return 1 if same
	    if len(a) != len(b):
		return 0
	    else:
		for i in range(len(a)):
		    if a != b:
			return 0
	    return 1

	canv = event.widget
	if canv.rbox != None:
	    canv.delete(canv.rbox)
	    canv.rbox = None
	x = canv.canvasx(event.x)
	y = canv.canvasy(event.y)
	#if y < self.yorg+self.canv_vmargin and self.rboxing:
	#if 1:
	#if inside(x, y, self.mouse_bounds):
	if self.rboxing:
	    self.rboxing = 0
	    items = list(canv.find_overlapping(canv.startx, canv.starty, 
					       canv.lastx, canv.lasty))
	    
	    pktlist = []
	    pktdict = {} # accumulate like this to avoid duplications
	    for h in self.phandles:
		if items.count(h[1]):
		    pktdict[h[0]] = 1
	    pktlist = pktdict.keys()
	    if len(pktlist) > 1:
		pktlist.sort(sort_pkt_by_time)
		indx1 = pktlist[0].indx
		indx2 = pktlist[-1].indx
		if indx1 != self.indx1 or indx2 != self.indx2:
		    #draw the new zoomed canvas
		    self.wdw.canvasses.append(TCP_canv(self.wdw, self.conn, 
						       self.pktlist, 
						       indx1, indx2, 
						       self.wi, 
						       self.ht, 
						       0, 0))
		    self.wdw.canvas_curr = self.wdw.canvas_curr + 1

##############################################################################

    def mouse_2_down_canv(self, event):

	def make_pkt_info_canv(master):
	    c = Canvas(master, width=500, height=500)
	    c.config(bg=master.bgcol)
	    return c


	canv = event.widget
	x = canv.canvasx(event.x)
	y = canv.canvasy(event.y)
	found_seg = 0
	self.move_rel = 0
	item = canv.find_closest(x, y)[0]
	#print 'item is '
	#print item
	#print self.phandles
	for h in self.phandles:
	    if item == h[1]:
		#print 'found item '
		#print item
		found_seg = 1
		c = self.pkt_info_canv = make_pkt_info_canv(canv)
		bb = self.Display.canv.pkt_txt(h[0], 0, extend = 'true',
                                               fold='true', scol=S_SEG_COL,
                                               canv=c)
		width = bb[2]-bb[0]
		xpos = MIN(x+5, TCP_WDW_WI - width)
		c.place(x=xpos, y=y+5, anchor=NW, width=width, 
			height=bb[3]-bb[1])
		break
	if not found_seg:
	    for tag in ['crel', 'srel', 'cack', 'sack']:
		tags = list(canv.find_withtag(tag))
		#print tags
		l = tags
		#print l
		if tags.count(item):
		#print 'XXX GOT C'
		    self.move_rel = tags
		    self.relstartpos = (x, y)
		    self.relpos = (x, y)
		    self.move_tag = tag
		    c = self.pkt_info_canv = make_pkt_info_canv(canv)
		    break

##############################################################################

    def mouse_2_drag(self, event):

	self.mouse_2_drag_common(event, 0)

##############################################################################

    def mouse_2_shift_drag(self, event):

	self.mouse_2_drag_common(event, 1)

##############################################################################

    def mouse_2_control_drag(self, event):

	self.mouse_2_drag_common(event, 2)

##############################################################################

    def mouse_2_drag_common(self, event, constraint):
	if self.move_rel:
	    canv = event.widget
	    #print 'Moving'
	    x = canv.canvasx(event.x)
	    y = canv.canvasy(event.y)
	    if y < self.yorg:
		#print 'at %d %d relpos %d %d' % (x,y, self.relpos[0],self.relpos[1] )
		xdiff = (x-self.relpos[0])
		ydiff = (y-self.relpos[1])
		#print 'diff %d %d' % (xdiff, ydiff)
		self.relpos = (x, y)

		if constraint == 1:
		    xdiff = 0
		if constraint == 2:
		    ydiff = 0
		
		self.slide(canv, x, y, xdiff, ydiff)

##############################################################################

    def mouse_2_up_canv(self, event):
	c = self.pkt_info_canv
	if c != None:
	    c.destroy()
	    self.pkt_info_canv = None

	if self.move_rel:
	    canv = event.widget
	    #print 'Moving'
	    x = canv.canvasx(event.x)
	    y = canv.canvasy(event.y)
	    xdiff = (x-self.relstartpos[0])
	    ydiff = (y-self.relstartpos[1])
	    #print 'diff %d %d' % (xdiff, ydiff)

	    self.slide(canv, x, y, -xdiff, -ydiff)
	    canv.delete('tmp')

##############################################################################


    def slide(self, canv, x, y, xd, yd):

	    for t in self.move_rel:
		xy = canv.coords(t)
		if len(xy) == 2:
		    #text
		    x1 = xy[0]+xd
		    y1 = xy[1]+yd
		    canv.coords(t, x1, y1)
		else:
		    #line
		    x1 = xy[0]+xd
		    y1 = xy[1]+yd
		    x2 = xy[2]+xd
		    y2 = xy[3]+yd
		    canv.coords(t, x1, y1, x2, y2)

	    #canv = self.pkt_info_canv
	    td = (x-self.relstartpos[0])/self.tscalef
	    sd = int((y-self.relstartpos[1])/self.sscalef)
	    tstr = '%+.3f %+d' % (td/1000, -sd)
	    canv.delete('tmp')
	    if x + 125 > self.wi:
		anch = E
	    else:
		anch = W
	    canv.create_text(x+5, y-5, fill=self.fgcol,  font=("helvetica", 8),
				  anchor=anch, text=tstr, tag='tmp')

##############################################################################


    def mouse_3_down(self, event):
	# rb scaler
	canv = event.widget
	x = canv.canvasx(event.x)
	y = canv.canvasy(event.y)
	
	canv.scalestartx = x
	canv.scalestarty = y
	canv.scaleline = None
	canv.scaletxt = None

	if inside(x, y, self.rtt_bounds):
	    #scale for server or client?
	    r = 2
	    got = 0
	    while not got:
		items = canv.find_overlapping(x-r, y-r, x+r, y+r)
		for i in items:
		    l = list(canv.gettags(i))
		    if l.count('rtt'):
			got = 1
			break
		r = r+2
	    
	    if l.count('c'):
		self.rttscaling = self.crscalef
		self.rttscalingcol=C_SEG_COL
	    elif l.count('s'):
		self.rttscaling = self.srscalef
		self.rttscalingcol=S_SEG_COL
	    else:
		print 'unknown rtt tag'
		sys.exit(1)

##############################################################################

    def mouse_3_drag(self, event):

	def txt1(tmd, sd, ib):
	    return '%+.3f %+d\n  IB=%.3fMbs' % (tmd/1000.0, sd, ib)

	def txt2(tmd, sd, ib):
	    return '%+.3f %+d' % (tmd/1000.0, sd)

	def txt3(tmd, sd, ib):
	    return '%+.3f %+.3f' % (tmd/1000.0, sd/1000.0)
	    
	# show scaled values
	canv = event.widget
	x = canv.canvasx(event.x)
	y = canv.canvasy(event.y)

	col = self.fgcol

	# beware noisy mouse buttons
	try:
	    if x != canv.scalestartx or y != canv.scalestarty:
		canv.delete(canv.scaleline)
		canv.delete(canv.scaletxt)
		canv.scaleline = canv.create_line(canv.scalestartx, 
						  canv.scalestarty, 
						  x, y, fill=FG_COL5)

		if y < self.yorg+self.canv_vmargin/2:
		    yscalef = self.sscalef
		    textf = txt1
		elif y < self.flight_yorg+self.flight_vmargin/2:
		    yscalef = self.fscalef
		    textf = txt2
		else:
		    yscalef = self.rttscaling
		    if not yscalef:
			return
		    col = self.rttscalingcol
		    textf = txt3
		tmd = (x-canv.scalestartx)/self.tscalef
		atmd = abs(tmd)
		sd = (canv.scalestarty-y)/yscalef
		asd = abs(sd)
		if atmd:
		    ib = (asd*8.0)/atmd
		else:
		    ib = 0.0
		if x + 100 > self.wi:
		    anch = E
		else:
		    anch = W
		scaletxt = textf(tmd, sd, ib)
		canv.scaletxt = canv.create_text(x+5, y-5, anchor=anch, 
				fill=col, font=("helvetica", 8), text=scaletxt)
	except AttributeError:
	    pass
	    

    def mouse_3_up(self, event):

	canv = event.widget
	x = canv.canvasx(event.x)
	y = canv.canvasy(event.y)

    #if inside(x, y, self.bounds) or inside(x, y, self.rtt_bounds):
	if 1:
	    if canv.scaleline != None:
		canv.delete(canv.scaleline)
		canv.delete(canv.scaletxt)


    def mouse_3_dblclick(self, event):
	# pop canvas zoom stack
	canv = event.widget


    def mouse_2_down_rtt(self, event):
	# get and display rtt point
	canv = event.widget
	x = canv.canvasx(event.x)
	y = canv.canvasy(event.y)

	t = canv.find_overlapping(x-5, y-5, x+5, y+5)
	#print t
	items = list(t)
	#print items
	#print self.rtthandles

	for h in self.rtthandles:
	    if items.count(h[1]): 
		c = Canvas(canv, width=500, height=500)
		c.config(bg=self.bgcol)
		p = h[0]
		if p.dir == SERVER:
		    col = self.fgcol
		else:
		    col = FG_COL2
		tstr = '%d %s' % (p.indx, trig_str(p))
		if p.prtt:
		    tstr = tstr + ' prtt %.3f' % (p.prtt/1000.0)
		if p.trtt:
		    tstr = tstr + ' trtt %.3f' % (p.trtt/1000.0)
		if p.delay:
		    tstr = tstr + ' delay %.3f' % (p.delay/1000.0)
		t = c.create_text(0, 0, fill=col, anchor=NW, 
				  font=("helvetica", 8, 'bold'), text=tstr)
		b = c.bbox(t)
		wi = b[2]-b[0]
		xpos = MIN(x+5, TCP_WDW_WI - wi)
		c.place(x=xpos, y=y+5, anchor=SW, width=wi, 
			height=b[3]-b[1])
		self.rttinfo.append(c)
		break
		    


    def mouse_2_up_rtt(self, event):

	for w in self.rttinfo:
	    w.destroy()
	    self.rttinfo = []


    def mouse_2_down(self, event):

	canv = event.widget
	x = canv.canvasx(event.x)
	y = canv.canvasy(event.y)

	if y < self.yorg:
	   self.mouse_2_down_canv(event)
	elif y > self.flight_yorg:
	   self.mouse_2_down_rtt(event)


    def mouse_2_up(self, event):

	canv = event.widget
	x = canv.canvasx(event.x)
	y = canv.canvasy(event.y)

	if y < self.yorg:
	   self.mouse_2_up_canv(event)
	elif y > self.flight_yorg:
	   self.mouse_2_up_rtt(event) 
	
############################################################################### 
    def mouse_4(self, event):

	canv = event.widget
	x = canv.canvasx(event.x)
	y = canv.canvasy(event.y)

	if inside(x, y, self.rtt_bounds):
	    self.rtt_scaleup(RTT_SCROLL_FACT) 
	
############################################################################### 
    def mouse_5(self, event):

	canv = event.widget
	x = canv.canvasx(event.x)
	y = canv.canvasy(event.y)

	if inside(x, y, self.rtt_bounds):
	    self.rtt_scaledown(RTT_SCROLL_FACT)

	
	
###############################################################################

    def draw(self):

	ht = self.ht
	wi = self.wi

	info_ht = self.draw_info()
	
	canv_ht = (TCP_CANVAS_HT_PROP*ht)/100 - info_ht
	flight_ht = (TCP_FLIGHT_HT_PROP*ht)/100
	rtt_ht = (TCP_RTT_HT_PROP*ht)/100
	#info_ht = ht - (canv_ht+flight_ht+rtt_ht) # balance

	canv_vmargin = (canv_ht*TCP_CANV_VMARGIN_PROP)/100
	flight_vmargin = (flight_ht*TCP_FLIGHT_VMARGIN_PROP)/100
	rtt_vmargin = (rtt_ht*TCP_RTT_VMARGIN_PROP)/100

	self.yorg = canv_ht+info_ht-canv_vmargin
	self.flight_yorg = canv_ht+info_ht+flight_ht-flight_vmargin
	self.rtt_yorg = canv_ht+info_ht+flight_ht+rtt_ht-rtt_vmargin

	self.yht = canv_ht-canv_vmargin
	self.flight_yht = flight_ht-(2*flight_vmargin) -25
	self.rtt_yht = rtt_ht-(2*rtt_vmargin) -25
	self.info_yht = info_ht

	self.bounds = [self.xorg, self.xrmargin, self.yorg-canv_ht, self.yorg]
	self.mouse_bounds = [self.xorg-15, self.xrmargin+15, 
			     self.yorg-canv_ht-15, self.yorg+15]
	#print self.bounds
	self.rtt_bounds = [self.hmargin, self.xrmargin, 
			   self.rtt_yorg-self.rtt_yht, self.rtt_yorg]

	self.canv_vmargin = canv_vmargin
	self.flight_vmargin = flight_vmargin

	self.min_xtic_wi = (self.xwi*MIN_XTIC_PROP)/100
	self.min_ytic_hi = (self.yht*MIN_YTIC_PROP)/100

	self.scale()
	
	self.draw_axes()
	self.draw_releases()
	self.draw_pkts()
	self.draw_highacks()
	self.draw_inflights()
	self.draw_rtts()
	
###############################################################################
	   

    def draw_info(self):

	conn = self.conn

	x = self.xorg
	wi = self.xwi
	y = 15

	TEXT = self.canv.create_text

	try:
	    trans = conn.tlist[0]
	    meth = trans.http_meth()
	    mstr = http_client_method_string(meth)
            #stype = trans.get_server()
            #ctype = trans.get_uagent()
	except IndexError:
	    mstr = ''

	if conn.http_status and conn.pers:
	    pstr = 'Persistent %d transactions' % (conn.ntrans)
	else:
	    pstr = 'Non-persistent'

	tstr= '#%d Client %s  Server %s port %d\n%s %s' % \
	       (conn.id, np_hostname(conn.client), np_hostname(conn.server), 
		conn.serverport, pstr, mstr)
	t = TEXT(x, y, fill=self.fgcol, width=wi, 
	     anchor=NW, text=tstr, font=INFO_FONT);
	y = self.canv.bbox(t)[3] + 5

	tstr= 'User agent %s  Server %s' % \
	       (conn.uagent, conn.sagent)
	t = TEXT(x, y, fill=self.fgcol, width=wi, 
	     anchor=NW, text=tstr, font=INFO_FONT);
	y = self.canv.bbox(t)[3] + 5
        
	tstr = 'Start %s duration %.3fms  ' % (tsDatestring(conn.abstart), 
					   conn.dur/1000)
	clostr = 'C: %d/%d mss %d S: %d/%d mss %d' % \
		 (conn.cbytes, conn.cpkts, conn.smss, 
		  conn.sbytes, conn.spkts, conn.cmss)
	t = TEXT(x, y, fill=self.fgcol, font=INFO_FONT, width=wi, 
	     anchor=NW, text=tstr+clostr);
	y = self.canv.bbox(t)[3] + 5

        if conn.modelled:
            text = 'Server imp %s Client imp %s' % (conn.simp.descr_str(), conn.cimp.descr_str())
        else:
            text = 'Not modelled'
        t = TEXT(x, y, fill=self.fgcol, font=INFO_FONT, width=wi, 
                     anchor=NW, text=text)
	return self.canv.bbox(t)[3] + 50 
	
###############################################################################

	
    def draw_pkts(self):
	#print self.phandles

	plist = self.pktlist
	i1 = self.indx1
	i2 = self.indx2

	LINE = self.canv.create_line
	TEXT = self.canv.create_text
	
	#draw packets
	for p in plist[i1:i2+1]:
	    #p.printself_rel()
	    seq = p.wseq
	    #flags = ord(p.flags)
	    flags = p.flags

	    x =  self.xorg + int((p.tm-self.tbase)*self.tscalef)
	    pno = '%d' % (p.indx)
	    self.canv.create_text(x, self.yaxpos+20, anchor=N, text=pno, 
				  fill= AXES_COL, font=PKT_LABEL_FONT)

	    #
	    # Draw the segment
	    #

	    if p.dir == SERVER:
		self.draw_seq(p, x, p.wseq-self.sseqbase, p.len, flags, 
			      S_SEG_COL)
	    else:
		self.draw_seq(p, x, p.wseq-self.cseqbase, p.len, flags, 
			      C_SEG_COL)
 
	
###############################################################################


    def draw_seq(self, pkt, x, seq, len, flags, col):

	LINE = self.canv.create_line
	TEXT = self.canv.create_text
	y0 = self.yorg - int(seq*self.sscalef)
	if len:
	    y1 = y0 - int(len*self.sscalef)
	    self.phandles.append((pkt, LINE(x, y0, x, y1, fill=col, 
					   arrow='both', arrowshape=(4,5,2))))
	    TEXT(x-5, (y0+y1)/2, fill=col, anchor=E, font=PKT_LABEL_FONT, 
		 text='%d/%d' % (pkt.indx, pkt.clu))
	    TEXT(x+5, (y0+y1)/2, fill=col, anchor=W, font=PKT_LABEL_FONT, 
		 width=100, text=pkt.tstr)
	else:
	    y1 = y0
	    self.phandles.append((pkt, self.draw_blob(x, y0, col)))

	if pkt.remark:
	    col = ATTENTION_COL
	    sz = MAX(y0-y1, 10)/2
	    ypos = (y0+y1)/2
	    self.canv.create_oval(x-sz, ypos-sz, x+sz, ypos+sz, outline=ATTENTION_COL)
	    

	if flags & TH_SYN:
	    TEXT(x, y1-2, fill=col, anchor=S, text='S', font=("helvetica", 8))
	txt = ''
	if flags & TH_FIN:
	    txt = txt + 'F'
	if flags & TH_RST:
	    txt = txt + 'R'
	    col = ATTENTION_COL
	if flags & TH_PUSH:
	    txt = txt + 'P'
	TEXT(x+2, y1, fill=col, anchor=W, text=txt, font=("helvetica", 8)) 

	if (pkt.trig & SEG_TRIG) and pkt.lag != None:
	    y = (y0+y1)/2
	    trigtm = pkt.tm - pkt.lag
	    ext = 0
	    x1 = self.xorg + int((trigtm-self.tbase)*self.tscalef)
	    if x1 < self.xorg:
		x1 = self.xorg
		ext = 1
	    LINE(x1, y, x, y, fill='salmon1', 
		 stipple=med_dash)
	    LINE(x-1, y, x, y, fill='blue', arrow='last', arrowshape=(4,5,2))
	    if ext:
		LINE(x1-5, y+5, x1+5, y-5, fill='blue')
	    
###############################################################################


    #
    # Draw the flight graph
    #
    def draw_inflights(self):

	plist = self.pktlist
	i1 = self.indx1
	i2 = self.indx2
	
	LINE = self.canv.create_line

	sinflight = plist[i1].sinflight
	cinflight = plist[i1].cinflight
	flightx = self.xorg
	sinflighty =  self.flight_yorg - sinflight*self.fscalef
	cinflighty = self.flight_yorg - cinflight*self.fscalef

	for p in plist[i1:i2+1]:

	    x =  self.xorg + int((p.tm-self.tbase)*self.tscalef)

	    # extend from last position
	    LINE(flightx, sinflighty, x, sinflighty, fill= S_SEG_COL)
	    LINE(flightx, cinflighty, x, cinflighty, fill= C_SEG_COL)
	    # any change
	    sy = self.flight_yorg - p.sinflight*self.fscalef
	    cy = self.flight_yorg - p.cinflight*self.fscalef
	    LINE(x, sinflighty, x, sy, fill=S_SEG_COL)
	    LINE(x, cinflighty, x, cy, fill=C_SEG_COL)
	    flightx = x
	    sinflighty = sy
	    cinflighty = cy 
	
###############################################################################

    #
    # Find current ack highwaters - return -1 if none
    #
    def find_curr_acks(self, plist, indx):

	i = indx
	si = ci = -1
	got = 0
	while 1:
	   i = i-1 
	   if i < 0 or got == 2:
	       #print '%d %d' % (si, ci)
	       return (si, ci)
	   p = plist[i]
	   #flags = ord(p.flags)
	   flags = p.flags
	   if flags & TH_ACK:
	       if p.dir == SERVER:
		   if si < 0:
		       si = i
		       got = got +1
	       else:
		   if ci < 0:
		       ci = i
		       got = got +1
 
	
###############################################################################

    #
    # draw the acks and highwaters
    #
    def draw_highacks(self):

	plist = self.pktlist
	i1 = self.indx1
	i2 = self.indx2

	LINE = self.canv.create_line
	TEXT = self.canv.create_text

	sacking = 0
	cacking = 0
	ackx = self.xorg

	scurracki, ccurracki = self.find_curr_acks(plist, i1)
	if scurracki >= 0:
	    scurrack = plist[scurracki].wack
	    #print 's curr ack = %s' % (Longstring(scurrack))
	    sacking = 1
	    scurracky = self.yorg - int((scurrack - self.cseqbase)*self.sscalef)
	if ccurracki >= 0:
	    ccurrack = plist[ccurracki].wack
	    #print 'c curr ack = %s' % (Longstring(ccurrack))
	    cacking = 1
	    ccurracky = self.yorg - int((ccurrack - self.sseqbase)*self.sscalef)

	for p in plist[i1:i2+1]:
	    #flags = ord(p.flags)
	    flags = p.flags
	    way = p.dir
	    x =  self.xorg + int((p.tm-self.tbase)*self.tscalef)

	    # extend from last position
	    if sacking and scurracky <= self.yorg:
		LINE(ackx, scurracky, x, scurracky, fill=C_ACK_COL, 
		     tag='sack')
	    if cacking and ccurracky <= self.yorg:
		LINE(ackx, ccurracky, x, ccurracky, fill=S_ACK_COL, 
		     tag='cack')
	    # any change
	    if (flags & TH_ACK):
		ack = p.wack
		if way == SERVER:
		    col = C_ACK_COL
		    y = self.yorg - int((ack-self.cseqbase)*self.sscalef) 
		    if sacking:
			if ack != scurrack:
			    y1 = MIN(scurracky, self.yorg) 
			    LINE(x, y1, x, y, 
				 fill=col, tag='sack')    
			    self.phandles.append((p, self.draw_point(x, y, 
							    col, p.indx)))
			    if (p.trig & ACK_TRIG) and p.lag != None:
				#print 'ack %d' % (p.indx)
				yt = self.yorg - int(((p.pkt_acked.wseq+(p.pkt_acked.len/2))-self.cseqbase)*self.sscalef)
				#yt = (y+y1)/2
				trigtm = p.tm - p.lag
				ext = 0
				x1 = self.xorg + int((trigtm-self.tbase)*self.tscalef)
				if x1 < self.xorg:
				    x1 = self.xorg
				    ext = 1
				LINE(x1, yt, x, yt, fill='orchid1', 
				     stipple=med_dash, arrow='last', arrowshape=(4,5,2))
				if ext:
				    LINE(x1-5, y+5, x1+5, y-5, fill='blue')
			else:
			    self.draw_vtic(x, scurracky, col, p.indx)
		    scurrack = ack
		    scurracky = y
		    sacking = 1
		    #print 'server ack %s - %s = %s' % (Longstring(p.ack), Longstring(self.slowack), Longstring(p.ack-self.slowack))
		    #print 'server change to %d %d' % (x, y)
		else:
		    col = S_ACK_COL
		    y = self.yorg - int((ack-self.sseqbase)*self.sscalef)
		    if cacking:
			if ack != ccurrack:
			    y1 =  MIN(ccurracky, self.yorg)
			    LINE(x, y1, x, y, 
				 fill=col, tag='cack')    
			    self.phandles.append((p, self.draw_point(x, y, 
							    col, p.indx)))
			    if (p.trig & ACK_TRIG) and p.lag != None:
				#yt = (y+y1)/2
				yt = self.yorg - int(((p.pkt_acked.seq+(p.pkt_acked.len/2))-self.sseqbase)*self.sscalef)
				trigtm = p.tm - p.lag
				ext = 0
				x1 = self.xorg + int((trigtm-self.tbase)*self.tscalef)
				if x1 < self.xorg:
				    x1 = self.xorg
				    ext = 1
				LINE(x1, yt, x, yt, fill='cyan', 
				     stipple=med_dash, arrow='last', arrowshape=(4,5,2))
				if ext:
				    LINE(x1-5, y+5, x1+5, y-5, fill='blue')
			else:
			    self.draw_vtic(x, scurracky, col, p.indx)
		    ccurrack = ack
		    ccurracky = y
		    cacking = 1
		    #print 'client ack %s - %s = %s' % (Longstring(p.ack), Longstring(self.clowack), Longstring(p.ack-self.clowack))
		    #print 'client change to %d %d' % (x, y)

	    ackx = x 
	
###############################################################################

    #
    # Find current release - return -1 if none
    #

    def find_curr_rel(self, plist, indx, way):

	i = indx
	while 1:
	   i = i-1 

	   if i < 0:
	       return -1

	   p = plist[i]
	   #p.printself_rel()
	   if p.dir == way and (p.flags & TH_ACK and p.wrel != None):
		return i 
	
###############################################################################


    def draw_releases(self):

	plist = self.pktlist
	i1 = self.indx1
	i2 = self.indx2

	LINE = self.canv.create_line
	TEXT = self.canv.create_text

	crelcol_flowwin = S_WDW_COL_SC
	crelcol_conwin = S_WDW_COL_SS
	srelcol_flowwin = C_WDW_COL_SC
	srelcol_conwin = C_WDW_COL_SS

	sreling = 0
	creling = 0
	srelx = crelx = self.xorg
	

	i = self.find_curr_rel(plist, i1, SERVER)
	if i >= 0:
	    p = plist[i]
	    sreling = 1
	    scurrel = p.wrel
	    scurrely = self.yorg - int((scurrel - self.cseqbase)*self.sscalef)
	    if p.markers & P_REL_BOSS_CWND:
		scurrelcol = srelcol_conwin
	    else:
		scurrelcol = srelcol_flowwin
	

	i = self.find_curr_rel(plist, i1, CLIENT)
	if i >= 0:
	    p = plist[i]
	    creling = 1
	    ccurrel = p.wrel
	    ccurrely = self.yorg - int((ccurrel - self.sseqbase)*self.sscalef)
	    if p.markers & P_REL_BOSS_CWND:
		ccurrelcol = crelcol_conwin
	    else:
		ccurrelcol = crelcol_flowwin


	for p in plist[i1:i2+1]:

	    x =  self.xorg + int((p.tm-self.tbase)*self.tscalef)
	    way = p.dir


	    #
	    # Draw the releases - ack + min(snd_wnd, cwnd)
	    #

	    if p.wrel != None:
		#print '#%d WREL %s' % (p.indx, Longstring(p.wrel))
		if way == CLIENT:
		    if p.markers & P_REL_BOSS_CWND:
			col = crelcol_conwin
		    else:
			col = crelcol_flowwin
		    if creling:
			#extend
			if ccurrely > self.info_yht:
			    LINE(crelx, ccurrely, x, ccurrely, 
				 fill=ccurrelcol, tag='crel')
			# increase?
			#if not seq_gt(p.wrel, ccurrel):
			if p.wrel <= ccurrel:
			    print '#%d Goof client ACK rel increase not increasing' % (p.indx)
			y = self.yorg - int((p.wrel - self.sseqbase)*self.sscalef)
			LINE(x, MAX(ccurrely, self.info_yht), 
			     x, MAX(y, self.info_yht), 
			     fill=col, tag='crel')
			# if small x increase mark with tic
			if x - crelx < 10 and ccurrely > self.info_yht:
			    LINE(x-5, ccurrely, x+5, ccurrely, fill=ccurrelcol, tag='crel')
			ty = (ccurrely+y)/2
			if ty > self.info_yht:
			    TEXT(x+2, ty, fill=col, anchor=W, 
				 font=PKT_LABEL_FONT,
				 text='+%d' % (int(p.wrel-ccurrel)), 
				 tag='crel')
			ccurrely = y
		    else:
		       ccurrely = self.yorg - int((p.wrel - self.sseqbase)*self.sscalef) 
		    ccurrel = p.wrel
		    creling = 1
		    crelx = x
		    ccurrelcol = col

		if way == SERVER:
		    if p.markers & P_REL_BOSS_CWND:
			col = srelcol_conwin
		    else:
			col = srelcol_flowwin
		    if sreling:
			#extend
			if scurrely > self.info_yht:
			    LINE(srelx, scurrely, x, scurrely, fill=scurrelcol, tag='srel')
			# increase?
			#if not seq_gt(p.wrel, scurrel):
			if p.wrel <= scurrel:
			    print '#%d Goof server ACK rel increase not increasing' % (p.indx)
			y = self.yorg - int((p.wrel - self.cseqbase)*self.sscalef)
			LINE(x, MAX(scurrely, self.info_yht), 
			     x, MAX(y, self.info_yht), 
			     fill=col, tag='srel')
			# if small x increase mark with tic
			if x - srelx < 10:
			    LINE(x-5, scurrely, x+5, scurrely, fill=scurrelcol, tag='srel')
			scurrely = y
		    else:
		       scurrely = self.yorg - int((p.wrel - self.cseqbase)*self.sscalef) 
		    scurrel = p.wrel
		    sreling = 1
		    srelx = x
		    scurrelcol = col 
	
###############################################################################

    #
    # Find current rtts  - return -1 if none
    #

    def find_curr_rtts(self, plist, indx, dirn='before'):

	i = indx
	si = ci = ti = -1
	got = 0
	if dirn == 'before':
	    inc = -1
	else:
	    inc = 1
	topi = len(plist)

	#print 'find_curr_rtts %s #%d' % (dirn, indx)
	while 1:
	   i = i + inc 
	   if i < 0 or i >= topi or got == 3:
	       #print 'si = %d ci = %d ti = %d' % (si, ci, ti)
	       return (si, ci, ti)

	   p = plist[i]
	   way = p.dir
	   if  si < 0:
	       if way == SERVER and p.prtt != 0:
		   si = i
		   got = got + 1
	   if  ci < 0:
	       if way == CLIENT and p.prtt != 0:
		   ci = i
		   got = got + 1
	   if ti < 0 and p.trtt != 0:
		   ti = i
		   got = got + 1
 
	
###############################################################################

    #
    # Draw the RTT graph
    #
    def draw_rtts(self):

	self.draw_rtt_axes()
	self.draw_rtt_ytics()

	smin = self.smin_rtt_curr
	cmin = self.cmin_rtt_curr

	plist = self.pktlist
	i1 = self.indx1
	i2 = self.indx2

	
	LINE = self.canv.create_line
	TEXT = self.canv.create_text
	#RECT = self.canv.create_rectangle

	srtting = 0
	crtting = 0
	rtting = 0
	#srttx = crttx = rttx = self.xorg
	

	si, ci, ri = self.find_curr_rtts(plist, i1)
	if si >= 0:
	    p = plist[si]
	    srtting = 1
	    scurrtt = p.prtt
	    scurrttx =  self.xorg + int((p.tm-self.tbase)*self.tscalef)
	    scurrtty = self.rtt_yorg - int((scurrtt-smin)*self.srscalef)
	if ci >= 0:
	    p = plist[ci]
	    crtting = 1
	    ccurrtt = p.prtt
	    ccurrttx =  self.xorg + int((p.tm-self.tbase)*self.tscalef)
	    ccurrtty = self.rtt_yorg - int((ccurrtt-cmin)*self.crscalef)
	if ri >= 0:
	    p = plist[ri]
	    rtting = 1
	    currtt = p.trtt
	    currttx =  self.xorg + int((p.tm-self.tbase)*self.tscalef)
	    currtty = self.rtt_yorg - int(currtt*self.srscalef)

	for p in plist[i1:i2+1]:
	    way = p.dir

	    x =  self.xorg + int((p.tm-self.tbase)*self.tscalef)

	    if p.prtt:
		if  way == CLIENT:
		    y = self.rtt_yorg - int((p.prtt-cmin)*self.crscalef)
		    col=C_SEG_COL
		    if crtting:
			try:
			    x1, y1, x2, y2 = endpoints(ccurrttx, 
					    ccurrtty, x, y, self.rtt_bounds)
			    LINE(x1, y1, x2, y2, fill=col, tag=('rtt','c'))
			except Nodraw:
			    pass
			
		    if not (p.trig & TRIG_RTT):
			col = LOW_ATTENTION_COL
		    if inside(x, y, self.rtt_bounds):
			self.rtthandles.append((p, self.draw_blob(x, y, col, 
 						   tag=('rtt','c'))))
			    
		    ccurrttx = x
		    ccurrtty = y
		    crtting =1
		else:
		    y = self.rtt_yorg - int((p.prtt-smin)*self.srscalef)
		    col=S_SEG_COL
		    if srtting:
			try:
			    x1, y1, x2, y2 = endpoints(scurrttx, scurrtty, x, y, self.rtt_bounds)
			    
			    LINE(x1, y1, x2, y2, fill=col, tags=('rtt','s'))
			except Nodraw:
			    pass

			
		    if not (p.trig & TRIG_RTT):
			col = LOW_ATTENTION_COL
		    if inside(x, y, self.rtt_bounds):
			self.rtthandles.append((p, self.draw_blob(x, y, col, 
 						   tag=('rtt','s'))))
		    scurrttx = x
		    scurrtty = y
		    srtting =1

	    if p.trtt:
		y = self.rtt_yorg - int(p.trtt*self.srscalef)
		col='blue'
		if rtting:
		    if currttx < self.xorg:
			    currtty = self.interpy_l(x, y, 
					   currttx, currtty, self.xorg)
		    LINE(currttx, currtty, x, y, fill=col, tag='rtt')
		self.rtthandles.append((p, RECT(x-2, y-2, x+2, y+2, 
						outline='', fill=col, tag='rtt')))
		currttx = x
		currtty = y
		rtting =1

	    if p.delay:
		if way == CLIENT:
		    col=C_SEG_COL
		    y = ccurrtty - int(p.delay*self.crscalef)
		    try:
			x1, y1, x2, y2 = endpoints(x, ccurrtty, x, y, 
							self.rtt_bounds)
			LINE(x1, y1, x2, y2, fill=col, tags=('rtt','c'))
			if p.delay < 0:
			    sz = MAX((y1-y2)/2, 5)
			    ya = (y1+y2)/2
			    self.canv.create_oval(x-sz, ya-sz, x+sz, ya+sz, 
				    outline=ATTENTION_COL, tags=('rtt','c'))
			if y2 !=y and y1 > self.rtt_bounds[2]:
			    TEXT(x, y2, fill=col, anchor=CENTER, 
			     font=("helvetica", 10), text='/', tags=('rtt','c'))
		    except Nodraw:
			pass
		if way == SERVER:
		    col=S_SEG_COL
		    y = scurrtty - int(p.delay*self.srscalef)
		    try:
			x1, y1, x2, y2 = endpoints(x, scurrtty, x, y, 
							self.rtt_bounds)
			LINE(x1, y1, x2, y2, fill=col, tags=('rtt','s'))
			if p.delay < 0:
			    sz = MAX((y1-y2)/2, 5)
			    ya = (y1+y2)/2
			    self.canv.create_oval(x-sz, ya-sz, x+sz, ya+sz, 
				    outline=ATTENTION_COL, tags=('rtt','s'))
			if y2 !=y and y1 > self.rtt_bounds[2]:
			    TEXT(x, y2, fill=col, anchor=CENTER, 
			     font=("helvetica", 10), text='/', tags=('rtt','s'))
		    except Nodraw:
			pass

	#
	# Extend rtt graph towards any following points
	#

	si, ci, ri = self.find_curr_rtts(plist, i2, dirn = 'after')

	if si > 0 and srtting:
	    p = plist[si]
	    xnew = self.xorg + int((p.tm-self.tbase)*self.tscalef)
	    ynew = self.rtt_yorg - int((p.prtt-smin)*self.srscalef)
	    #y = self.interpy_l(scurrttx, scurrtty, xnew, ynew, self.xorg+self.xwi)
	    try:
		x1, y1, x2, y2 = endpoints(scurrttx, scurrtty, xnew, ynew,
						self.rtt_bounds)
		LINE(x1, y1, x2, y2, fill=S_SEG_COL, tags=('rtt','s'))
	    except Nodraw:
		pass
	if ci > 0 and crtting:
	    p = plist[ci]
	    xnew = self.xorg + int((p.tm-self.tbase)*self.tscalef)
	    ynew = self.rtt_yorg - int((p.prtt-cmin)*self.crscalef)
	    #y = self.interpy_l(ccurrttx, ccurrtty, xnew, ynew, self.xorg+self.xwi)
	    #LINE(ccurrttx, ccurrtty, x, y, fill= C_SEG_COL, tag='rtt')
	    try:
		x1, y1, x2, y2 = endpoints(ccurrttx, ccurrtty, xnew, ynew,
						self.rtt_bounds)
		LINE(x1, y1, x2, y2, fill=C_SEG_COL, tags=('rtt','c'))
	    except Nodraw:
		pass
	if ri > 0 and rtting:
	    p = plist[ri]
	    xnew = self.xorg + int((p.tm-self.tbase)*self.tscalef)
	    ynew = self.rtt_yorg - int(p.trtt*self.srscalef)
	    #y = self.interpy_l(currttx, currtty, xnew, ynew, self.xorg+self.xwi)
	    #LINE(currttx, currtty, x, y, fill= TOT_RTT_COL, tag='rtt')
	    try:
		x1, y1, x2, y2 = endpoints(currttx, currtty, xnew, ynew,
						self.rtt_bounds)
		LINE(x1, y1, x2, y2, fill=C_SEG_COL, tag='rtt')
	    except Nodraw:
		pass 
	
##############################################################################
	    

    def calc_ticfacts(self, minx, scalef, range):
	try:
	    t = int(minx/scalef)
	except OverflowError:
	    #print 'range = %d scalef = %f' % (range, scalef)
	    inc = range
	    xinc = inc*scalef
	    return
	inc = 1
	while 1:
	    if inc > t:
		break
	    inc = inc*10

	lastdelta = abs(inc - t)
	lastinc = inc
	for scale in [2,4,10]:
	    thisinc = inc/scale
	    delta = abs(thisinc - t)
	    if delta > lastdelta or round(thisinc) != thisinc:
		break
	    lastdelta = delta
	    #inc = thisinc
	    lastinc = thisinc

	inc = lastinc

	if inc == 0:
	    inc = 1 #?

	xinc = inc*scalef

	return (xinc, inc) 
	
###############################################################################
	

    def draw_xtics(self, y, tbase):
	
	LINE = self.canv.create_line
	TEXT = self.canv.create_text
	scalef = self.tscalef
	x = self.xorg
	xinc, tinc = self.calc_ticfacts(self.min_xtic_wi, scalef, self.dur)
	tm = tbase
	xadj = 0
	# get the first tic to a sensible value
	baseadj = tbase%tinc
	if baseadj:
	    tm = tbase-baseadj + tinc
	    # offset the tics
	    xadj = int(baseadj*self.tscalef)
	    x = x - xadj + xinc

	n = self.dur/tinc
	if not n:
	    n = 1
	y0 = y
	y1 = y0+5

	while x < self.xorg+self.xwi:
	    LINE(x, y0, x, y1, fill=self.fgcol)
        
            if modf(tinc/1000.00)[0] == 0:
                sstr = '%d' % (tm/1000)
            else:
                sstr = '%.3f' % (tm/1000.0)
	    #print sstr
	    TEXT(x, y1, text = sstr, fill=self.fgcol, anchor = N, 
		 font=TIC_FONT)
	    x = x+xinc
	    tm = tm+tinc  
	
###############################################################################
	

    def draw_ytics(self, side, x, base):
	
	LINE = self.canv.create_line
	TEXT = self.canv.create_text
	scalef = self.sscalef
	y = self.yorg
	yinc, sinc = self.calc_ticfacts(self.min_ytic_hi, scalef, self.seqrange)

	# get the first tic to a sensible value
	baseadj = base%sinc
	seq = base-baseadj 
	n = self.seqrange/sinc
	if not n:
	    n = 1

	if side == 'left':
	    xoff = -5
	    anch = E
	    col = S_SEG_COL
	else:
	    xoff = 5
	    anch = W
	    col = C_SEG_COL

	x0 = x
	x1 = x0 + xoff

	for l in range(n+1):
	    LINE(x0, y, x1, y, fill=col)
	    sstr = '%d' % (seq)
	    #print sstr
	    TEXT(x1, y, text = sstr, fill=col, anchor = anch, 
		 font=("helvetica", 8))
	    y = y-yinc
	    seq = seq+sinc

	if side == 'left':
	    # flight canvas
	    #print 'Flight range %d scalef %f' % (self.max_inflight, self.fscalef)
	    lab = 0
	    y = self.flight_yorg
	    yinc = int(self.fscalef)

	    for i in range(self.max_inflight+1):
		LINE(x0, y, x1, y, fill=self.fgcol)
		fstr = '%d' % (i)
		TEXT(x1, y, text=fstr, fill=self.fgcol, anchor=E,  font=TIC_FONT)
		y = y-yinc

	# let caller know base adj required
	return baseadj 
	
##############################################################################

    def draw_rtt_ytics(self):

	LINE = self.canv.create_line
	TEXT = self.canv.create_text

	# LH yaxis for server rtts
	min = self.smin_rtt
	max = self.smax_rtt
	r = max - min
	#print 'srtt min %.3f max %.3f range %.3f' % (min/1000.0, max/1000.0, r/1000.0)
	x0 = self.xaxposl
	x1 = x0-5
	y = self.rtt_yorg
	yinc, sinc = self.calc_ticfacts(self.min_ytic_hi/2, self.srscalef, r)
	#print 'yinc = %d sinc = %d' % (yinc, sinc)

	# get the first tic to a sensible value
	baseadj = min%sinc
	self.smin_rtt_curr = self.smin_rtt-baseadj
	seq = min-baseadj
	n = self.rtt_yht/yinc
	if not n:
	    n = 1
	for l in range(n+1):
	    LINE(x0, y, x1, y, fill=S_SEG_COL, tag='rtt_tic')
	    fstr = '%.3f' % (seq/1000.0)
	    TEXT(x1, y, text=fstr, fill=S_SEG_COL, anchor=E,  
		 font=TIC_FONT, tag='rtt_tic')
	    y = y-yinc
	    seq = seq + sinc


	# RH yaxis for client rtts
	min = self.cmin_rtt
	max = self.cmax_rtt
	r = max - min
	#print 'crtt min %.3f max %.3f range %.3f' % (min/1000.0, max/1000.0, r/1000.0)
	x0 = self.xaxposr
	x1 = x0+5
	y = self.rtt_yorg
	yinc, sinc = self.calc_ticfacts(self.min_ytic_hi/2, self.crscalef, r)
	#print 'yinc = %d sinc = %d' % (yinc, sinc)

	# get the first tic to a sensible value
	baseadj = min%sinc
	self.cmin_rtt_curr = self.cmin_rtt-baseadj
	seq = min-baseadj
	n = self.rtt_yht/yinc
	if not n:
	    n = 1
	for l in range(n+1):
	    LINE(x0, y, x1, y, fill=C_SEG_COL, tag='rtt_tic')
	    fstr = '%.3f' % (seq/1000.0)
	    TEXT(x1, y, text=fstr, fill=C_SEG_COL, anchor=W,  
		 font=TIC_FONT, tag='rtt_tic')
	    y = y-yinc
	    seq = seq + sinc
	
	
###############################################################################

    def draw_rtt_axes(self):

	LINE = self.canv.create_line
        TEXT = self.canv.create_text


	xposl = self.xaxposl
	xposr = self.xaxposr
	ypos = self.rtt_yorg

	LINE(xposr, ypos, xposl, ypos, fill=self.fgcol)

	LINE(xposl, self.rtt_yorg, 
	     xposl, self.rtt_yorg-self.rtt_yht, fill=S_SEG_COL)
	TEXT(xposl, self.rtt_yorg-self.rtt_yht-5, text='server\nprtt ms', 
	     fill=S_SEG_COL, anchor = SE, font=("helvetica", 8))

	LINE(xposr, self.rtt_yorg, 
	     xposr, self.rtt_yorg-self.rtt_yht, fill=C_SEG_COL)
	TEXT(xposr, self.rtt_yorg-self.rtt_yht-5, text='client\nprtt ms', 
	     fill=C_SEG_COL, anchor = SW, font=("helvetica", 8)) 
	

 
	
###############################################################################


	
    def draw_axes(self):

	LINE = self.canv.create_line
        TEXT = self.canv.create_text

	xaxposl = self.xaxposl = self.xorg-5
	xaxposr = self.xaxposr = self.xorg+self.xwi+5
	yaxpos = self.yaxpos = self.yorg+5

	# server y axis
	LINE(xaxposl, self.yorg, xaxposl, self.yorg-self.yht, 
	     fill=S_SEG_COL, width=1)
	TEXT(xaxposl, self.yorg-self.yht-5, text='server\nsequence', 
	     fill=S_SEG_COL, anchor = SE, font=("helvetica", 8))

	# client y axis
	LINE(xaxposr, self.yorg, xaxposr, 
	     self.yorg-self.yht, fill=C_SEG_COL, width=1)
	TEXT(xaxposr, self.yorg-self.yht-5, 
	     text='client\nsequence', fill=C_SEG_COL, 
	     anchor = SW, font=("helvetica", 8))

	# x axis
	LINE(self.xorg, yaxpos, self.xorg+self.xwi, yaxpos, 
	     fill=self.fgcol, width=1)
	TEXT(self.xorg+self.xwi, yaxpos+5, text='ms', fill=self.fgcol, 
	     anchor = NW, font=("helvetica", 8))

	xadj = self.draw_xtics(yaxpos, self.tbase)
	## LINE(self.xorg, yaxpos, self.xorg-xadj, yaxpos, 
## 	     fill=self.fgcol, width=1)
	adj = self.draw_ytics('left', xaxposl, self.sseqbase-self.conn.sseqbase)
	self.sseqbase = self.sseqbase - adj
	adj = self.draw_ytics('right', xaxposr, 
			self.cseqbase-self.conn.cseqbase)
	self.cseqbase = self.cseqbase - adj

	# flight canvas
	LINE(xaxposl, self.flight_yorg, 
	     xaxposl, self.flight_yorg-self.flight_yht, fill=self.fgcol)
	LINE(xaxposl, self.flight_yorg, 
	     xaxposr, self.flight_yorg, fill=self.fgcol)
	TEXT(xaxposl, self.flight_yorg-self.flight_yht-5, 
	     text='pkts in\nflight', 
	     fill=FG_COL1, anchor = SE, font=("helvetica", 8))
	
###############################################################################

    def scale(self):

	hdrlist = self.pktlist
	i1 = self.indx1
	i2 = self.indx2

	hlen = 0
	first = hdrlist[i1].tm
	last = hdrlist[i2].tm
	
	#scale for period - easy pkt list is ordered
	self.tbase = first
	dur = self.dur = last-first
	#print 'Pkt list: first %d last %d dur %d' % (first, last, dur)

	if dur == 0:
	    dur = self.dur = 100

	self.tscalef = self.xwi*1.0/dur

	# scale for seq spaces - may not be ordered
	self.has_sacks = 1
	self.has_cacks = 1
	slowseq = SEQ_MAX
	slowack = SEQ_MAX 
	clowseq = SEQ_MAX 
	clowack = SEQ_MAX 
	stopseq = 0L
	stopack = 0L
	ctopseq = 0L
	ctopack = 0L

	sseq_wraps = 0L
	sack_wraps = 0L
	cseq_wraps = 0L
	cack_wraps = 0L
	srel_wraps = 0L
	crel_wraps = 0L

	sfirstack = -1
	cfirstack = -1

	slastack = None
	clastack = None

	slastrel = None
	clastrel = None

	smax_inflight = 0
	cmax_inflight = 0

	smax_rtt = 0
	smin_rtt = BIGNUMBER
	cmax_rtt = 0
	cmin_rtt = BIGNUMBER

	order = 0
	first = 1

        nomod = not self.modelled

	#print 'Pkts %d-%d:' % (i1, i2)
	for p in hdrlist[i1:i2+1]:
	    #p.printself_rel()
	    #print 'sf = %d cf = %d' % (p.sinflight, p.cinflight)
	    #print '#%d %.3f %.3f' % (p.indx, p.prtt, p.delay)
	    p.order = order
	    order = order+1
	    hlen = hlen + 1
	    seq = p.seq
	    ack = p.ack
	    len = p.len
	    pend = seq+len
	    #pend = seq_add(seq, len)
	    #flags = ord(p.flags)
	    flags = p.flags
	    #print 'seq %s' % (Longstring(seq))
            if nomod:
                p.markers = 0
                p.highrel = None
                p.remark = 0
                p.trig = 0
                p.tstr = ''
                p.prtt = p.trtt = p.delay = 0
                p.lag = None
	    if p.dir == SERVER:
		if not (p.markers & P_SEQ_RTMT):
		    wseq = seq + sseq_wraps
		    pend = wseq + len
		    if seq_add(seq, len) < seq:
			# wrap
			sseq_wraps += SEQ_MAX
		    stopseq = MAX(pend, stopseq)
		    slowseq = MIN(wseq, slowseq)
		else:
		    # NB this probably hasn't been tested
		    wseq = seq
		    while stopseq - wseq > SEQ_MAX/2:
			wseq += SEQ_MAX

		p.wseq = wseq
		    
		if (flags & TH_ACK): # and not (p.markers & P_BOGUS_ACK):
		    if sfirstack == -1:
			sfirstack = ack
		    elif ack < slastack:
			# wrap
			sack_wraps += SEQ_MAX
		    slastack = ack
		    wack = ack + sack_wraps
		    stopack = MAX(wack, stopack)
		    slowack = MIN(wack, slowack)
		    p.wack = wack

                if self.modelled:
                    smax_rtt = MAX(smax_rtt, p.prtt+p.delay)
                    if p.prtt:
                        smin_rtt = MIN(smin_rtt, p.prtt+p.delay)

                if p.highrel != None:
                    if (slastrel != None and p.highrel < slastrel) or p.highrel < p.ack:
                            # wrap
                            srel_wraps += SEQ_MAX
                    slastrel = p.highrel
                    p.wrel = p.highrel + srel_wraps
                else:
                    p.wrel = None
		    

	    elif p.dir == CLIENT:
		if not (p.markers & P_SEQ_RTMT):
		    wseq = seq + cseq_wraps
		    pend = wseq + len
		    if seq_add(seq, len) < seq:
			# wrap
			cseq_wraps += SEQ_MAX
		    ctopseq = MAX(pend, ctopseq)
		    clowseq = MIN(wseq, clowseq)
		else:
		    # NB this probably hasn't been tested
		    wseq = seq
		    while ctopseq - wseq > SEQ_MAX/2:
			wseq += SEQ_MAX

		p.wseq = wseq
		    
		if (flags & TH_ACK): # and not (p.markers & P_BOGUS_ACK):
		    if cfirstack == -1:
			cfirstack = ack
		    elif ack < clastack:
			# wrap
			cack_wraps += SEQ_MAX
		    clastack = ack
		    wack = ack + cack_wraps
		    ctopack = MAX(wack, ctopack)
		    clowack = MIN(wack, clowack)
		    p.wack = wack

                if self.modelled:
                    cmax_rtt = MAX(cmax_rtt, p.prtt+p.delay)
                    if p.prtt:
                        cmin_rtt = MIN(cmin_rtt, p.prtt+p.delay)

                if p.highrel != None:
                      #if p.highrel < clastrel:
                    if (clastrel != None and p.highrel < clastrel) or p.highrel < p.ack:
                        # wrap
                        crel_wraps += SEQ_MAX
                    clastrel = p.highrel
                    p.wrel = p.highrel + crel_wraps
                else:
                    p.wrel = None

	    else:
		errstr = 'Goof - unknown pkt direction %d' % (p.dir)
		sys.stderr.write(errstr)
		sys.exit(0)

	    smax_inflight = MAX(p.sinflight, smax_inflight)
	    cmax_inflight = MAX(p.cinflight, cmax_inflight)

	if slowseq == BIGNUMBER:
	    slowseq = clowack
	    stopseq = ctopack
	if slowack == BIGNUMBER:
	    slowack = clowseq
	    stopack = ctopseq
	    self.has_sacks = 0
	if clowseq == BIGNUMBER:
	    clowseq = slowack
	    ctopseq = stopack
	if clowack == BIGNUMBER:
	    clowack = slowseq
	    ctopack = stopseq
	    self.has_cacks = 0

	self.sseqbase = MIN(clowack, slowseq)
	self.cseqbase = MIN(slowack, clowseq)

	sseqhigh = MAX(stopseq, ctopack)
	cseqhigh = MAX(ctopseq, stopack)

	self.hdrlist_len = hlen
	self.slowseq = slowseq
	self.stopseq = stopseq
	self.slowack = slowack
	self.stopack = stopack
	self.sseqrange = sseqhigh - self.sseqbase
	self.sackrange = stopack - slowack
	self.clowseq = clowseq
	self.ctopseq = ctopseq
	self.clowack = clowack
	self.ctopack = ctopack
	self.cseqrange = cseqhigh - self.cseqbase
	self.cackrange = ctopack - clowack

	self.sfirstack = sfirstack
	self.cfirstack = cfirstack

	self.seqrange = MAX(self.sseqrange, self.cseqrange)
	#self.ackrange =  MAX(self.sackrange, self.cackrange)
	#self.seqspace = MAX(self.seqrange, self.ackrange)

## 	if self.seqspace == 0:
## 	    self.seqspace = 100

	self.seqrange = MAX(self.seqrange,100)

	if not smax_rtt:
	    smax_rtt = 100
	self.smax_rtt = smax_rtt

	if smin_rtt == BIGNUMBER:
	    smin_rtt = 1
	self.smin_rtt = smin_rtt

	if not cmax_rtt:
	    cmax_rtt = 100
	self.cmax_rtt = cmax_rtt

	if cmin_rtt == BIGNUMBER:
	    cmin_rtt = 1
	self.cmin_rtt = cmin_rtt
	#print 'CMINRTT %d' % (cmin_rtt)

## 	print 'server seq %s-%s = %s' % (Longstring(slowseq), 
## 				     Longstring(stopseq), 
## 				     Longstring(self.sseqrange))
## 	print 'client seq %s-%s = %s' % (Longstring(clowseq), 
## 				     Longstring(ctopseq), 
## 				     Longstring(self.cseqrange))

## 	print 'server ack %s-%s = %s' % (Longstring(slowack), 
## 				     Longstring(stopack), 
## 				     Longstring(self.sackrange))
## 	print 'client ack %s-%s = %s' % (Longstring(clowack), 
## 				     Longstring(ctopack), 
## 				     Longstring(self.cackrange))

	self.sscalef = self.yht*1.0/self.seqrange

	## for hdr in hdrlist:
## 	    hdr.printself_rel()

	self.cmax_inflight = cmax_inflight
	self.smax_inflight = smax_inflight
	self.max_inflight = MAX(cmax_inflight, smax_inflight)
	self.max_inflight = MAX(self.max_inflight, 1)

	self.fscalef = self.flight_yht*1.0/self.max_inflight

	#print 'srtt min %.3f max %.3f range %.3f' % (smin_rtt/1000.0, smax_rtt/1000.0, (smax_rtt-smin_rtt)/1000.0)
	#print 'crtt min %.3f max %.3f range %.3f' % (cmin_rtt/1000.0, cmax_rtt/1000.0, (cmax_rtt-cmin_rtt)/1000.0)

	try:
	    self.srscalef = float(self.rtt_yht-self.min_ytic_hi/2)/(smax_rtt-smin_rtt)
	except ZeroDivisionError:
	    # max-min range is 0 - only one packet
	    hrange = smax_rtt/5
	    self.srmax_rtt = smax_rtt+hrange
	    self.srmin_rtt = smin_rtt-hrange
	    self.srscalef = float(self.rtt_yht-self.min_ytic_hi/2)/(hrange*2)
	try:
	    self.crscalef = float(self.rtt_yht-self.min_ytic_hi/2)/(cmax_rtt-cmin_rtt)
	except ZeroDivisionError:
	    # max-min range is 0 - only one packet
	    hrange = cmax_rtt/5
	    self.crmax_rtt = cmax_rtt+hrange
	    self.crmin_rtt = cmin_rtt-hrange
	    self.crscalef = float(self.rtt_yht-self.min_ytic_hi/2)/(hrange*2)
	
###############################################################################
	    

    def draw_blob(self, x, y, col, tag=''):

	RECT = self.canv.create_rectangle

	return RECT(x-2, y-2, x+2, y+2, outline='', fill=col, tag=tag) 
	
###############################################################################


    def draw_cross(self, x, y, col, indx):
	
	LINE = self.canv.create_line
	TEXT = self.canv.create_text
	LINE(x-5, y-5, x+5, y+5, fill=col)
	TEXT(x+5, y+5, anchor=NW, fill=col, font=PKT_LABEL_FONT, 
	     text='%d' % (indx))
	return LINE(x-5, y+5, x+5, y-5, fill=col) 
	
###############################################################################


    def draw_point(self, x, y, col, indx):

	OVAL = self.canv.create_oval
	TEXT = self.canv.create_text
	
	if indx != None:
	    TEXT(x+5, y+5, anchor=NW, fill=col, font=PKT_LABEL_FONT, 
	     text='%d' % (indx))

	return OVAL(x-2, y-2, x+2, y+2, outline=col, fill=col) 
	
###############################################################################


    def draw_vtic(self, x, y, col, indx):

	LINE = self.canv.create_line
	TEXT = self.canv.create_text
	
## 	if indx != None:
## 	    TEXT(x+5, y+5, anchor=NW, fill=col, font=("helvetica", 10), 
## 	     text='%d' % (indx))

	return LINE(x, y-2, x, y+2, fill=col)
	    

##############################################################################
##############################################################################

#
# Window displaying graph of TCP connection activity
#

class TCP_Window(Toplevel):

    def __init__(self, conn, Display, root=None):

	Toplevel.__init__(self)
	self.root = root
	self.Display = Display
	self.canvasses = []

	self.draw_conn(conn)
 	geomstr = '%dx%d' % (TCP_WDW_WI,TCP_WDW_HT)
	self.geometry(geomstr)

##############################################################################

    def gone(self):
	##if self.root != None:
	    #self.root.destroy()
	# Comment out following line when using np_tcp.py
	self.destroy()

##############################################################################


    def mouse_3_dblclick(self, event):
	# pop canvas zoom stack
	canv = event.widget
	x = canv.canvasx(event.x)
	y = canv.canvasy(event.y)

	Canv = canv.Canv

	if inside(x, y, Canv.mouse_bounds):
	    if self.canvas_curr > 0:
		self.canvasses[self.canvas_curr].canv.place_forget() 
		self.canvas_curr = self.canvas_curr - 1 
		self.canvasses.pop()
		# re-show previous in stack
		curr = self.canvasses[self.canvas_curr]
		curr.canv.place(x=curr.xpos, y=curr.ypos, width=curr.wi, 
				height=curr.ht)
	elif inside(x, y, Canv.rtt_bounds):
	    Canv.rtt_scaledown(RTT_ZOOM_FACT)

##############################################################################

    def clear_canvasses(self):

	for c in self.canvasses:
	    #c.canv.place.forget()
	    c.canv.delete(ALL)
	    del(c)

	self.canvasses = []
	self.clear_titlebar()
	
############################################################################

    def set_titlebar(self, conn):

	tstr = 'James pretty picture of connection #%d' % (conn.id)
	self.winfo_toplevel().title(tstr)
	

############################################################################

    def clear_titlebar(self):

	tstr = ''
	self.winfo_toplevel().title(tstr)
	

############################################################################

	
    def draw_conn(self, conn):

	if len(self.canvasses):
	    self.clear_canvasses()

	#if conn == None:
	if not conn:
	    return

	self.conn = conn
	self.conn.sseqbase = MIN(self.conn.slowseq, self.conn.clowack)
	self.conn.cseqbase = MIN(self.conn.clowseq, self.conn.slowack)
	self.pktlist = conn.pktlist
	self.pktlist_len = len(conn.pktlist)

	self.calc_inflights(self.pktlist) # does for all zooms

	self.canvasses = []
	self.canvasses_tot = 1
	self.canvas_curr = 0
	self.canvasses.append(TCP_canv(self, conn, self.pktlist,
				       0, self.pktlist_len-1,
				       TCP_WDW_WI, TCP_WDW_HT, 
				       0, 0))
	self.set_titlebar(conn)

##############################################################################

    #
    # For each pkt how many pkts are in flight (seen but not acked)
    # - invariant over zooms
    #
    def calc_inflights(self, plist):

	sinflight = 0
	cinflight = 0

	sinflight_min = 0
	cinflight_min = 0

## 	slowseq = BIGNUMBER
## 	clowseq = BIGNUMBER

## 	slowack = BIGNUMBER
## 	clowack = BIGNUMBER

	slastacked_indx = 0
	clastacked_indx = 0

	

	for i in range(len(plist)):
	    p = plist[i]
	    #p.indx = i # tag with indx for zoom selection
	    way = p.dir
	    #flags = ord(p.flags)
	    flags = p.flags
	    plen = p.len
	    if flags & TH_FIN or flags & TH_SYN:
		plen = plen + 1
	    if plen:
		p.needs_ack = 1
	    else:
		p.needs_ack = 0
	    if way == SERVER:
		if plen:
		    sinflight = sinflight+1
		if flags & TH_ACK:
		    ack = p.ack
		    on = 0
		    cleared = 0
		    # ack may be for more than one pkt
		    for pkt in plist[clastacked_indx:i]:
			#on = on+1
			pway = pkt.dir
			if pway == CLIENT:
			    if pkt.seq+pkt.len <= ack:
				on = on+1
				if pkt.needs_ack:
				    pkt.needs_ack = 0
				    cleared = cleared+1
			    else:
				break
		    clastacked_indx = clastacked_indx + on
		    cinflight = cinflight - cleared
	    if way == CLIENT:
		if plen:
		    cinflight = cinflight+1
		if flags & TH_ACK:
		    ack = p.ack
		    on = 0
		    cleared = 0
		    # ack may be for more than one pkt
		    for pkt in plist[slastacked_indx:i]:
			#on = on+1
			pway = pkt.dir
			if pway == SERVER:
			    if pkt.seq+pkt.len <= ack:
				on = on+1
				if pkt.needs_ack:
				    pkt.needs_ack = 0
				    cleared = cleared+1
			    else:
				break
		    slastacked_indx = slastacked_indx + on
		    sinflight = sinflight - cleared

	    
	    p.sinflight = sinflight
	    p.cinflight = cinflight

	    if sinflight < sinflight_min:
		sinflight_min = sinflight
	    if cinflight < cinflight_min:
		cinflight_min = cinflight

	#
	# Acks arriving for pkts in flight before trace start will have forced
	# inflights negative - patch to reflect pkts in flight at start
	#
	if sinflight_min < 0 or sinflight_min < 0:
	    sadd = -sinflight_min
	    cadd = -cinflight_min

## 	    for p in plist:
## 		p.sinflight = p.sinflight+sadd
## 		p.cinflight = p.cinflight+cadd

				
				
			    
		    
		    
