###############################################################################
#                                                                             #
#   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 copy import copy
from string import ascii_letters

from nprobe import DUMMY_VALUE, LR_TS, LR_SCRIPTED, LR_ARCHIVE, LR_INLINE, \
     LR_LINK, LR_UNKNOWN, LR_REL_BASE, LR_END_BASE, LR_REDIRECT, \
     LR_SCRIPT_ARCHIVE, LR_SCRIPTED_LINK, LR_SCRIPTED_INLINE, \
     LR_SCRIPTED_UNKNOWN, LR_REL_SCRIPT_BASE, LR_END_SCRIPT_BASE, \
     LR_REDIRECT_INLINE, LR_REDIRECT_LINK, LR_HAS_URL
from nprobe import intoa_string, http_server_objtype_string, \
     http_client_method_string, http_server_returncode_string, http_errstring
from nprobe import TCP_SERV_SYN, TCP_CLI_SYN, TCP_SERV_HTTP, SERVER, CLIENT, \
     TRANS_VAL, TRANS_DUMMY_UNSYNCH, TRANS_DUMMY_ERR, TRANS_ERR, \
     TRANS_INCOMPLETE, TRANS_FINISHED
from nprobe import TSP_SYN, TSP_DUP_SYN, TSP_FIN, TSP_ACKSYN, TSP_RST, \
     CT_NON_OBJECT, CT_UNKNOWN, CT_UNSYNCH, CT_POST_ERR, CT_OTHER
from nprobe import TCP_CONN_ERROR, TCP_TIMEO, TCP_FORCED_ALT, TCP_QUICK_CLOSE,\
     TCP_RUNEND, \
     TSP_SEQ_TIMEO, TSP_SEQ_TIMEO_FORCED, TSP_SEQ_TIMEO_QL, \
     TSP_SEQ_TIMEO_ACK, TSP_FORCED_ALT, TSP_FORCED_OPEN, TSP_FORCED_CLOSE
from np_WebHost import LR_INLINE_MULTI, LR_REFERRER_INLINE, LR_REFERRER_LINK, \
     LR_FAIL, LR_FOLLOW_ON, DUM
from np_WebHost import EV_NSOP, EV_NSCLO, EV_CONNOP, EV_CONN_CPKT, \
     EV_CONN_CSYN,EV_CONN_SPKT, EV_CONN_SSYN, EV_CONN_CACKSYN, \
     EV_CONN_SACKSYN, EV_REQBEGIN, EV_REQSTART, EV_REQEND, EV_REPSTART, \
     EV_REPEND, EV_REQFINISH, EV_CONN_CFIN, EV_CONN_SFIN, EV_CONN_CRST, \
     EV_CONN_SRST, EV_CONNCLO, EV_DELREPEND
#from np_obnode import TH_SYN, TH_RST, TH_FIN, LinkDict, TreeError
from np_WebHost import TH_SYN, TH_RST, TH_FIN, LinkDict, TreeError
#from np_lookups import np_hostname, cache_lookups
#from np_TCPDisplay import TcpDisplay
from np_longutil import tsDatestring

from np_drawutil import P_AFTER, P_BEFORE
from bsearch import NotInRange

from print_col import inform

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

#
# Gap types
#

GAP_CONNS = 0
GAP_REQS = 1
GAP_TM = 2

G_BEFORE = 0
G_AFTER = 1

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



class ObTree:
	
    def __init__(self, client, lookup=intoa_string, trace=0):

	self.client = client
        self.Class = client.Class
	self.attach_colours()
        self.attach_marktypes_dict()
        self.LinkDict = LinkDict().dict
        self.nslookup = lookup
	self.nwarnings = 0
        client.make_tree(caller=self, trace=trace)
        self.tree_trans = []
        self.tree_conns = []
        self.pktdict = {}
        self.rooturl = None
        #self.xrefs()
	self.start = client.start
        self.abstart = client.abstart
	self.end = client.end
	self.build_tdict()
        self.build_agentsdict()


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

    def __str__(self):

        return 'Obtree for %s %s' % (self.client.Class,
                                          self.client.addr_str())


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


    def xrefs(self):

        #
        # Provide cross references needed for drawing
        #
        conndict = {}
        for c in self.client.connlist:
            conndict[c.id] = c

        for t in self.client.translist:
            t.TConn = conndict[t.connid]


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

    def calc_yinc(self):
        
	mindur = self.client.mindur
	scalef = self.calc_scalef(mindur)
	#min_show_dur = int(self.mingapx/scalef)

	#self.calc_gaps(self.client.eventlist, mindur, min_show_dur)
	self.calc_gaps(self.client.eventlist)


	n = len(self.client.connlist)
	if len(self.client.dummytrans):
	    n += 1

        scaleht = min(len(self.gaps), self.gap_text_steps)*self.gap_text_ysep + self.toplabely
        #print self.draw
        tbb = self.draw.bbox(self.draw.create_text(0, 0, fill='white',
                          anchor=E, text = ' ', font=self.font10))
        scaleht += tbb[3]-tbb[1]+10

	ht = self.screen.dframe_ht - scaleht -self.topmargin - self.bottommargin

	connht = max(self.connht, ht/n)
	self.conncentre = connht/2
	self.connht = connht
        self.scaleht = scaleht
        
##         print 'scaleht %d' % (scaleht)
##         print 'drawht %d' % (ht)


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

    def calc_vyinc(self):

	n = len(self.client.connlist)
	if len(self.client.dummytrans):
	    n += 1

	ht = self.screen.dframe_ht - self.screen.tscalecanv_ysz -self.topmargin - self.bottommargin

	connht = max(self.vconnht, ht/n)
	self.vconncentre = connht/2
	self.vconnht = connht

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

    def calc_scalef(self, mindur):
	# Set a scale factor so the shortest duration is decently visible
	min_x = float(self.min_x)
	self.draw.scalef = scalef = min_x/mindur

	return scalef

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

    def calc_scalef_v(self, maxdur):
	## # Set a scale factor so the shortest timespan is decently visible
## 	min_x = float(self.min_viewx)
## 	self.view.scalef = scalef = min_x/self.mindur

## 	xrange = int(self.end - self.start)
## 	if xrange*scalef < self.scaleup_minx:
## 	    scalef = self.scaleup_minx/float(xrange)
	
## 	self.view.scalef = scalef
## 	return scalef

	# Set a scale factor so the longest timespan is decently visible
	max_x = float(self.max_viewx)
	self.view.scalef = scalef = max_x/maxdur

        # but check it doesn't result in an over-large canvas (ie. where short
        # duration events are widely separated)

        dur = self.end - self.start
        if dur*scalef > self.max_canv_sz:
            self.view.scalef = scalef = self.max_canv_sz/float(dur) 

	
	self.view.scalef = scalef
	return scalef

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

    #def calc_gaps(self, eventlist, mindur, min_show_dur):
    def calc_gaps(self, eventlist):

	gaps = []
	tot_gap = 0
	ngaps = 0
	current_reqs = []
	current_conns = []
	current_ns = []
	last_etm = 0

	#print 'start %s' % (tsLongstring(self.client.start))

	first = 0
	first_tm = eventlist[0][0]
        last_etm = first_tm #?
	mingap = self.client.mindur
        mindur = mingap
	mintm = int(self.mingapx/self.draw.scalef)
	#print 'events:'
	for evno in range(len(eventlist)):
	    e = eventlist[evno]
	    tm = e[0]
	    type = e[1]
	    ob = e[2]

            #print last_etm, tm

	    #print 'event time %s type %d' % (tsLongstring(tm), type)
	    
	    # any compacting possible?
            try:
                # note any ver long gap
                sincelast =  int(tm - last_etm)
            except OverflowError:
                inform('%s Large Sincelast: last_etm=%d, tm=%d, ev No %d, type %d' % (self.nslookup(self.client.addr), last_etm, tm, evno, type))
                sincelast =  tm - last_etm
                
	    if first:
		minleft = mindur
	    else:
		minleft = mintm
	    #if sincelast > mingap:
	    gap = sincelast - minleft
	    if gap > mingap:
		gap_tm = last_etm + minleft/2
		if not len(current_conns) and not len(current_ns):
		    gaptype = GAP_CONNS
		elif not len(current_reqs):
		    gaptype = GAP_REQS
		else:
		    gaptype = GAP_TM
		
		gaps.append([gaptype, gap_tm, gap, 0])
		tot_gap = tot_gap + gap
		ngaps = ngaps + 1
		#print 'internal gap %9d %9d' % (gap_tm/1000, gap/1000)
		for c in current_conns:
		    c.gaps.append((gap_tm, gap))
		    c.totgap = c.totgap + gap
		for n in current_ns:
		    n.gaps.append((gap_tm, gap))
		    n.totgap = n.totgap + gap
		for r in current_reqs:
		    r.gaps.append((gap_tm, gap))
		    r.totgap = r.totgap + gap

	    if tm > first_tm + mindur:
		first = 0


	    if type == EV_REQBEGIN:
		first = 1
		first_tm = tm
		if len(current_conns) == 0 and ob.sobtype != CT_UNSYNCH:
		    # Sanity - must be at least one in progress
		    self.whoops('Req starting but no conn in progress! tm = %d' % (tm/1000))
		    #sys.exit(1)
		current_reqs.append(ob)
		ob.gaps = []
		ob.totgap = 0
		ob.gap_before = tot_gap
		ob.ngaps_before = ngaps
		
	    elif type == EV_REQSTART:
		# req start
		ob.reqstart_gap_before = tot_gap
		ob.reqstart_ngaps_before = ngaps

	    elif type ==  EV_REQEND:
		ob.reqend_gap_before = tot_gap
		ob.reqend_ngaps_before = ngaps 

	    elif type == EV_REPSTART:
		ob.repstart_gap_before = tot_gap
		ob.repstart_ngaps_before = ngaps
		    
	    elif type == EV_REPEND:
		ob.repend_gap_before = tot_gap
		ob.repend_ngaps_before = ngaps
		    
	    elif type == EV_DELREPEND:
                #print 'XXXXEV_DELREPEND'
		ob.delrepend_gap_before = tot_gap
		ob.delrepend_ngaps_before = ngaps

	    elif type == EV_REQFINISH:
		current_reqs.remove(ob)

	    elif type == EV_CONNOP:
		# conn open
		current_conns.append(ob)
		ob.gap_before = tot_gap
		ob.ngaps_before = ngaps
		ob.gaps = []
		ob.totgap = 0
	    elif type == EV_CONNCLO:
		# conn close
		current_conns.remove(ob)
		if len(current_conns) == 0:
		    # Sanity s/be no reqs
		    if len(current_reqs):
			self.whoops('Last conn finished but still live reqs! tm = %d %d reqs' % (tm/1000, len(current_reqs)))
	    elif type == EV_NSOP:
		# ns req
		current_ns.append(ob)
		ob.gap_before = tot_gap
		ob.ngaps_before = ngaps
		ob.gaps = []
		ob.totgap = 0
	    elif type == EV_NSCLO:
		# ns rep
		current_ns.remove(ob)

	    elif type == EV_CONN_CSYN:
		ob.csyn_gap_before = tot_gap
		ob.csyn_ngaps_before = ngaps
	    elif type == EV_CONN_SSYN:
		ob.ssyn_gap_before = tot_gap
		ob.ssyn_ngaps_before = ngaps

	    elif type == EV_CONN_CACKSYN:
		ob.cacksyn_gap_before = tot_gap
		ob.cacksyn_ngaps_before = ngaps
	    elif type == EV_CONN_SACKSYN:
		ob.sacksyn_gap_before = tot_gap
		ob.sacksyn_ngaps_before = ngaps

	    elif type == EV_CONN_CRST:
		ob.crst_gap_before = tot_gap
		ob.crst_ngaps_before = ngaps
	    elif type == EV_CONN_SRST:
		ob.srst_gap_before = tot_gap
		ob.srst_ngaps_before = ngaps
	    elif type == EV_CONN_CFIN:
		ob.cfin_gap_before = tot_gap
		ob.cfin_ngaps_before = ngaps
	    elif type == EV_CONN_SFIN:
		ob.sfin_gap_before = tot_gap
		ob.sfin_ngaps_before = ngaps
	    elif type == EV_CONN_SPKT:
		ob.pkt_gap_before = tot_gap
		ob.pkt_ngaps_before = ngaps
	    elif type == EV_CONN_CPKT:
		ob.pkt_gap_before = tot_gap
		ob.pkt_ngaps_before = ngaps
	    else:
		print 'WHOOPS - non-existant event type %d' % (type)
		sys.exit(1)
		

	    last_etm = tm

            self.gaps = gaps
            self.ngaps = ngaps
            self.tot_gap = tot_gap

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

    def scale_canvas(self):

	eventlist = self.client.eventlist
	mindur = self.client.mindur
	scalef = self.calc_scalef(mindur)

        sys.stdout.flush()
	#self.draw.xrange = int(self.end - self.start)
	self.draw.xrange = self.end - self.start
	self.draw.yrange = len(self.client.connlist)*self.connht
	#allow space for any implied objects
 	if len(self.client.implied_obs):
	    self.draw.yrange += self.connht

	#print'o/a time: %d' % (xrange/1000)
        self.draw.scalef = scalef
	self.draw.xrange -= self.tot_gap

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

    def scale_canvas_v(self):


	scalef = self.calc_scalef_v(self.client.maxdur)

	#print 'start: %s / %d' % (Longstring(self.start), self.start-self.start)
	#print 'end: %s / %d' % (Longstring(self.end), int(self.end - self.start)/1000)
    
	#self.view.xrange = int(self.end - self.start)
	self.view.xrange = self.end - self.start
	self.view.yrange = len(self.client.connlist)*self.connht + self.toplabely + self.bottommargin
        self.view.scalef = scalef
 	# allow space for any implied objects
 	if len(self.client.implied_obs):
	    self.view.yrange += self.connht

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

    def label_conn(self, canv, conn, xfront, x, xback, y, dist):

	TEXT = canv.create_text
	flags = conn.flags
	cflags = conn.cflags
	sflags = conn.sflags

	if flags & TCP_CONN_ERROR:
	    error = 1
	    colour = 'red'
	else:
	    error = 0
	    colour = "blue"

        if not dist:
            colour = colour1 = 'grey72'
        else:
            colour1 = 'DarkOrchid1'

        #
	#label host(s)
        #
        if self.Class == 'WebClient':
            serverstr = '%s\n' % (self.nslookup(conn.server))
        elif self.Class == 'WebServer':
            serverstr = '%s\n' % (self.nslookup(conn.client))
        else:
            serverstr = '%s/%s\n' % (self.nslookup(conn.client),
                                     self.nslookup(conn.server))
        
	if conn.http_status & 0x40000:
	    #persistent connection
	    serverstr = serverstr + ' P '
	serverstr = serverstr + conn.http_verstr
	t = TEXT(xfront, y+4, fill=colour, anchor=E, 
			      text=serverstr, font = self.font10)

        # N conns previously open
        t = TEXT(canv.bbox(t)[0]-10, y+4, fill=colour1, anchor=E, font=self.font12bold,
             text='PO: %d/%d/%d ' % (conn.cop1, conn.cop2, conn.cop3))

        # Conn id etc.
        TEXT(canv.bbox(t)[0]-10, y+4, fill=colour, anchor=E, font=self.font12bold,
             text='  %d (%d)' % (conn.indx, conn.id))

	# label times
	optmstr = '%d' % (int(conn.open/1000))
	TEXT(x, y + 2, fill=colour, anchor=NE, 
			      text=optmstr, font = self.font10)

	clostr = '(+%d) C: %d/%d S: %d/%d ' % \
		 (int(conn.dur/1000), conn.cbytes, conn.cpkts, 
		  conn.sbytes, conn.spkts)
	if error:
	    clostr = clostr + 'ERROR '
	if flags & TCP_TIMEO :
	    clostr = clostr + 'TIMED OUT '
	if flags & TCP_FORCED_ALT:
	    clostr = clostr + 'ALT FORCED '
	if flags & TCP_QUICK_CLOSE:
	    clostr = clostr + 'QUICK CLOSE '
	if flags & TCP_RUNEND:
	    clostr = clostr + 'END OF RUN '
	if cflags & (TSP_SEQ_TIMEO | TSP_SEQ_TIMEO_FORCED | TSP_SEQ_TIMEO_QL \
		     | TSP_SEQ_TIMEO_ACK):
	    clostr = clostr + 'C SEQ TIMEO '
	if sflags & (TSP_SEQ_TIMEO | TSP_SEQ_TIMEO_FORCED | TSP_SEQ_TIMEO_QL \
		     | TSP_SEQ_TIMEO_ACK):
	    clostr = clostr + 'S SEQ TIMEO '
	if cflags & TSP_FORCED_ALT:
	    clostr = clostr + 'C FORCED ALT '
	if sflags & TSP_FORCED_ALT:
	    clostr = clostr + 'S FORCED ALT '
	if cflags & TSP_FORCED_OPEN:
	    clostr = clostr + 'C FORCED OPEN '
	if sflags & TSP_FORCED_OPEN:
	    clostr = clostr + 'S FORCED OPEN '
	if cflags & TSP_FORCED_CLOSE:
	    clostr = clostr + 'C FORCED CLOSE '
	if sflags & TSP_FORCED_CLOSE:
	    clostr = clostr + 'S FORCED CLOSE '

	t = TEXT(xback+2, y, fill=colour, anchor=W, 
			      text=clostr, font = self.font10)
        if canv == self.draw:
            conn.maxx = max(conn.maxx, canv.bbox(t)[2])
        else:
            conn.maxvx = max(conn.maxvx, canv.bbox(t)[2])
        
#############################################################################

    def label_req(self, canv, req, xreqstart, xreqend, xrepstart, 
			   xrepend, y0, yc, y1, dist):

	def checkstr(s):
	    ok = 1
	    for c in s:
		if not 31 < ord(c) < 127:
		    ok = 0
		    break;

	    if ok:
		return s
	    else:
		return 'XXX'

	cstatus = req.cstatus 
	sstatus = req.sstatus
	TEXT = canv.create_text
        TConn = req.TConn
	dur = req.reqdur + req.repdur

        if canv == self.draw:
            try:
                reqlabelstart = TConn.reqlabelstart + 20
                replabelstart = max(TConn.replabelstart + 20, reqlabelstart)
            except AttributeError: # first label
                reqlabelstart = xreqstart
                replabelstart = xrepstart
        elif canv == self.view:
            try:
                reqlabelstart = TConn.vreqlabelstart + 20
                replabelstart = max(TConn.vreplabelstart + 20, reqlabelstart)
            except AttributeError: # first label
                reqlabelstart = xreqstart
                replabelstart = xrepstart
  
        if not replabelstart:
            replabelstart = reqlabelstart

	## if req.cisdummy or req.sisdummy:
## 	    print 'XXXXXXX DUMMY XXXXX'
	
	if req.cvalid and not req.cisdummy:
	    tmbase = req.reqstart
	    #label request
            if not dist:
                ccolour = 'grey72'
	    elif req.cerr:
		ccolour = 'red'
		errstr = '(%d)' % (req.cerr)
		obstr = checkstr(req.obname)
	    else:
		ccolour = 'black'
		errstr = ''
		#obstr = req.obname
		obstr = req.absurl
                
            if canv == self.draw:
                count_mark = 1
            else:
                count_mark = 0
            marked = 0
            if req.reflink:
                refstr = '%d' % (req.reflink.trans.order)
                if req.reflink.type & LR_REDIRECT:
                    self.draw_mark(canv, 'Re-directed', reqlabelstart-5, y0-15,
                                   count=count_mark)
            else:
                refstr = '?'
                if not len(req.downlinks):
                    self.draw_mark(canv, 'Unlinked', reqlabelstart-5, y0-15,
                                   count=count_mark)
                    marked = 1

            if req.isfail:
                if marked:
                    self.draw_mark(canv, 'Transaction failed',
                                   reqlabelstart-5, y0-15, count=count_mark,
                                   rh=1)
                else:
                    self.draw_mark(canv, 'Transaction failed',
                                   reqlabelstart-5, y0-15, count=count_mark)
                    
            t = TEXT(reqlabelstart, y0-4, fill=ccolour, anchor=SW, 
                     text='%d (<-%s)' % (req.order, refstr),
                     font = self.font10bold)

            # if distinguished tree root mark it
            if self.rooturl and self.rooturl[0] == req.absurl:
                bb = canv.bbox(t)
                canv.create_rectangle(bb[0], bb[1], bb[2], bb[3],
                                      outline='red')

            if dist:

                uagent = req.uagent
                if uagent == None:
                    uagent = 'Unknown'
                labelstr = '(%s) %s %s %s' % (self.get_agentdict(uagent), 
                                 http_client_method_string(req.reqtype), 
                                      obstr, errstr)
                t = TEXT(canv.bbox(t)[2] + 4, y0-4, fill=ccolour, anchor=SW, 
                                      text=labelstr, font = self.font10)

                # label times
                TEXT(xreqstart, y1 + 2, fill=ccolour, anchor=N, 
                     text='%d' % (tmbase/1000), 
                     font = self.font10)

                if req.reqend != tmbase:
                    TEXT(xreqend, y1+2, fill=ccolour, anchor=N, 
                         text='+%d' % ((req.reqend - tmbase)/1000), 
                         font = self.font10)

        elif dist:
            # not valid etc
            t = TEXT(reqlabelstart, y0-4, fill='red', anchor=SW, 
                     text='Client request invalid',
                     font = self.font10)
        else:
            # just empty text for positioning
            t = TEXT(reqlabelstart, y0-4, anchor=SW, 
                     text=' ',
                     font = self.font10)
            

        end = canv.bbox(t)[2]
        if canv == self.draw:
            # don't know if we have a value yet
            try:
                maxx = TConn.maxx
            except AttributeError:
                maxx = 0
            TConn.maxx = max(maxx, end)
            TConn.reqlabelstart = end
        else:
            try:
                maxvx = TConn.maxvx
            except AttributeError:
                maxvx = 0
            TConn.maxvx = max(maxvx, end)
            TConn.vreqlabelstart = end

	if req.svalid and not req.sisdummy:
	    if not req.cvalid:
		tmbase = req.repstart
		repstart_pre = ''
	    else:
		tmbase = req.reqstart
		repstart_pre = '+'

	    #label response
            if not dist:
                scolour = 'grey72'
	    elif req.serr:
		scolour = 'red'
		errstr = '(%s)' % (req.serr)
	    else:
		scolour = 'black'
		errstr = ''
                

	    if not sstatus & TRANS_INCOMPLETE: #object complete
		cstr = 'C'
	    elif sstatus & TRANS_FINISHED: #transaction finished - eg redirected, not mod, etc
		cstr = 'F'
	    else:
		cstr = '*'

            if dist:

                server = req.server
                if server == None:
                    server = 'Unknown'
                labelstr = '(%s) %s (%d) %d/%d %s' % \
                           (self.get_agentdict(server), 
                            cstr, req.retcode,  
                            req.nbytes, req.npkts, errstr)
                t = TEXT(replabelstart, yc-1, fill=scolour, anchor=SW, 
                                      text=labelstr, font = self.font10)

                # label times

                TEXT(xrepstart, y1+2, fill=scolour, anchor=N, 
                        text='%s%d' % \
                     (repstart_pre, (req.repstart - tmbase)/1000), 
                     font = self.font10)
                
                if req.repend != req.repstart:
                    TEXT(xrepend, y1+2, fill=scolour, anchor=N, 
                         text='+%d' % ((req.repend - tmbase)/1000), 
                         font = self.font10)
            else:
                t = TEXT(replabelstart, yc-1, fill=scolour, anchor=SW, 
                                      text=cstr, font = self.font10)

        elif dist:
            # not valid etc - might need these next time round
            t = TEXT(replabelstart, yc-1, fill='red', anchor=SW, 
                     text='Server response invalid',
                     font = self.font10)
        else:
            # just empty text for positioning
            t = TEXT(replabelstart, yc-1, anchor=SW, 
                     text=' ',
                     font = self.font10)

        # record text end for next label op
        end = canv.bbox(t)[2]
        if canv == self.draw:
            # don't know if we have a value yet
            try:
                maxx = TConn.maxx
            except AttributeError:
                maxx = 0
            TConn.maxx = max(maxx, end)
            TConn.replabelstart = end
        else:
            try:
                maxvx = TConn.maxvx
            except AttributeError:
                maxvx = 0
            TConn.maxvx = max(maxvx, end)
            TConn.vreplabelstart = end 

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

    def label_lookup(self, canv, lookup, xfront, xback, y):
	TEXT = canv.create_text
	colour="red"

	# label times
	reqtmstr = '%d' % (lookup.reqtm/1000)
	TEXT(xfront, y + 2, fill=colour, anchor=NE, 
			      text=reqtmstr, font = self.font10)

	reptmstr = '+%d' % (lookup.dur/1000)
	TEXT(xback+2, y + 2, fill=colour, anchor=NW, 
			      text=reptmstr, font = self.font10)
	


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

    
    def draw_conns(self):

	tconns = self.client.connlist
	canv = self.draw
	scalef = canv.scalef
	LINE = canv.create_line

	topy = self.draw.topy
	if len(self.client.dummytrans):
	    #topy = topy+self.impobjht
	    topy = topy+self.connht

	canv.topconns = topy

        if self.pktdict:
            pd = 0
        else:
            pd = 1
            pktdict = self.pktdict

            
        pktdict = self.pktdict

	for conn in tconns:

            #if pd:
                #for p in conn.pktlist:
                    #pktdict[conn, p.tm] = p

            # in distinguished tree?
            if self.rooturl and not (conn in self.tree_conns):
                dist = 0
            else:
                dist = 1
                
	    optm = conn.open
	    clotm = conn.close
	    dur = conn.dur

	    csyn = conn.clisyn
	    ssyn = conn.servsyn
	    cacksyn = conn.clisyn
	    sacksyn = conn.servsyn
	    crst = conn.clirst
	    srst = conn.servrst
	    cfin = conn.clifin
	    servfin = conn.servfin

	    totgap = 0
	    ngaps = 0
	    gap_blanks = []
	    for gap in conn.gaps:
		tm = gap[0]
		gap_blanks.append((tm - totgap, ngaps))
		totgap = totgap + gap[1]
		ngaps = ngaps + 1
	  

## 	    print '#%d %9d-%9d %9d %9d %3d' % (conn.id, optm/1000,
## 					   clotm/1000, dur/1000, 
## 					   conn.gap_before, conn.ngaps_before),
            if dist:
                colour = "blue"
            else:
                colour = 'grey72'

	    y  = conn.y = topy + conn.indx*self.connht + self.connht/2
	    xopen = conn.x0 = self.leftmargin \
		 + int((optm - conn.gap_before)*scalef) \
		 + conn.ngaps_before*self.gapwidth
	    xclose = conn.x1 = xopen + int((dur - totgap)*scalef) + ngaps*self.gapwidth
	    #print '(%d-%d)' % (x0, xclose)

	    if conn.ns_attached:
		labelfront = self.draw_lookup(conn.lookup, y) - 8
	    else:
		labelfront = xopen - 8
		

	    conn.handle = LINE(xopen, y, xclose, y, fill=colour, width=2)
            # don't know if maxx set yet
            try:
                maxx = conn.maxx
            except AttributeError:
                maxx = 0
            conn.maxx = max(maxx, xclose)

	    # blank out the gaps
	    for blank in gap_blanks:
		blankx =  self.leftmargin \
		 + int((blank[0] - conn.gap_before)*scalef) \
		 + (blank[1] + conn.ngaps_before)*self.gapwidth + 1
		LINE(blankx, y, 
					   blankx+self.gapwidth-1, y, 
					   fill="white", width=2)

                

	    # draw in the packet arrivals
            sfin_seen = 0
            cfin_seen = 0
            ssyn_seen = 0
            csyn_seen = 0
            pl = conn.pktlist[:]
            pl.reverse()
	    for pkt in pl:
		#way = ord(pkt.way[0])
                way = pkt.dir
		#flags = ord(pkt.flags[0])
		flags = pkt.flags
		pkt.pktx = x = self.leftmargin \
		       + int((pkt.tm - pkt.pkt_gap_before)*scalef) \
		       + pkt.pkt_ngaps_before*self.gapwidth
		if flags & (TH_SYN | TH_RST | TH_FIN):
		    wi = 2
                    ht = 6
                    col = 'blue'
                    if flags & TH_SYN:
                        if way == SERVER:
                            if ssyn_seen:
                                col = '#00d7ff'
                            ssyn_seen = 1
                        else:
                            if csyn_seen:
                                col = '#00d7ff'
                            csyn_seen = 1
                    elif flags & TH_FIN:
                        if way == SERVER:
                            if sfin_seen:
                                col = '#00d7ff'
                            sfin_seen = 1
                        else:
                            if cfin_seen:
                                col = '#00d7ff'
                            cfin_seen = 1
                    elif flags & TH_RST:
                        col = 'red'
                else:
                    wi = 1
                    ht = 3
                    col = 'black'
		if way == SERVER:
		    if pkt.tm == conn.cliacksyn:
			col = 'green'
		    yp = y+ht
		else:
		    if pkt.tm == conn.servacksyn:
			col = 'green'
		    yp = y-ht

                if not dist:
                    col = 'grey72'
		LINE(x, y, x, yp, fill=col, width=wi)
		
            self.label_conn(canv, conn, labelfront, xopen, xclose, y, dist)

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

    
    def draw_conns_v(self):

	tconns = self.client.connlist
	canv = self.view
	scalef = canv.scalef
	LINE = canv.create_line

	topy = self.view.topy
	if len(self.client.implied_obs):
	    topy = topy+self.impobjht

        if self.pktdict:
            pd = 0
        else:
            pd = 1
            pktdict = self.pktdict

	#print 'drawing conns_v:'
        
	for conn in tconns:

            # in distinguished tree?
            if self.rooturl and not (conn in self.tree_conns):
                dist = 0
            else:
                dist = 1
                
	    optm = conn.open
	    clotm = conn.close
	    dur = conn.dur

            if dist:
                colour = "blue"
            else:
                colour = 'grey72'

	    y  = conn.vy = topy + conn.indx*self.vconnht + self.vconnht/2
	    x0 = conn.xv0 = self.leftmargin + int(optm*scalef)
	    x1 = conn.xv1 = x0 + int(dur*scalef)
	    #print '(%d-%d, %d)' % (x0, x1, y)

	    if conn.ns_attached:
		labelfront = self.draw_lookup_v(conn.lookup, y) - 8
	    else:
		labelfront = x0 - 8
		

	    conn.vhandle = LINE(x0, y, x1, y, fill=colour, width=2)
            # don't know if maxvx set yet
            try:
                maxvx = conn.maxvx
            except AttributeError:
                maxvx = 0
            conn.maxvx = max(maxvx, x1)
            
	    # draw in the packet arrivals
            sfin_seen = 0
            cfin_seen = 0
            ssyn_seen = 0
            csyn_seen = 01
            pl = conn.pktlist[:]
            pl.reverse()
	    for pkt in pl:
		#way = ord(pkt.way[0])
                way = pkt.dir
		#flags = ord(pkt.flags[0]) 
		flags = pkt.flags 
		pkt.pktvx = x = self.leftmargin + int(pkt.tm*scalef)
		if flags & (TH_SYN | TH_RST | TH_FIN):
		    wi = 2
                    ht = 6
                    col = 'blue'
                    if flags & TH_SYN:
                        if way == SERVER:
                            if ssyn_seen:
                                col = '#00d7ff'
                            ssyn_seen = 1
                        else:
                            if csyn_seen:
                                col = '#00d7ff'
                            csyn_seen = 1
                    elif flags & TH_FIN:
                        if way == SERVER:
                            if sfin_seen:
                                col = '#00d7ff'
                            sfin_seen = 1
                        else:
                            if cfin_seen:
                                col = '#00d7ff'
                            cfin_seen = 1
                    elif flags & TH_RST:
                        col = 'red'
                else:
                    wi = 1
                    ht = 3
                    col = 'black'
		if way == SERVER:
		    if pkt.tm == conn.cliacksyn:
			col = 'green'
		    yp = y+ht
		else:
		    if pkt.tm == conn.servacksyn:
			col = 'green'
		    yp = y-ht

                if not dist:
                    col = 'grey72'
		LINE(x, y, x, yp, fill=col, width=wi)
		


	    # draw in the packet arrivals
	    for pkt in conn.pktlist:
		#pkt.printself_rel()
		#flags = ord(pkt.flags[0])
		flags = pkt.flags 
		pkt.pktvx = x = self.leftmargin + int(pkt.tm*scalef)
		if (flags & TH_SYN) or (flags & TH_RST) or (flags & TH_FIN):
		    continue
		#way = ord(pkt.way[0])
		way = pkt.dir
		if way == SERVER:
		    if pkt.tm == conn.servacksyn:
			continue
		    yp = y+3
		else:
		    if pkt.tm == conn.cliacksyn:
			continue
		    yp = y-3

                if dist:
                    col = 'black'
                else:
                    col = 'grey72'
		LINE(x, y, x, yp, fill=col, width = 1)
                
            if dist:
                self.label_conn(canv, conn, labelfront, x0, x1, y, dist)

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

    def draw_lookup(self, lookup, y):
	canv = self.draw
	LINE = canv.create_line
	scalef = canv.scalef

	#print 'drawing lookup'

	reqtm = lookup.reqtm
	reptm = lookup.reptm
	dur = lookup.dur

	totgap = 0
	ngaps = 0
	gap_blanks = []
	for gap in lookup.gaps:
	    gap_blanks.append((gap[0] - totgap, ngaps))
	    totgap = totgap + gap[1]
	    ngaps = ngaps + 1

## 	    print '#%d %9d-%9d %9d %9d %3d' % (conn.id, optm/1000,
## 					   clotm/1000, dur/1000, 
## 					   conn.gap_before, conn.ngaps_before),

	colour = "red"
	x0 = self.leftmargin \
		 + int((reqtm - lookup.gap_before)*scalef) \
		 + lookup.ngaps_before*self.gapwidth
	x1 = x0 + int((dur - totgap)*scalef) + ngaps*self.gapwidth
	    #print '(%d-%d)' % (x0, x1)

	LINE(x0, y, x1, y, fill=colour, width=2)
	LINE(x0, y+4, x0, y-4, fill=colour, width=2)
	LINE(x1, y+4, x1, y-4, fill=colour, width=2)

	# blank out the gaps
	for blank in gap_blanks:
	    blankx =  self.leftmargin \
		 + int((blank[0] - lookup.gap_before)*scalef) \
		 + (blank[1] + lookup.ngaps_before)*self.gapwidth + 1
	    LINE(blankx, y, 
					   blankx+self.gapwidth-1, y, 
					   fill="white", width=2)

	self.label_lookup(canv, lookup, x0, x1, y)

	return x0

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

    def draw_lookup_v(self, lookup, y):

	canv = self.view
	LINE = canv.create_line
	scalef = canv.scalef

	#print 'drawing lookup_v:'

	reqtm = lookup.reqtm
	reptm = lookup.reptm
	dur = lookup.dur

	colour = "red"
	x0 = self.leftmargin \
		 + int(reqtm*scalef)
	x1 = x0 + int(dur*scalef)
	    #print '(%d-%d)' % (x0, x1)

	LINE(x0, y, x1, y, fill=colour, width=2)
	LINE(x0, y+4, x0, y-4, fill=colour, width=2)
	LINE(x1, y+4, x1, y-4, fill=colour, width=2)

	self.label_lookup(canv, lookup, x0, x1, y)

	return x0

#############################################################################
    
    def draw_reqs(self):
	reqs = self.client.translist
	canv = self.draw
	scalef = canv.scalef

	LINE = canv.create_line
	RECT = canv.create_rectangle

	topy = self.draw.topy
	if len(self.client.implied_obs):
	    #topy = topy+self.impobjht
	    topy = topy+self.connht

	#print 'drawing %d reqs:' % (len(reqs))
	for req in reqs:
            
            # part of distinguished tree?
            if self.rooturl and not (req in self.tree_trans):
                dist = 0
            else:
                dist = 1

	    ycentre = req.y = topy +req.TConn.indx*self.connht + self.conncentre

	    y0 = req.y0 = ycentre - self.reqht/2
	    y1 = req.y1 = req.y0 + self.reqht

	    if req.cvalid:
		reqstart = req.reqstart
		reqend = req.reqend
		cobtype = req.cobtype
                if dist:
                    ccolour = self.tdict[cobtype][1]
                else:
                    ccolour = 'grey72'

		#draw the request
		xreqstart = req.xreqstart = self.leftmargin \
		     + int((reqstart - req.reqstart_gap_before)*scalef) \
		     + req.reqstart_ngaps_before*self.gapwidth
		if reqend != reqstart:
		    xreqend = req.xreqend = self.leftmargin \
			 + int((reqend - req.reqend_gap_before)*scalef) \
		     + req.reqend_ngaps_before*self.gapwidth
		else:
		    xreqend = req.xreqend = xreqstart

		RECT(xreqstart, y0, xreqend, ycentre, fill=ccolour, outline=ccolour, width=2)
	    else:
		xreqstart = xreqend = 0

	    if req.svalid:
		repstart = req.repstart
		repend = req.repend
		sobtype = req.sobtype
                if dist:
                    scolour = self.tdict[sobtype][1]
                else:
                    scolour = 'grey72'

		#draw the response
		xrepstart = req.xrepstart = self.leftmargin \
		     + int((repstart - req.repstart_gap_before)*scalef) \
		     + req.repstart_ngaps_before*self.gapwidth
		if repend != repstart:
		    xrepend = req.xrepend = self.leftmargin \
			 + int((repend - req.repend_gap_before)*scalef) \
		     + req.repend_ngaps_before*self.gapwidth
		else:
		    xrepend = req.xrepend = xrepstart

		RECT(xrepstart, ycentre, xrepend, y1, fill=scolour, 
		     outline=scolour, width=2)

                repdel = req.delrepend - req.repend
              ##   if repdel:
##                     xdelrepend = self.leftmargin \
## 			 + int((req.TConn.servfin - req.TConn.sfin_gap_before)*scalef) \
## 		     + req.TConn.sfin_ngaps_before*self.gapwidth
##                     RECT(xrepend, ycentre, xdelrepend, y1, 
## 		     outline=scolour, width=1)
                    
	    else:
		# no response drawn - point it all at the request
                xrepstart = xrepend = req.xrepstart = req.xrepend = xreqend

	    # blank out the gaps
	    totgap = 0
	    ngaps = 0
	    gap_blanks = []

	    for gap in req.gaps:
		gap_blanks.append((gap[0] - totgap, ngaps))
		totgap = totgap + gap[1]
		ngaps = ngaps + 1
	    for blank in gap_blanks:
		blankx = self.leftmargin \
                      + int((blank[0] - req.gap_before)*scalef) \
                      + (blank[1] + req.ngaps_before)*self.gapwidth + 1
		RECT(blankx, y0-1, blankx+self.gapwidth-2, y1+1, 
                                        fill="white", outline="")

	    
            self.label_req(canv, req, xreqstart, xreqend, xrepstart, 
                               xrepend, y0, ycentre, y1, dist)

#############################################################################
    
    def draw_reqs_v(self):
	reqs = self.client.translist
	canv = self.view
	scalef = canv.scalef

        canv.leftmargin = self.leftmargin

	LINE = canv.create_line
	RECT = canv.create_rectangle

	topy = self.view.topy
	if len(self.client.implied_obs):
	    topy = topy+self.impobjht

	#print 'drawing reqs_v:'
	for req in reqs:
            
            # part of distinguished tree?
            if self.rooturl and not (req in self.tree_trans):
                dist = 0
            else:
                dist = 1

	    ycentre = req.yv = topy +req.TConn.indx*self.vconnht + self.vconncentre

	    y0 = req.yv0 = ycentre - self.reqht/2
	    y1 = req.yv1 = y0 + self.reqht

	    if req.cvalid:
		reqstart = req.reqstart
		reqend = req.reqend
		reqdur = req.reqdur
		cobtype = req.cobtype
                if dist:
                    ccolour = self.tdict[cobtype][1]
                else:
                    ccolour = 'grey72'

		#draw request
		xreqstart = req.xvreqstart = self.leftmargin +int(reqstart*scalef)
		if reqdur:
		    xreqend = req.xvreqend = xreqstart + int(reqdur*scalef)
		else:
		    xreqend = req.xvreqend = xreqstart
		RECT(xreqstart, y0, xreqend, ycentre, fill=ccolour, 
		     outline=ccolour, width=2)
	    else:
		xreqstart = xreqend = 0


	    #if req.svalid:
	    if 1: ###
		repstart = req.repstart
		repend = req.repend
		repdur = req.repdur
		sobtype = req.sobtype
                if dist:
                    scolour = self.tdict[sobtype][1]
                else:
                    scolour = 'grey72'

		#draw response
		xrepstart = req.xvrepstart = self.leftmargin +int(repstart*scalef)
		if repdur:
		    xrepend = req.xvrepend = xrepstart + int(repdur*scalef)
		else:
		    xrepend = req.xvrepend = xrepstart
		RECT(xrepstart, ycentre, xrepend, y1, fill=scolour, 
		     outline=scolour, width=2)
                repdel = req.delrepend - repend
                #if repdel:
                    #xdelrepend = xrepend + int(repdel*scalef)
                    #RECT(xrepend, ycentre, xdelrepend, y1, 
                         #outline=scolour, width=1)
	    else:
		xrepstart = xrepend = 0

            self.label_req(canv, req, xreqstart, xreqend, xrepstart, 
			   xrepend, y0, ycentre, y1, dist)

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

    def draw_gaps(self):
	
	gaps = self.gaps
	canv = self.draw
	tcanv = self.drawscale
	TEXT = tcanv.create_text
	LINE = canv.create_line
	TLINE = tcanv.create_line
	#scalef = canv.scalef
	scalef = self.draw.scalef

	#print 'drawing %d gaps' % (len(gaps))

	totgap = 0
	ngaps = 0
	y0 = self.toplabely
	y1 = self.scaleht
	#y3 = self.draw.yrange
        y3 =  canv.ymin
	y4 = canv.ymax

        if not len(gaps):
            tcanv.ysize = self.scaleht
            return

	# find largest gap label and calculate scale steps to stop trampling
	t = TEXT(0, 0, anchor=W, fill='black', font=self.font10, text='%d' % (gaps[-1][1]/1000))
	bb=tcanv.bbox(t)
	mtw = bb[2] - bb[0] + 5
	tcanv.delete(t)
	steps = mtw/self.mingapx

        tsep = self.gap_text_ysep

	for gap in gaps:
	    #print gap
	    type = gap[0]
	    tm = gap[1]
	    g = gap[2]

	    #print '%9d %8d %d' % (tm/1000, g/1000, type)
	    x1 = self.leftmargin \
		 + int((tm - totgap)*scalef) + ngaps*self.gapwidth
	    x2 = x1 + self.gapwidth
	    x = x1 + self.gapwidth/2
	    gap[3] = x
	    if type == GAP_TM:
		colour = "azure2"
		tcolour = 'black'
	    elif type == GAP_CONNS:
		colour = "PaleTurquoise1"
		tcolour = 'RoyalBlue'
	    else:
		colour = "DarkSeaGreen1"
		tcolour = 'DarkSeaGreen4'


	    # Sanity
	    if g < 0:
		print 'WHOOPS negative gap (%d-%d)' % (tm/1000, (tm+g)/1000)
		colour="red"

	    down = ngaps%steps
	    texty = y0 + down*tsep
	    #if texty > maxdown:
		#maxdown = texty

	    startstr = '%d' % (tm/1000)
	    endstr = '%d' % ((tm+g)/1000)
	    #self.draw_dotted_vline(canv, x, y0, y1, colour)
	    TLINE(x, texty, x, y1, fill=colour)
	    l = LINE(x, y3, x, y4, fill=colour)
            canv.lower(l)
	    TEXT(x1, texty, fill=tcolour, anchor=E,
				  text = startstr, font=self.font10)
	    TEXT(x2, texty, fill=tcolour, anchor=W,
				  text = endstr, font=self.font10)
	    totgap = totgap + g
	    ngaps = ngaps+1

	bb = tcanv.bbox(ALL)
	tcanv.ysize = bb[3]-bb[1]+10

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

    def draw_implied_objects(self):
        
	width = self.min_x
	canv = self.draw
	TEXT = canv.create_text
        col = 'black'
        tconns = self.client.connlist
        
        canv.dummytrans_map = {}
        
	for req in self.client.dummytrans:

            if self.rooturl and not req in self.tree_trans:
                draw_me = 0
                for t in req.downlinks:
                    if t in self.tree_trans:
                        draw_me = 1
                if not draw_me:
                    continue

##             # distinguished?
##             if self.rooturl and self.rooturl != req.absurl:
##                 col = 'grey72'
##             else:
##                 col = 'black'
                
	    req.xreqstart = req.xrepstart = req.firstrans.xreqstart - width/2
	    req.xreqend = req.xrepend = req.xreqstart + width

            # find nearest convenient y location
            if self.connht > 50:
                y = req.firstrans.TConn.y - self.conncentre
                i = None
            else:
                i = req.firstrans.TConn.indx - 1
                while i >= 0:
                    if tconns[i].maxx < req.xreqstart:
                        break
                    i -= 1
                    
                if i < 0:
                    for s in self.client.dummytrans:
                        if not canv.dummytrans_map.has_key(i) or canv.dummytrans_map[i] < req.xreqstart:
                            break
                        i -= 1
                    y = canv.topy + i*self.connht + self.topmargin
                else:
                    y = tconns[i].y
            
            req.y = y
	    y0 = req.y0 = req.y-self.reqht/2
	    y1 = req.y1 = req.y0+self.reqht
	    self.draw.create_rectangle(req.xreqstart, y0, req.xreqend, y1, 
				       outline=col, width=2)

	    #label object
	    labelstr = req.obname
	    t = TEXT(req.xreqstart+10, (y0+y1)/2, fill=col, anchor=W, 
		       text='>%d' % (-req.order), font = self.font10bold)
          ##   # if distinguished tree root mark it
##             if self.rooturl == req.absurl:
##                 bb = canv.bbox(t)
##                 canv.create_rectangle(bb[0], bb[1], bb[2], bb[3],
##                                       outline='red')
	    t = TEXT(canv.bbox(t)[2]+5, (y0+y1)/2, fill=col, anchor=W, 
				  text=labelstr, font = self.font10)
            if i != None:
                # record this so not over-written later
                if i >= 0:
                    tconns[i].maxx = max(tconns[i].maxx, canv.bbox(t)[2])
                else:
                    canv.dummytrans_map[i] = canv.bbox(t)[2]
#############################################################################

    def draw_implied_objects_v(self):
        
	width = self.min_x
        canv = self.view
	TEXT = canv.create_text
        col = 'black'
        tconns = self.client.connlist
        
        canv.dummytrans_vmap = {}
        
	for req in self.client.dummytrans:
            #print '#%d ' % (req.order),

            if self.rooturl and not req in self.tree_trans:
                draw_me = 0
                for t in req.downlinks:
                    if t in self.tree_trans:
                        draw_me = 1
                if not draw_me:
                    continue

            

##             # distinguished?
##             if self.rooturl and self.rooturl != req.absurl:
##                 col = 'grey72'
##             else:
##                 col = 'black'
                
	    req.xvreqstart = req.xvrepstart = req.firstrans.xvreqstart - width/2
	    req.xvreqend = req.xvrepend = req.xvreqstart + width

            # find nearest convenient y location
            if self.connht > 50:
                y = req.firstrans.TConn.vy - self.conncentre
                i = None
            else:
                i = req.firstrans.TConn.indx - 1
                #print 'i start = %d ' % (i),
                while i >= 0:
                    if tconns[i].maxvx < req.xvreqstart:
                        break
                    i -= 1
                    
                if i < 0:
                    for s in self.client.dummytrans:
                        #print 'XVXV ',
                        #print req.order
                        if not canv.dummytrans_vmap.has_key(i) or canv.dummytrans_vmap[i] < req.xvreqstart:
                            break
                        i -= 1
                    y = canv.topy + i*self.connht + self.topmargin
                else:
                    y = tconns[i].vy

            #if i != None:
                #print 'at indx %d' % (i)
            req.yv = y
	    y0 = req.yv0 = req.yv-self.reqht/2
	    y1 = req.yv1 = req.yv0+self.reqht
	    canv.create_rectangle(req.xvreqstart, y0, req.xvreqend, y1, 
				       outline=col, width=2)

	    #label object
	    labelstr = req.obname
	    t = TEXT(req.xvreqstart+10, (y0+y1)/2, fill=col, anchor=W, 
			 text='>%d' % (-req.order), font = self.font10bold)
          ##   # if distinguished tree root mark it
##             if self.rooturl == req.absurl:
##                 bb = canv.bbox(t)
##                 canv.create_rectangle(bb[0], bb[1], bb[2], bb[3],
##                                       outline='red')
	    t = TEXT(canv.bbox(t)[2]+5, (y0+y1)/2, fill=col, anchor=W, 
				  text=labelstr, font = self.font10)
            if i != None:
                if i >= 0:
                    tconns[i].maxvx = max(tconns[i].maxvx, canv.bbox(t)[2])
                    #print '%d - adj maxvx' % (i)
                else:
                    canv.dummytrans_vmap[i] = canv.bbox(t)[2]

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

    def draw_link(self, canv, fx, fy, tx, ty, type, obsepy, obwi):


	LINE = canv.create_line

	self.LinkDict['types-seen'] += 1
	style = self.get_linktype(type)
        style[0] += 1
	colour = style[2]
	st = style[3]

        if ty == fy:
            # same level
            thisy = fy - obsepy/2
            LINE(fx, thisy, tx, thisy, fill=colour, stipple=st)
            LINE(fx, thisy, fx, fy-obwi, fill=colour, stipple=st)
            LINE(tx, thisy, tx, ty-obwi, fill=colour, stipple=st)
            LINE(tx, ty-obwi-1, tx, ty-obwi, fill=colour,
                 arrow="last", arrowshape=(4,5,2))
        elif ty > fy:
            # object lower than referrer
            thisy = fy + obsepy/2
            #print 'object lower - thisy %d' % (thisy)
            LINE(fx, thisy, tx, thisy, fill=colour, stipple=st)
            LINE(fx, thisy, fx, fy + obwi, fill=colour, stipple=st)
            LINE(tx, thisy, tx, ty - obwi, fill=colour, stipple=st)
            LINE(tx, ty-obwi-1, tx, ty - obwi, fill=colour,
                 arrow="last", arrowshape=(4,5,2))
        else:
            # object higher than referrer
            thisy = fy - obsepy/2
            #print 'object higher - thisy %d' % (thisy)
            LINE(fx, thisy, tx, thisy, fill=colour, stipple=st)
            LINE(fx, thisy, fx, fy - obwi, fill=colour, stipple=st)
            LINE(tx, thisy, tx, ty + obwi, fill=colour, stipple=st)
            LINE(tx, ty+obwi+1, tx, ty + obwi, fill=colour, arrow="last", 
                 arrowshape=(4,5,2))

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

    def draw_deps(self, canv, obsepy, obwi):

	def find_pkt(pktlist, time):
	    for pkt in pktlist:
		if pkt.tm == time:
		    return pkt
            return None

## 	def find_pkt(conn, time):
##             try:
##                 return self.pktdict[conn, time]
##             except KeyError:
##                 return None
        translist = self.client.translist + \
                    [d for d in self.client.dummytrans if d.reflink]
    
        for trans in translist:
            if trans.reflink \
                   and ((not self.rooturl) or trans in self.tree_trans):
                link = trans.reflink
                ref = link.trans
                type = link.type

                if (type & (LR_INLINE | LR_LINK)) and ref.TConn:
                    pkt = find_pkt(ref.TConn.pktlist, link.tm)
                    #pkt = find_pkt(ref.TConn, link.tm)
                    if not pkt:
                        print 'No pkt found trans', trans.order
                        print link
                        #raw_input('...')
                        continue
                        
                    if canv == self.draw:
                        refx = pkt.pktx
                        obx = trans.xreqstart
                        refy = ref.y
                        oby = trans.y
                    else:
                        refx = pkt.pktvx
                        obx = trans.xvreqstart
                        refy = ref.yv
                        oby = trans.yv

                else:
                    ## try is tmp for debugging
                    try:
                        if canv == self.draw:
                            refx = (ref.xrepstart+ref.xrepend)/2
                            obx = trans.xreqstart
                            refy = ref.y
                            oby = trans.y
                        else:
                            refx = (ref.xvrepstart+ref.xvrepend)/2
                            obx = trans.xvreqstart
                            refy = ref.yv
                            oby = trans.yv
                    except AttributeError, s:
                        print s
                        print 'Client %s connid %d trans %d cflags=%x sflags=%x' % (intoa_string(self.client.id), trans.TConn.id, trans.order, trans.cstatus, trans.sstatus)
                        print 'link type %x' % (type)
                        print ref, ref.connid, ref.order
                        #trans.TConn.printself_with_trans()
                        raw_input('...')
                        break
                        

                self.draw_link(canv, refx, refy, obx, oby, type, obsepy, obwi)
            
            

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

    def draw_scale(self):

	canv = self.view
	scanv = self.viewscale
	TEXT = scanv.create_text
	LINE = canv.create_line
	TLINE = scanv.create_line

	scalef = self.view.scalef

	# calculate scale width and units
	t = int(self.minscalewidth/scalef)
	inc = 1
	while 1:
	    if inc > t:
		break
	    inc = inc*10

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

	x = self.leftmargin
	xinc = inc*scalef
	tm = 0
	n = self.view.xrange/inc
	if not n:
	    n = 1
	
	#print "xinx = %d" % (inc)
	# scale and key in tscale window, lines only in tree window

	y3 = canv.ymin
        y4 = canv.ymax
	
	x = self.leftmargin
	for l in range(n+2):
	    y0 = self.toplabely
	    y1 = y0+self.screen.tscalecanv_ysz
	    TLINE(x, y0, x, y1, fill='black')
	    l = LINE(x, y3, x, y4, fill='black')
            canv.lower(l)
	    sstr = '%d' % (tm/1000)
	    TEXT(x, y0, text = sstr, fill='black', anchor = S, font=self.font10)
	    x = x+xinc
	    tm = tm+inc


	bb = scanv.bbox(ALL)
        try:
            scanv.ysize = bb[3]-bb[1]
        except:
            print 'Can\'t get bounding box - setting ysize to 1000'
            scanv.ysize = 1000
	    

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

    def draw_map(self, canv):

	if canv.drawn:
	    return
	LINE = canv.create_line
	RECT = canv.create_rectangle

	xscalef =  self.view.mxfact
	yscalef =  self.view.myfact
	tm = self.map_margin

        markers = []

	if len(self.client.connlist) > 10:
	    cw = 1
	    ow = 2
	else:
	    cw = 2
	    ow = 4

	for conn in self.client.connlist:
	    x0 = int(conn.xv0*xscalef)
	    x1 = int(conn.xv1*xscalef)
	    y = tm + int(conn.vy*yscalef)
            if self.rooturl and not(conn in self.tree_conns):
                col = 'grey39'
            else:
                col = 'blue'
	    LINE(x0, y, x1, y, fill=col, width=cw)

	for req in self.client.translist:
            if self.rooturl and not (req in self.tree_trans):
                continue
	    y = tm + int(req.yv*yscalef)
	    if req.cvalid:
		cobtype = req.cobtype
		ccolour = self.tdict[cobtype][1]
		x0 = int(req.xvreqstart*xscalef)
		x1 = int(req.xvreqend*xscalef)
		if x1 == x0:
		    x0 = x1 + 1
		RECT(x0, y-ow, x1, y+ow, fill=ccolour, outline=ccolour)
		req.mapreqx = (x0+x1)/2
		req.mapreqy = y

	    #if req.svalid:
	    if 1: ###
		sobtype = req.sobtype
		scolour = self.tdict[sobtype][1]
		x0 = int(req.xvrepstart*xscalef)
		x1 = int(req.xvrepend*xscalef)
		if x1 == x0:
		    x0 = x1 + 1
		RECT(x0, y-ow, x1, y+ow, fill=scolour, outline=scolour)
		req.maprepx = (x0+x1)/2
		req.maprepy = y

            # mark as interesting
            if (req.reflink and (req.reflink.type & LR_REDIRECT)) \
               or ((not req.reflink) and not (len(req.downlinks))) \
               or req.isfail \
               or req.multi \
               or req.fon:
                markers.append((x0-2, y-ow))

	for imp in self.client.dummytrans:
            
            if self.rooturl and not (imp in self.tree_trans):
                continue
            
	    #print imp
	    #y = tm + int(((imp.dummy.yv0+imp.dummy.yv1)/2)*yscalef)
	    y = tm + int(((imp.yv0+imp.yv1)/2)*yscalef)
	    #x0 = int(imp.dlinks[0].xvreqstart*xscalef)
	    x0 = int(imp.firstrans.xvreqstart*xscalef)
	    x1 = x0 + 10
	    RECT(x0, y-ow, x1, y+ow, fill='', outline='black', width=cw)
	    #imp.dummy.maprepx = imp.dummy.mapreqx = (x0+x1)/2
	    #imp.dummy.maprepy = imp.dummy.mapreqy = y
	    imp.maprepx = imp.mapreqx = (x0+x1)/2
	    imp.maprepy = imp.mapreqy = y
            
        translist = self.client.translist + \
                    [d for d in self.client.dummytrans if d.reflink]

	for trans in translist:
            if (not trans.reflink) or \
                   (self.rooturl and not trans in self.tree_trans):
                continue
            else:
                link = trans.reflink
                ref = link.trans
                if self.rooturl and not ref in self.tree_trans:
                    continue

		refx = ref.maprepx
		obx = trans.mapreqx
		refy = ref.maprepy
		oby = trans.mapreqy

		#print 'link (%d,%d) to (%d,%d)' % (refx, refy, obx, oby)
		#self.draw_link(canv, refx, refy, obx, oby, colour, style, 
			       #self.connht*yscalef, ow)
		self.draw_link(canv, refx, refy, obx, oby, link.type, 
			       self.connht*yscalef, ow)

        # draw last so distinct
        for m in markers:
            #self.draw_sarrow(canv, m[0], m[1], 'red')
            self.draw_mark(canv, 'Any - map', m[0], m[1], count=1) 
	canv.drawn = 1
	    

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

    def init_map_port(self, canv):
	xsz = canv.xsize
	ysz = canv.ysize
	map = self.map
        canv.map = map
	canv.draw_map_port = self.draw_map_port
	canv.delete_map_port = self.delete_map_port
	canv.find_gap_by_time = self.find_gap_by_time

	map.port = None
	#canv.tlc = [canv.xmin, 0]
        canv.tlc = [canv.canvasx(0), canv.canvasy(0)]

	canv.myfact = float(self.screen.map_canv_ht)/self.draw.ysize
	canv.mysize = int(canv.myfact*canv.screenht)
        canv.mxfact = self.view.scalef*self.view.mxfact
	canv.mxoff = int(self.leftmargin*self.view.mxfact)
	canv.mi = 0

        return
	    

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

    def init_vmap_port(self, canv):
	xsz = canv.xsize
	ysz = canv.ysize
	map = self.map
	canv.draw_map_port = self.draw_vmap_port
	canv.delete_map_port = self.delete_vmap_port

	map.port = None
	#canv.tlc = [canv.xmin, 0]
        canv.tlc = [canv.canvasx(0), canv.canvasy(0)]

	canv.mxfact = float(self.screen.uframe_width)/self.view.xsize
	canv.myfact = float(self.screen.map_canv_ht)/self.view.ysize
	canv.mxsize = int(canv.mxfact*self.screen.dframe_width)
	canv.mysize = int(canv.myfact*canv.screenht)
	canv.map = map

	h = int(map.cget('height'))
	#print 'h=%d' % (h)
	w = int(map.cget('width'))
	#print 'w=%d' % (w)
	    

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

    def draw_map_port(self, canv):

	#
	# Detail canvas has non-linear x-scale - use the gaps list to map 
	# display screen x0, x1 coords to gap time, then scale and offset time 
	# to map coords
	#

        map = canv.map
	#if map.port:
	    #self.delete_vmap_port(canv)
        x1 = canv.tlc[0]
        x2 = canv.tlc[0]+self.screen.dframe_width
        gaps = self.gaps

        #print 'x1=%d x2=%d' % (x1, x2)

        try:
            if x1 <= gaps[0][3]:
                mx1 = 0
            else:
                i1 = self.find_gap(x1, gaps, 0, len(self.gaps)-1, G_BEFORE)
                mx1 = int(gaps[i1][1]*canv.mxfact) + canv.mxoff

            if x2 >= gaps[-1][3]:
                mx2 = self.screen.uframe_width
            else:
                i2 = self.find_gap(x2, gaps, 0, len(self.gaps)-1, G_AFTER)
                mx2 = int(gaps[i2][1]*canv.mxfact) + canv.mxoff

            y0 = canv.tlc[1]*canv.myfact+1
            y1 = y0 + canv.mysize-1
        except (NotInRange, IndexError):
            return
	#print 'creating port %d %d %d %d' % (mx1, y0, mx2, y1)
	if map.port:
	    self.delete_vmap_port(canv)
	map.port = map.create_rectangle(mx1, y0, mx2, y1, width=1, outline='red')
	map.pl = mx1
	    

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

    def draw_vmap_port(self, canv):

	map = canv.map
	if map.port:
	    self.delete_vmap_port(canv)

	tlc = canv.tlc

	x0 = tlc[0]*canv.mxfact+1
	x1 = x0 + canv.mxsize-1
	y0 = tlc[1]*canv.myfact+1
	y1 = y0 + canv.mysize-1

	#print 'creating port %d %d %d %d' % (x0, y0, x1, y1)
	map.port = map.create_rectangle(x0, y0, x1, y1, width=1, outline='red')
        map.pl = x0

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

    def delete_map_port(self, canv):
	map = canv.map
	map.delete(map.port)

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

    def delete_vmap_port(self, canv):
        
	map = canv.map
	map.delete(map.port)

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



    def build_tdict(self):

	#
	# object types
	#
	# (code: text, colour, client seen?, server seen?)
	tdict = {
            CT_NON_OBJECT: ['no object', '', 0, 0],
	    CT_UNKNOWN: ['type unknown', 'grey50', 0, 0],
	    1: ['text/html', 'orange1', 0, 0],
	    4: ['text/xml', 'sienna1', 0, 0],
	    2100: ['image/gif', 'SpringGreen', 0, 0],
	    2200: ['image/jpeg', 'LawnGreen', 0, 0], 
	    CT_POST_ERR: ['error', 'red', 0, 0],
	    CT_UNSYNCH: ['unsynchronised', 'grey85', 0, 0],
	    CT_OTHER: ['other', 'LightPink', 0, 0]
	    }

        for t in [(self.client.ctypedict.items(), 2),
                  (self.client.stypedict.items(), 3)]:
            i = t[1]
            tlist = t[0]

            for e in tlist:
                type = e[0]
                n = e[1]
                if tdict.has_key(type):
                    tdict[type][i] += n
                else:
                    tdict[type] = \
                                [http_server_objtype_string(type),
                                 self.object_cols[self.object_cols_indx], 0, 0]
                    tdict[type][i] += n
                    self.object_cols_indx = self.object_cols_indx + 1
		    
	self.tdict = tdict

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

    def build_agentsdict(self):

        keylist = ascii_letters
        keylen = len(keylist)
        
        self.agentdict = dict = {}

        # each list = [(no. occurences, name)]
        alist, slist = self.client.get_agentlists()

        keyi = 0
        self.alist = list = []
        for a in alist:
            list.append((a[0], a[1]))
            dict[a[1]] = keylist[keyi]
            keyi = (keyi+1)%keylen

        #nagents = ord('A')
        self.slist = list = []
        for a in slist:
            list.append((a[0], a[1]))
            dict[a[1]] = keylist[keyi]
            keyi = (keyi+1)%keylen

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

    def get_agentdict(self, a):

        #print 'val is', a
        return self.agentdict[a]
	    
#############################################################################

    def whoops(self, txt):
	x = self.leftmargin + 2*self.keyfieldwidth
	y = self.topkeyy + self.keyentryht*self.nwarnings
	self.nwarnings = self.nwarnings + 1

	self.view.create_text(x, y, text=txt, fill="red", font=self.font12bold)
	self.draw.create_text(x, y, text=txt, fill="red", font=self.font12bold)
	

#############################################################################
	
    def draw(self, screen):

        print '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~'
        print 'Drawing tree for %s %s' % (self.client.Class,
                                          self.client.addr_str())
##         print '(connections',
##         for c in self.client.connlist:
##             print ' %d' % (c.id),
##         print ')'
##         print '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~'
        print '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~'


        #self.client.build_event_list()
        #self.client.find_curr_conns()
        
        if self.client.rooturl:
            self.rooturl = self.client.rooturl
            try:
                self.tree_trans, self.tree_conns = \
                                 self.client.get_reftree(self.client.rooturl)
            except TreeError:
                print 'Not distinguishing tree'
                self.rooturl = None
        
	self.screen = screen
	indx = screen.indx 
	self.set_draw_parms()

	self.info = screen.canvlist[indx][0]
	self.map = screen.canvlist[indx][1]
        self.draw = screen.canvlist[indx][2][1]
        self.view = screen.canvlist[indx][3][1]
	self.draw_detailed(screen.canvlist[indx][2])
	self.draw_view(screen.canvlist[indx][3])

	self.init_vmap_port(self.view)
	self.init_map_port(self.draw)
	self.draw_map(self.map)
	self.draw_info(self.info)


#############################################################################
	
    def draw_detailed(self, canvasses):

	c = self.draw #= canvasses[1]
	if c.drawn:
	    return
	c.tree = self
	c.mode = 'detailed'
	self.drawscale = canvasses[0]
	self.drawscale.mode = 'scale'
	c.scroll_with = self.drawscale
	self.drawscale.scroll_with = c
	c.topy = 15
	self.calc_yinc()
	self.scale_canvas()
	self.draw_reqs()
	self.draw_conns()
	self.draw_implied_objects()
	self.draw_deps(c, self.connht, self.reqht/2)

	bb = c.bbox(ALL)
	c.xsize = bb[2]-bb[0] + self.rightmargin + self.leftmargin
	c.ysize = bb[3]-bb[1] + self.bottommargin + self.topmargin
	c.xsize = self.drawscale.xsize = max([c.xsize, self.screen.dframe_width])
        c.xmin = bb[0] - self.leftmargin
        c.xmax = bb[2] + self.rightmargin
        c.ymin = bb[1] - self.topmargin#nn
        c.ymax = bb[3] + self.bottommargin #nn
        
	self.draw_gaps() #nn

	c.screenht = self.screen.dframe_ht-self.drawscale.ysize
	c.ysize = max([c.ysize, c.screenht])

	c.config(height=c.screenht, 
			scrollregion=(c.xmin, c.ymin, c.xmax, c.ymax))
	self.drawscale.config(height=self.drawscale.ysize, 
			scrollregion=(c.xmin, 0, c.xmax, 
					     self.drawscale.ysize))
        # get the left edge nicely placed (essential if gone x negative)
        c.xview(MOVETO, c.xmin)
        c.yview(MOVETO, c.ymin) #nn
        self.drawscale.xview(MOVETO, c.xmin)
        
	c.drawn = 1
	#print 'done'

##         print 'scaleht %d' % (self.drawscale.ysize)
##         print 'drawht %d' % (c.ysize)

#############################################################################
	
    def draw_view(self, canvasses):
	
	c = self.view #= canvasses[1]
	if c.drawn:
	    return
	c.tree = self
	c.mode = 'time'
	self.viewscale = canvasses[0]
	self.viewscale.mode = 'scale'
	c.scroll_with = self.viewscale
	self.viewscale.scroll_with = c
	c.topy = self.toplabely
	self.calc_vyinc()
	self.scale_canvas_v()
	self.draw_reqs_v()
	self.draw_conns_v()
	self.draw_implied_objects_v()
	self.draw_deps(c, self.vconnht, self.reqht/2)

	bb = c.bbox(ALL)
	c.xsize = bb[2]-bb[0] + self.rightmargin + self.leftmargin
	c.ysize = bb[3]-bb[1] + self.bottommargin + self.topmargin
	c.xsize = self.viewscale.xsize = max([c.xsize, self.screen.dframe_width])
        c.xmin = bb[0]
        c.xmax = bb[2] + self.rightmargin
        c.ymin = bb[1] - self.topmargin#nn
        c.ymax = bb[3] + self.bottommargin #nn
        #print 'c.xmin=%d' % (c.xmin)

        self.draw_scale()

	c.screenht = self.screen.dframe_ht - self.viewscale.ysize
	c.ysize = max([c.ysize, c.screenht])
        
	c.config(height=c.screenht, scrollregion=(c.xmin, c.ymin, c.xmax, 
				       c.ymax))
	self.viewscale.config(height=self.viewscale.ysize, 
			      scrollregion=(c.xmin, 0, c.xmax, 
					     self.viewscale.ysize))
        # just in case we've strayed into negative x-coord when labelling
        #print 'c.xmin=%d' % (c.xmin)
        c.xview(MOVETO, c.xmin)
        c.yview(MOVETO, c.ymin)
        self.viewscale.xview(MOVETO, c.xmin)
        
	c.drawn = 1
	print 'done'


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

    def draw_info(self, canv):
	if canv.drawn:
	    return
	canv.topy = self.toplabely
	self.labeltree(canv)
	self.drawkeys(canv)
	self.draw_agents(canv)
        self.list_roots(canv)

	canv.topy += 5
 	canv.config(scrollregion=(0, 0, self.screen.uframe_width, canv.topy))
	canv.drawn = 1
	

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

    def labeltree(self, canv):

	TEXT = canv.create_text
	lm = self.leftkeymargin
	y = canv.topy
	width = self.screen.uframe_width - self.screen.info_can_sbar_width
	#print width

        client = self.client
        
	clientstr = '%s %s' %(client.Class, client.addr_str())
	t = TEXT(lm, y, fill='black', anchor=W, text=clientstr, 
		 font=self.font12bold, width=width)
	y = canv.bbox(t)[3] + 15

        if  client.eventlist[-1][0] - client.eventlist[0][0] !=  self.end - self.start:
            print 'XXX start %d evlist %d end %d evlist %d' % \
                  (self.start,
                  client.eventlist[0][0],
                  self.end,
                  client.eventlist[-1][0])
            print client.eventlist[-1]
            print client.eventlist[-1][2].id, client.eventlist[-1][2].open, client.eventlist[-1][2].close
            #raw_input('...')

        if self.rooturl:
            if self.rooturl[0]:
                txt = 'Ref Tree root %s conn %d' % \
                      (self.rooturl[0], self.rooturl[1])
            else:
                txt = 'Unlinked objects'
            t = TEXT(lm, y+10, fill='black', anchor=W, text=txt, 
                     font=self.font12, width=width)
            y = canv.bbox(t)[3] + 15
            
            
        str = 'Start %s\nDuration %.3fms' % (tsDatestring(self.abstart), (self.end - self.start)/1000.0)
        t = TEXT(lm, y,  anchor=NW, text=str, 
		 font=self.font10, width=width)
	y = canv.bbox(t)[3] + 20

	nc = len(client.connlist)
	if nc == 1:
	    psc = ''
	else:
	    psc = 's'
	nt = len(client.translist)
	if nt == 1:
	    pst = ''
	else:
	    pst = 's'
	cstr = '%d Connection%s\n(max. %d/%d/%d concurrent/eff. concurrent)\n%d transaction%s\n (%d invalid %d failed)'\
               % (nc, psc,
                  client.maxcop1, client.maxcop2,
                  client.maxcop3, nt, pst, client.trans_invalid,
                  client.trans_failed)
	t = TEXT(lm, y, fill='black', anchor=NW, text=cstr, 
		 font=self.font10, width=width)
	y = canv.bbox(t)[3] + 10

        lstr = 'objects linked: %d  unlinked: %d' % (client.nlinks,
                                                     client.nnolinks)
	t = TEXT(lm, y, fill='black', anchor=NW, text=lstr, 
		 font=self.font10, width=width)
	y = canv.bbox(t)[3] + 20
	    
	canv.topy = y
        
#############################################################################

    def list_roots(self, canv):

        def by_n(a, b):
            
            lb = len(b[1])
            la = len(a[1])
            if la != lb:
                return lb - la
            else:
                return a[1][0] - b[1][0]

        y = canv.topy

        for l in [(self.client.rootlist, 'Page roots'),
                  (self.client.unlinkedlist, 'Unlinked objects'),
                  (self.client.refreshlist, 'Refreshes'),
                  (self.client.unreslist, 'Unresolved')
                  ]:
            if l[0]:

                rdict = {}
                for r in l[0]:
                    re = rdict.setdefault(r.absurl, [])
                    re.append(r.order)

                TEXT = canv.create_text
                lm = self.leftkeymargin
                y = canv.topy + 10
                width = self.screen.uframe_width - self.screen.info_can_sbar_width - 10

                t = TEXT(lm, y, fill='black', anchor=NW, text='%s:-' % (l[1]), 
                         font=self.font10, width=width)
                y = canv.bbox(t)[3] + 10
                rootlist = rdict.items()
                rootlist.sort(by_n)
                for r in rootlist:
                    url = r[0]
                    txt = '%s (' % (url)
                    count = 0
                    for co in r[1]:
                        if count > 0:
                            txt += ', '
                        txt += '%d' % (co)
                        count += 1
                    txt += ')'
                    t = TEXT(lm, y, fill='black', anchor=NW, text=txt, 
                             font=self.font10, width=width)
                    y = canv.bbox(t)[3] + 5
                y += 10

            canv.topy = y
        
#############################################################################

    def drawkeys(self, canv):

	width = self.screen.uframe_width - self.screen.info_can_sbar_width
	width2 = width-10
	LINE = canv.create_line
	TEXT = canv.create_text
	x = self.leftkeymargin
	x2 = x+10
	y = canv.topy
	yinc = self.keyentryht

        client = self.client

	# Object types
	if len(client.translist):
	    t = TEXT(x, y, fill='black', text='Object types:-', 
			 anchor=NW, font=self.font12, width=width)
	    y = canv.bbox(t)[3]+10
	    
	    # indent?
	    obtypes = self.tdict.values()
	    obtypes.sort()
	    for o in obtypes:
		if o[2] or o[3]: #seen
                    fill = o[1]
                    if fill == '':
                        outline = 'black'
                    else:
                        outline = ''
		    canv.create_rectangle(x, y, x+50, y+10,
					  fill=fill, outline=outline)
		    t = TEXT(x+55, y+4, fill='black', 
			     text='%s %d/%d' % (o[0], o[2], o[3]),
                             anchor=W, font=self.font10, 
			     width=width)
		    y += yinc

	    y = canv.bbox(t)[3]+10

	# Link types
        ltypes = self.LinkDict
	if ltypes['types-seen']:
            del(ltypes['types-seen'])
	    t = TEXT(x, y, fill='black', text='Link types:-', 
			 anchor=NW, font=self.font12, width=width)
	    y = canv.bbox(t)[3]+10
            tl = ltypes.values()
            tl.sort()
	    for ty in tl:
		if ty[0]:
		    LINE(x, y, x+50, y, fill=ty[2], stipple=ty[3])
		    t = TEXT(x+55, y, fill='black',
                             text='%s %d' % (ty[1], ty[0]/3), anchor=W,
			     font=self.font10, width=width)
		    y += yinc

	    y = canv.bbox(t)[3]+10

        # markers
        if self.marksdict['marks-seen']:
	    t = TEXT(x, y, fill='black', text='Markers:-', 
			 anchor=NW, font=self.font12, width=width)
	    y = canv.bbox(t)[3]+10
            for type in ['Any - map', 'Re-directed', 'Unlinked',
                         'Transaction failed']:
                no = self.marksdict[type][0]
                if no:
                    self.draw_mark(canv, type, x, y)
                    str = '%s %d' % (type, self.marksdict[type][0])
                    t = TEXT(x+15, y, text=str, fill='black', anchor=W,
                             font=self.font10, width=width)
                    y += yinc

	    y = canv.bbox(t)[3]+10
                
        

	# server return codes
        codes = client.get_retcodelist()
	if len(codes):
	    t = TEXT(x, y, fill='black', text='Server return codes:-', 
			 anchor=NW, font=self.font12, width=width)
	    y = canv.bbox(t)[3]
	    # ? indent
	    for code in codes:
		txt = '%d  %s %d' % (code[0],
                       http_server_returncode_string(code[0]), code[1])
		t = TEXT(x2, y, fill='black', text=txt,
				 anchor=NW, font=self.font10, width=width2)
		y = canv.bbox(t)[3]

	    y = canv.bbox(t)[3]+10

	#any errors
        elist = client.get_transerrlist()
	if len(elist):
	    t = TEXT(x, y, fill='red', text='Transaction errors:-', 
			 anchor=NW, font=self.font12, width=width)
	    y = canv.bbox(t)[3] + 10
	    for e in elist:
		#t = TEXT(x2, y, fill='red', 
 		#     text='(%s)' % (chr(e[0])),
 		#		 anchor=W, font=self.font10, width=width2)
		#y = canv.bbox(t)[3]
		t = TEXT(x2+25, y, fill='red',
			 text='%d %s %d' % (e[1], http_errstring(e[1]), e[0]),
			 anchor=NW, font=self.font10, width=width2-25)
		y = canv.bbox(t)[3]

	    y = canv.bbox(t)[3]+10
	    
	canv.topy = y
#############################################################################

    def draw_agents(self, canv):

	TEXT = canv.create_text
	width = self.screen.uframe_width - self.screen.info_can_sbar_width
	width2 = width-10

	x = self.leftkeymargin
	x2 = x+10
	y = canv.topy
	yinc = self.keyentryht

	# agents
	if len(self.alist):
	    t = TEXT(x, y, fill='black', text='User agents:-', 
			 anchor=NW, font=self.font12, width=width)
	    y = canv.bbox(t)[3]

	    for a in self.alist:
		TEXT(x2, y, fill='black', 
		     text='(%s)' % (self.get_agentdict(a[1])),
		     anchor=NW, font=self.font10, width=width2)
		t = TEXT(x2+25, y, fill='black',
			 text='%s %d' % (a[1], a[0]),
			 anchor=NW, font=self.font10, width=width2-25)
		y = canv.bbox(t)[3]

	    y = canv.bbox(t)[3]+10
		
	# servers
	if len(self.slist):
	    t = TEXT(x, y, fill='black', text='Servers:-', 
			 anchor=NW, font=self.font12, width=width)
	    y = canv.bbox(t)[3]
	    for s in self.slist:
		TEXT(x2, y, fill='black', 
 		     text='(%s)' % (self.get_agentdict(s[1])),
 				 anchor=NW, font=self.font10, width=width2)
		t = TEXT(x2+25, y, fill='black',
			 text='%s %d' % (s[1], s[0]),
			 anchor=NW, font=self.font10, width=width2-25)
		y = canv.bbox(t)[3]

	    y = canv.bbox(t)[3]+10

	canv.topy = y

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

    def draw_sstar(self, canv, x, y, col):

        LINE = canv.create_line

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

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

    def draw_lstar(self, canv, x, y, col, full, lh, rh):

        LINE = canv.create_line

        if full:
            LINE(x-5, y, x+5, y, fill=col)
            LINE(x-4, y-4, x+4, y+4, fill=col)
            LINE(x, y-5, x, y+5, fill=col)
            LINE(x-4, y+4, x+4, y-4, fill=col)
        elif lh:
            LINE(x-4, y-4, x, y, fill=col)
            LINE(x-5, y, x, y, fill=col)
            LINE(x-4, y+4, x, y, fill=col)
            LINE(x, y+5, x, y, fill=col)
        elif rh:
            LINE(x, y, x, y-5, fill=col)
            LINE(x, y, x+4, y-4, fill=col)
            LINE(x, y, x+5, y, fill=col)
            LINE(x, y, x+4, y+4, fill=col)

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

    def draw_hstar(self, canv, x, y, col):

        LINE = canv.create_line

        LINE(x, y, x, y-5, fill=col)
        LINE(x, y, x+4, y-4, fill=col)
        LINE(x, y, x+5, y, fill=col)
        LINE(x, y, x+4, y+4, fill=col)

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

    def draw_lcircle(self, canv, x, y, col, full, lh, rh):

        canv.create_oval(x-5, y-5, x+5, y+5, outline=col)

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

    def draw_sarrow(self, canv, x, y, col, full, lh, rh):

        canv.create_line(x-6, y, x, y, fill=col, width=2,
                         arrow=LAST, arrowshape=(3,3,2))

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

    def attach_colours(self):
	# non - fixed object colours
	self.object_cols_indx = 0
	self.object_cols = [
	    'CadetBlue2',
	    'CadetBlue3', 
	    'DarkSeaGreen2',
	    'DarkOrchid1',
	    'DarkSlateGray2',
	    'DeepPink1',
	    'IndianRed1',
	    'LavenderBlush2',
	    'LightGoldenRod2',
	    'LightSalmon1',
	    'LightSalmon3',
	    'LightSteelBlue2',
	    'LightYellow2',
	    'MistyRose3',
	    'OliveDrab2',
	    'PaleGreen4',
	    'SlateBlue1',
	    'brown',
	    'gold', 
	    'goldenrod2',
	    'yellow',
	    'yellow3'
	    ]

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

    def get_linktype(self, linktype):

        try:
            return self.LinkDict[linktype]
        except KeyError:
            raw_input('unknown link type %#x' % (linktype))

            return self.LinkDict[0]

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

    #
    # Configure interest mark drawing and keys
    #


    def attach_marktypes_dict(self):

        d = {}

        d['marks-seen'] = 0 # Seen any?
        d['Any - map'] = [0, self.draw_sarrow, 'red']
        d['Re-directed'] = [0, self.draw_lcircle, 'purple']
        d['Unlinked'] = [0, self.draw_lstar, 'orange']
        d['Transaction failed'] = [0, self.draw_lstar, 'red']

        self.marksdict = d

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

    #
    # Configure interest mark drawing and keys
    #


    def draw_mark(self, canv, type, x, y, count=0, lh=0, rh=0):

        if lh or rh:
            full = 0
        else:
            full = 1

        self.marksdict['marks-seen'] += 1
        e = self.marksdict[type]
        fn = e[1]
        col = e[2]

        if count:
            e[0] += 1

        fn(canv, x, y, col, full, lh, rh)

        
	    

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

    def set_draw_parms(self):

	#
	# Minimum compacted gaps - us
	self.min_internal_gap = 1000 # us
	self.min_external_gap = 1000 #us

	#
	# Control canvas layout - sizes in canvas coordinates
	#
        self.max_canv_sz = self.screen.dframe_width*10
	self.leftmargin = 50
	self.rightmargin = 100
	self.leftkeymargin = 15
	self.rightkeymargin = 15
	self.topmargin = 25
	self.bottommargin = 25
	self.labelregionht = 25
	self.topscaleregionht = 64
	self.keyfieldwidth = self.screen.uframe_width - (self.leftkeymargin + self.rightkeymargin + self.screen.info_can_sbar_width)
	
	self.toplabely = 15
	self.topkeyy = self.labelregionht
	self.scaleht = 15
	self.gapkeyht = 60
	self.impobjht = 30

	self.connht = 40
	self.vconnht = 40
	self.reqht = 15
	self.gapwidth = 6

	self.keyentryht = 15

	self.min_x = 5 #50 # minimum time width
	#self.min_viewx = 1 # minimum time width for view
	self.max_viewx = 1000#500 # minimum time width for view
	self.mingapx = 5#10#30 # minimum displayed width between gaps
	self.scaleup_minx = 250 # minimum time width if spare room
	self.minscalewidth = 100 # closest spacing for view scale

	self.map_margin = 10

        self.gap_text_steps = 6
        self.gap_text_ysep = 10

	#
	# Fonts
	#
	self.font8 = ("6x13")
	self.font10 = ("7x13")
	self.font10bold = ("7x13bold")
	self.font12 = ("7x14")
	self.font12bold = ("7x14bold")

	self.textht8 = 8
	self.textht10 = 10
	self.textht12 = 128


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

    #
    # In gap list find gap before/after the given x coord
    #

    def find_gap(self, x, gaps, i1, i2, where):

        l = i1
        r = i2

        #print data

        if i1 == None:
            raise NotInRange

        if i1 == i2:
            # single point
            if x != gaps[i1][3]:
                raise NotInRange
            else:
                return i1

        while r > l:
            p = (l+r)/2
            #print 'l=%d r=%d p=%d' % (l, r, p)
            if x < gaps[p][3]:
                r = p
            else:
                l = p
            if r - l == 1:
                if gaps[r][3] < x or x < gaps[l][3]:
                    raise NotInRange
                if where == P_AFTER:
                    if gaps[l][3] <= x <= gaps[r][3]:
                        if gaps[l][3] <= x < gaps[r][3]:
                            return r
                        else:
                            return min([i2, r+1])
                elif where == P_BEFORE:
                    if gaps[l][3] <= x <= gaps[r][3]:
                        if gaps[l][3] < x <= gaps[r][3]:
                            return l
                        else:
                            return max([l-1, i1])


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

    #
    # In gap list find gap before/after the given gap time
    #

    def find_gap_by_time(self, t, gaps, i1, i2, where):

        l = i1
        r = i2

        #print data

        if i1 == None:
            raise NotInRange

        if i1 == i2:
            # single point
            if t != gaps[i1][1]:
                raise NotInRange
            else:
                return i1

        while r > l:
            p = (l+r)/2
            #print 'l=%d r=%d p=%d' % (l, r, p)
            if t < gaps[p][1]:
                r = p
            else:
                l = p
            if r - l == 1:
                if gaps[r][1] < t or t < gaps[l][1]:
                    raise NotInRange
                if where == P_AFTER:
                    if gaps[l][1] <= t <= gaps[r][1]:
                        if gaps[l][1] <= t < gaps[r][1]:
                            return r
                        else:
                            return min([i2, r+1])
                elif where == P_BEFORE:
                    if gaps[l][1] <= t <= gaps[r][1]:
                        if gaps[l][1] < t <= gaps[r][1]:
                            return l
                        else:
                            return max([l-1, i1])


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

#
# End class Obtree
#
	
#############################################################################
#############################################################################
