#lang racket/base
(require racket/list
         racket/class
         racket/draw
         pict
         data/interval-map
         "visualizer-data.rkt"
         "graph-drawing.rkt"
         "drawing-helpers.rkt"
         "display.rkt"
         "constants.rkt")

(provide timeline-pict
         timeline-pict-for-trace-data
         timeline-overlay
         seg-in-vregion
         calc-segments
         calc-ticks
         calc-row-mid-y
         find-seg-for-coords
         segment-edge
         segs-equal-or-later
         creation-tree-pict
         draw-creategraph-pict
         zoom-level->factor
         graph-overlay-pict
         (struct-out segment)
         (struct-out frame-info)
         (struct-out timeline-tick)
         find-node-for-coords
         find-fid-for-coords
         first-seg-for-fid
         print-seg)

;Represents a dot or square on the timeline
(struct segment (event
                 x
                 y
                 width
                 height
                 color
                 opacity
                 p
                 prev-future-seg
                 next-future-seg
                 prev-proc-seg
                 next-proc-seg
                 prev-targ-future-seg
                 next-targ-future-seg) #:transparent #:mutable)

;General information about the timeline image
(struct frame-info (adjusted-width
                    adjusted-height
                    row-height
                    modifier
                    timeline-ticks
                    process-line-coords) #:transparent)

;Represents a vertical line depicting a specific time in the execution history
(struct timeline-tick (x
                       abs-time
                       rel-time
                       show-label?) #:transparent)

;;viewable-region-from-frame : frame-info -> viewable-region
(define (viewable-region-from-frame finfo)
  (viewable-region 0
                   0
                   (frame-info-adjusted-width finfo)
                   (frame-info-adjusted-height finfo)))

;;print-seg : segment -> void
(define (print-seg seg)
  (printf "(segment type:~a x:~a y:~a width:~a height:~a color:~a\n"
          (event-type (segment-event seg))
          (segment-x seg)
          (segment-y seg)
          (segment-width seg)
          (segment-height seg)
          (segment-color seg)))

;;seg-in-vregion : viewable-region segment -> bool
(define (seg-in-vregion vregion)
  (λ (seg)
    (in-viewable-region vregion
                        (segment-x seg)
                        (segment-y seg)
                        (segment-width seg)
                        (segment-height seg))))

;;calc-seg-x : event process-timeline trace uint float -> uint
(define (calc-seg-x evt tr modifier)
  (floor (* (relative-time tr (event-start-time evt))
            modifier)))

;;calc-seg-width : float event -> uint
(define (calc-seg-width modifier evt)
  (case (event-type evt)
    [(start-work start-0-work) (max MIN-SEG-WIDTH (* modifier (- (event-end-time evt)
                                                                 (event-start-time evt))))]
    [else MIN-SEG-WIDTH]))

;Finds the segment for given x and y mouse coordinates
;;find-seg-for-coords : uint uint interval-map -> segment
(define (find-seg-for-coords x y index)
  (let ([xmap (interval-map-ref index y #f)])
    (if xmap
        (interval-map-ref xmap x #f)
        #f)))

;;find-fid-for-coords : uint uint (listof drawable-node) -> drawable-node
(define (find-node-for-coords x y nodes)
  (define node-l (filter (λ (n)
                               (define n-x (drawable-node-x n))
                               (define n-y (drawable-node-y n))
                               (define n-w (drawable-node-width n))
                               (and (n-x . < . x)
                                    (n-y . < . y)
                                    (x . < . (+ n-x n-w))
                                    (y . < . (+ n-y n-w))))
                          (remove-duplicates (flatten nodes))))
  (cond
    [(empty? node-l)
     #f]
    [(= 1 (length node-l))
     (car node-l)]
    [else
     (error 'find-node-for-coords "Multiple nodes found for coords: ~s ~s, ~s" x y node-l)]))

;;find-fid-for-coords : x y ??(listof (listof nodes)) by depth?? viewable-region -> fid
(define (find-fid-for-coords x y nodes vregion)
  (define n (find-node-for-coords x y nodes))
  (if n
      (event-user-data (node-data (drawable-node-node n)))
      #f))

;;first-seg-for-fid : future-id (listof segments) -> segment
(define (first-seg-for-fid fid segs)
  (first
   (sort
    (filter (λ (s) (define seg-fid (event-future-id (segment-event s)))
              (and seg-fid fid (= fid seg-fid))) segs)
    < #:key (λ (s) (event-start-time (segment-event s))))))

;;calc-adjusted-width : uint trace -> uint
(define (calc-adjusted-width w tr)
  (define baseModifier (/ w (- (trace-end-time tr) (trace-start-time tr))))
  (define max-x-extent (for*/fold ([x 0]) ([tl (in-list (trace-proc-timelines tr))]
                                           [evt (in-list (process-timeline-events tl))])
                         (max (+ (calc-seg-x evt tr baseModifier)
                                 (calc-seg-width baseModifier evt))
                              x)))
  (- (- w (- max-x-extent w)) MIN-SEG-WIDTH))

;;calc-row-mid-y : uint uint trace -> uint
(define (calc-row-mid-y proc-index row-height tr)
  (case proc-index 
    [(gc) 0]
    [else
     (define PADDING 2)
     (floor (- (+ (* proc-index row-height)
                  (/ row-height 2))
               PADDING))]))

;Gets the center of a circle with (xleft, ytop) as the top-left coordinate.
;;calc-center : uint uint uint -> (values uint uint)
(define (calc-center xleft ytop diameter)
  (let ([rad (floor (/ diameter 2))])
    (values (+ xleft rad)
            (+ ytop rad))))

;;segs-equal-or-after : float (listof segment) -> (listof segment)
(define (segs-equal-or-later real-time segs)
  (let loop ([sgs segs])
    (cond
      [(null? sgs) '()]
      [(>= (event-start-time (segment-event (car sgs))) real-time) sgs]
      [else (loop (cdr sgs))])))

;;segment-edge : segment -> uint
(define (segment-edge seg)
  (define evt (segment-event seg))
  (if (event-has-duration? evt)
      (segment-x seg)
      (+ (segment-x seg) (segment-width seg))))

(define (find-most-recent-and-next segs time)
  (let loop ([ss segs])
    (cond
      [(empty? (cddr ss))
       (values (first ss) (second ss) ss)]
      [(> (event-start-time (segment-event (second ss))) time)
       (values (first ss) (second ss) ss)]
      [else
       (loop (cdr ss))])))

;;timeline-tick-label-pict : real -> pict
(define (timeline-tick-label-pict rel-time)
  (text-block-pict (format "~a ms" (real->decimal-string rel-time))
                   #:backcolor (timeline-tick-label-backcolor)
                   #:forecolor (timeline-tick-label-forecolor)
                   #:padding 3))

;;calc-ticks : (listof segment) float trace -> (listof timeline-tick)
(define (calc-ticks segs timeToPixMod tr)
  (define LABEL-PAD 3)
  (define trace-start (inexact->exact (trace-start-time tr)))
  (define segs-len (length segs))
  (define-values (lt lx tks _ __)
    (for/fold ([last-time trace-start]
               [last-x 0]
               [ticks '()]
               [last-label-x-extent 0]
               [remain-segs segs])
              ([i (in-range 0 (floor (/ (- (trace-end-time tr)
                                           trace-start)
                                        DEFAULT-TIME-INTERVAL)))])
      (define tick-rel-time (* (add1 i) DEFAULT-TIME-INTERVAL))
      (define tick-time (+ trace-start tick-rel-time))
      (define want-x (+ last-x (* DEFAULT-TIME-INTERVAL timeToPixMod)))
      (define-values (most-recent-seg next-seg r-segs)
        (find-most-recent-and-next remain-segs tick-time))
      (define most-recent-evt (segment-event most-recent-seg))
      (define most-recent-time (inexact->exact (event-start-time most-recent-evt)))
      (define next-evt (segment-event next-seg))
      (define next-evt-time (inexact->exact (event-start-time next-evt)))
      (define most-recent-edge (segment-edge most-recent-seg))
      (define next-edge (segment-x next-seg))
      (define tick-x
        (cond
          [(= most-recent-time tick-time) (segment-x most-recent-seg)]
          [(= (segment-x next-seg) (add1 (+ (segment-x most-recent-seg) (segment-width most-recent-seg))))
           (+ (segment-x most-recent-seg) (segment-width most-recent-seg))]
          [else
           (define start-x (max most-recent-edge last-x))
           (define start-time (max most-recent-time last-time))
           (define size-mod (/ (- next-edge start-x) (- next-evt-time start-time)))
           (define x-offset (ceiling (* (- tick-time start-time) size-mod)))
           (round (+ start-x x-offset))]))
      (define show-tick? ((- tick-x last-x) . >= . TIMELINE-MIN-TICK-PADDING))
      (define show-label?
        (if (not show-tick?)
            #f
            (>= tick-x (+ last-label-x-extent LABEL-PAD))))
      (define new-label-x-extent
        (if show-label?
            (+ tick-x (pict-width (timeline-tick-label-pict tick-rel-time)))
            last-label-x-extent))
      (if show-tick?
          (values tick-time
                  tick-x
                  (cons (timeline-tick tick-x tick-time tick-rel-time show-label?) ticks)
                  new-label-x-extent
                  r-segs)
          (values tick-time
                  last-x
                  ticks
                  new-label-x-extent
                  r-segs))))
  tks)

;;calc-process-timespan-lines : trace (listof segment) uint -> (listof (uint . uint))
(define (calc-process-timespan-lines trace segs max-x)
  (for/list ([tl (in-list (trace-proc-timelines trace))])
    (define sgs (filter (λ (s) (equal? (process-timeline-proc-id tl)
                                        (event-proc-id (segment-event s))))
                         segs))
    (cond
      [(empty? sgs) (cons 0 max-x)]
      [else
       (cons (segment-x (car sgs))
             (segment-x (last sgs)))])))

;;get-first-future-seg : seg -> seg
(define (get-first-future-seg seg)
  (let loop ([cur seg])
    (if (segment-prev-future-seg cur)
        (loop (segment-prev-future-seg cur))
        cur)))

;;get-first-future-seg-in-region : viewable-region segment -> segment
(define (get-first-future-seg-in-region vregion seg)
  (define prev-seg (get-seg-previous-to-vregion vregion seg))
    (if ((seg-in-vregion vregion) prev-seg)
        prev-seg
        (segment-next-future-seg prev-seg)))

;; get-seg-previous-to-vregion : viewable-region segment -> segment
(define (get-seg-previous-to-vregion vregion seg)
  (let loop ([cur seg])
    (define prev (segment-prev-future-seg cur))
    (cond
      [(or (not prev) (not ((seg-in-vregion vregion) cur))) cur]
      [else (loop prev)])))

;;Set pixel widths of segments with variable widths, e.g.
;;work and GC events
;;adjust-variable-width-segs! : (listof segment) uint -> void
(define (adjust-variable-width-segs! segs max-x)
  (cond
    [(empty? segs) void]
    [else
     (define cur (car segs))
     (case (event-type (segment-event cur))
       [(start-work start-0-work)
        (define next-seg (segment-next-proc-seg cur))
        ;Because we are truncating logs after they reach a certain size,
        ;next-seg could be #f (where before it was safe to assume that a work segment
        ;was always followed by another segment).
        (define x-end (if next-seg
                          (segment-x next-seg)
                          max-x))
        (set-segment-width! cur (max MIN-SEG-WIDTH
                                     (- x-end (segment-x cur))))
        (adjust-variable-width-segs! (cdr segs) max-x)]
       [(gc)
        (cond
          [(empty? (cdr segs)) void]
          [else
           (set-segment-width! cur (max MIN-SEG-WIDTH
                                        (- (segment-x (car (cdr segs))) (segment-x cur))))
           (adjust-variable-width-segs! (cdr segs) max-x)])]
       [else (adjust-variable-width-segs! (cdr segs) max-x)])]))

;;connect-segments! : (listof segment) -> void
(define (connect-segments! segs)
  (for ([s (in-list segs)])
    (define evt (segment-event s))
    (define (segment-of-evt getter)
      (define x (getter evt))
      (and x (event-segment x)))
    (set-segment-prev-proc-seg! s (segment-of-evt event-prev-proc-event))
    (set-segment-next-proc-seg! s (segment-of-evt event-next-proc-event))
    (set-segment-prev-future-seg! s (segment-of-evt event-prev-future-event))
    (set-segment-next-future-seg! s (segment-of-evt event-next-future-event))
    (set-segment-prev-targ-future-seg! s (segment-of-evt event-prev-targ-future-event))
    (set-segment-next-targ-future-seg! s (segment-of-evt event-next-targ-future-event))))

;;build-seg-layout : flonum (listof event) trace -> (values (listof segment) uint uint)
(define (build-seg-layout timeToPixModifier events tr max-y)
  (define num-tls (length (trace-proc-timelines tr)))
  (define last-right-edges (build-vector num-tls (λ (n) 0)))
  (define-values (sgs d x-extent)
    (for/fold ([segs '()]
               [delta 0]
               [largest-x 0]) ([evt (in-list events)])
      (define is-gc-evt? (equal? (event-type evt) 'gc))
      (define last-right-edge (if is-gc-evt?
                                  largest-x
                                  (vector-ref last-right-edges (event-proc-index evt))))
      (define wanted-offset (+ delta (* DEFAULT-TIMELINE-WIDTH
                                        (inexact->exact
                                         (/ (- (event-start-time evt) (trace-start-time tr))
                                            (- (trace-end-time tr) (trace-start-time tr)))))))
      (define-values (offset new-delta)
        (if (last-right-edge . <= . wanted-offset)
            (values wanted-offset delta)
            (values last-right-edge (+ delta (- last-right-edge wanted-offset)))))
      (define radius (if is-gc-evt? 0 (/ MIN-SEG-WIDTH 2)))
      (define segw MIN-SEG-WIDTH)
      (define segh (cond
                     [is-gc-evt? max-y]
                     [else MIN-SEG-WIDTH]))
      (define seg (segment evt
                           (round offset)
                           (- (calc-row-mid-y (event-proc-index evt) TIMELINE-ROW-HEIGHT tr) radius)
                           segw
                           segh
                           (get-event-color (event-type evt))
                           (get-event-opacity (event-type evt))
                           #f
                           #f
                           #f
                           #f
                           #f
                           #f
                           #f))
      (set-event-segment! evt seg)
      (when (not is-gc-evt?)
        (vector-set! last-right-edges (event-proc-index evt) (+ offset segw)))
      (values (cons seg segs)
              new-delta
              (max largest-x (+ offset segw) #;last-right-edge))))
  (values sgs x-extent))

;;calc-segments : trace uint uint -> (values frame-info (listof segment))
(define (calc-segments tr)
  (define evts (trace-all-events tr))
  (define timeToPixModifier (/ DEFAULT-TIMELINE-WIDTH (- (trace-end-time tr) (trace-start-time tr))))
  (define max-y (* TIMELINE-ROW-HEIGHT (length (trace-proc-timelines tr))))
  (define-values (segments x)
    (build-seg-layout timeToPixModifier evts tr max-y))
  (define max-x (+ MIN-SEG-WIDTH (round x)))
  (define ordered-segs (reverse segments))
  (connect-segments! ordered-segs)
  (adjust-variable-width-segs! ordered-segs max-x)
  (define ticks (calc-ticks ordered-segs timeToPixModifier tr))
  (values (frame-info max-x
                      max-y
                      TIMELINE-ROW-HEIGHT
                      timeToPixModifier
                      ticks
                      (calc-process-timespan-lines tr ordered-segs max-x))
          ordered-segs))

;;pict-for-segment : segment -> pict
(define (pict-for-segment seg)
  (unless (segment-p seg)
    (define p (if (event-has-duration? (segment-event seg))
                  (rect-pict (segment-color seg)
                             (timeline-event-strokecolor)
                             (segment-width seg)
                             (segment-height seg)
                             #:stroke-width .5)
                  (circle-pict (segment-color seg)
                               (timeline-event-strokecolor)
                               MIN-SEG-WIDTH
                               #:stroke-width .5)))
    (set-segment-p! seg (if (< (segment-opacity seg) 1)
                            (cellophane p (segment-opacity seg))
                            p)))
  (segment-p seg))

;;draw-ruler-on : pict viewable-region frameinfo -> pict
(define (draw-ruler-on base vregion frameinfo)
  (for/fold ([pct base]) ([tick (in-list (filter (λ (t) (in-viewable-region-horiz vregion (timeline-tick-x t)))
                                                 (frame-info-timeline-ticks frameinfo)))])
    (define cur-x (timeline-tick-x tick))
    (define pinnedline
      (pin-over pct
                (- cur-x (viewable-region-x vregion))
                0
                (linestyle 'dot
                           (colorize (vline 1
                                            (viewable-region-height vregion))
                                     (timeline-tick-color)))))
    (if (timeline-tick-show-label? tick)
        (pin-over pinnedline
                  (- cur-x (viewable-region-x vregion))
                  3
                  (timeline-tick-label-pict (timeline-tick-rel-time tick)))
        pinnedline)))

;;draw-row-lines-on : pict viewable-region trace frameinfo -> pict
(define (draw-row-lines-on base vregion tr finfo opacity)
  (define num-tls (length (trace-proc-timelines tr)))
  (pin-over base
            0
            0
            (for/fold ([pct base]) ([tl (in-list (filter (λ (tline)
                                                           (define midy (calc-row-mid-y (process-timeline-proc-index tline)
                                                                                        (frame-info-row-height finfo)
                                                                                        tr))
                                                           (define topy (- midy (frame-info-row-height finfo)))
                                                           (define boty (+ midy (frame-info-row-height finfo)))
                                                           (or (in-viewable-region-vert? vregion topy)
                                                               (in-viewable-region-vert? vregion boty)))
                                                         (trace-proc-timelines tr)))])
              (let* ([line-coords (list-ref (frame-info-process-line-coords finfo)
                                           (process-timeline-proc-index tl))]
                     [line-start (car line-coords)]
                     [line-end (cdr line-coords)]
                     [vregion-start (viewable-region-x vregion)]
                     [vregion-end (viewable-region-x-extent vregion)]
                     [start-x (cond
                                [(< line-start vregion-start) 0]
                                [(between line-start vregion-start vregion-end)
                                 (- line-start vregion-start)]
                                [else vregion-end])]
                     [end-x (cond
                              [(< line-end vregion-start) 0]
                              [(between line-end vregion-start vregion-end)
                               (- line-end vregion-start)]
                              [else vregion-end])]
                     [index (process-timeline-proc-index tl)]
                     [proc-name (if (zero? index)
                                    "Thread 0 (Runtime Thread)"
                                    (format "Thread ~a" (process-timeline-proc-id tl)))]
                     [proc-title (text-block-pict proc-name
                                                  #:backcolor (header-backcolor)
                                                  #:forecolor (header-forecolor)
                                                  #:padding HEADER-PADDING
                                                  #:opacity opacity
                                                  #:width (viewable-region-width vregion))])
                (draw-stack-onto pct
                                 (at 0
                                     (- (* index (frame-info-row-height finfo)) (viewable-region-y vregion))
                                     (colorize (hline (viewable-region-width vregion) 1) (timeline-baseline-color)))
                                 (at 0
                                     (+ (+ (- (* index (frame-info-row-height finfo)) (viewable-region-y vregion))
                                           (- (frame-info-row-height finfo) (pict-height proc-title)))
                                        1)
                                     proc-title)
                                 (at start-x
                                     (- (calc-row-mid-y index
                                                        (frame-info-row-height finfo)
                                                        tr)
                                        (viewable-region-y vregion))
                                     (colorize (hline (- end-x start-x) 1)
                                               (timeline-event-baseline-color))))))))

;Magnifies a segment's pict (dot or square) to make
;it stand out when hovered over with the mouse pointer.
;;make-stand-out-pict : segment -> pict
(define (make-stand-out-pict seg)
  (case (event-type (segment-event seg))
    [(start-work start-0-work) (scale (pict-for-segment seg) 1 2)]
    [(gc) (cellophane (pict-for-segment seg) 1)]
    [else (scale (pict-for-segment seg) 2)]))

;;frame-bg : viewable-region frame-info trace -> pict
(define (frame-bg vregion finfo tr)
  (draw-frame-bg-onto (colorize (filled-rectangle (viewable-region-width vregion)
                                                  (frame-info-adjusted-height finfo))
                                (timeline-frame-bg-color))
                      vregion
                      finfo
                      tr
                      TIMELINE-HEADER-OPACITY))

;;draw-frame-bg-onto : pict viewable-region frameinfo trace -> pict
(define (draw-frame-bg-onto base vregion finfo tr opacity)
  (let ([with-ruler (draw-ruler-on base vregion finfo)])
    (draw-row-lines-on with-ruler vregion tr finfo opacity)))

;;timeline-pict : (listof indexed-future-event) [viewable-region] [integer] -> pict
(define (timeline-pict logs
                       #:x [x #f]
                       #:y [y #f]
                       #:width [width #f]
                       #:height [height #f]
                       #:selected-event-index [selected-event-index #f])
  (define tr (build-trace logs))
  (define-values (finfo segments) (calc-segments tr))
  (define vregion (if x
                      (viewable-region x y width height)
                      (viewable-region 0 0 (frame-info-adjusted-width finfo) (frame-info-adjusted-height finfo))))
  (timeline-pict-for-trace-data vregion
                                tr
                                finfo
                                segments
                                #:selected-event-index selected-event-index))

;;timeline-pict : (or viewable-region #f) trace frame-info (listof segment) -> pict
(define (timeline-pict-for-trace-data vregion
                                      tr
                                      finfo
                                      segments
                                      #:selected-event-index [selected-event-index #f])
  (define vr (if (not vregion)
                 (viewable-region 0
                                  0
                                  (frame-info-adjusted-width finfo)
                                  (frame-info-adjusted-height finfo))
                 vregion))
  (define tp (for/fold ([pct (frame-bg vr finfo tr)])
               ([seg (in-list (filter (seg-in-vregion vr) segments))])
               (pin-over pct
                         (- (segment-x seg) (viewable-region-x vr))
                         (- (segment-y seg) (viewable-region-y vr))
                         (pict-for-segment seg))))
  (cond
    [selected-event-index
     (define overlay (timeline-overlay vregion
                                       #f
                                       (list-ref segments selected-event-index)
                                       finfo
                                       tr))
     (pin-over tp
               0
               0
               overlay)]
    [else tp]))

;;draw-connection : viewable-region segment segment pict string [uint bool symbol] -> pict
(define (draw-connection vregion
                         start
                         end
                         base-pct
                         color
                         #:width [width 1]
                         #:with-arrow [with-arrow #f]
                         #:style [style 'solid])
  (let*-values ([(midx midy) (calc-center (- (segment-x start) (viewable-region-x vregion))
                                          (- (segment-y start) (viewable-region-y vregion))
                                          MIN-SEG-WIDTH)]
                [(nextx nexty) (calc-center (- (segment-x end) (viewable-region-x vregion))
                                            (- (segment-y end) (viewable-region-y vregion))
                                            MIN-SEG-WIDTH)]
                [(dx dy) (values (- nextx midx) (- nexty midy))])
    (if (and (zero? dy)
             (or (not (eq? (segment-next-proc-seg start) end))
                 (< dx CONNECTION-LINE-HAT-THRESHOLD)))
        (let* ([dxa (/ dx 2)]
               [dya (- HAT-HEIGHT CONNECTION-LINE-HAT-THRESHOLD)]
               [breakx (+ midx dxa)]
               [breaky (+ midy dya)])
          (draw-line-onto (draw-line-onto base-pct
                                          midx
                                          midy
                                          breakx
                                          breaky
                                          color
                                          #:width width
                                          #:style style)
                          breakx
                          breaky
                          nextx
                          nexty
                          color
                          #:width width
                          #:with-arrow with-arrow
                          #:style style))
        (draw-line-onto base-pct
                        midx
                        midy
                        nextx
                        nexty
                        color
                        #:width width
                        #:with-arrow with-arrow
                        #:style style))))

;;draw-arrows : pict viewable-region segment -> pict
(define (draw-arrows base-pct vregion seg)
  (let ([fst (get-seg-previous-to-vregion vregion seg)])
    (let loop ([pct base-pct]
               [cur-seg fst]
               [event-scope #f])
        (if (not cur-seg)
            pct
            (let ([next (segment-next-future-seg cur-seg)])
              (define cur-evt (segment-event cur-seg))
              ;(define next-evt (segment-event next))
              (define in-rtcall-scope 
                (cond 
                  [(and (symbol? event-scope) (or (runtime-block-event? cur-evt)
                                                  (runtime-sync-event? cur-evt))) #f]
                  [(worker-sync-event? cur-evt) 'sync] 
                  [(worker-block-event? cur-evt) 'block] 
                  [else event-scope]))
              (let* ([next-targ (segment-next-targ-future-seg cur-seg)]
                     [prev-targ (segment-prev-targ-future-seg cur-seg)]
                     [ftl-arrows (if (not next)
                                     pct
                                     (draw-connection vregion
                                                      cur-seg
                                                      next
                                                      pct
                                                      (case in-rtcall-scope 
                                                        [(block) (get-event-color 'block)]
                                                        [(sync) (get-event-color 'sync)]
                                                        [else (event-connection-line-color)])
                                                      #:width 2))]
                     [prev-targ-arr (if (not prev-targ)
                                        ftl-arrows
                                        (draw-connection vregion
                                                         prev-targ
                                                         cur-seg
                                                         ftl-arrows
                                                         (event-target-future-line-color)
                                                         #:with-arrow #t
                                                         #:style 'dot))]
                     [next-targ-arr (if (not next-targ)
                                        prev-targ-arr
                                        (draw-connection vregion
                                                         cur-seg
                                                         next-targ
                                                         prev-targ-arr
                                                         (event-target-future-line-color)
                                                         #:with-arrow #t
                                                         #:style 'dot))])
                (if (and next
                         ((seg-in-vregion vregion) next))
                    (loop next-targ-arr next in-rtcall-scope)
                    next-targ-arr)))))))

;Draws the pict that is layered on top of the exec. timeline canvas
;to highlight a specific future's event sequence
;;timeline-overlay : uint uint (or segment #f) (or segment #f) frame-info trace -> pict
(define (timeline-overlay vregion tacked hovered finfo tr)
  (define-values (width height) (values (viewable-region-width vregion)
                                        (viewable-region-height vregion)))
  (define base (blank (viewable-region-width vregion)
                      (viewable-region-height vregion)))
  (define-values (picked-seg showing-tacked)
    (if tacked (values tacked #t) (values hovered #f)))
  (cond
    [picked-seg
     (define bg base)
     (define aseg-rel-x (- (segment-x picked-seg) (viewable-region-x vregion)))
     (define aseg-rel-y (- (segment-y picked-seg) (viewable-region-y vregion)))
     (define emphasized (make-stand-out-pict picked-seg))
     (case (event-type (segment-event picked-seg))
       [(gc)
        (pin-over bg aseg-rel-x aseg-rel-y emphasized)]
       [else
        (let* ([line (pin-over bg
                               (- (+ aseg-rel-x
                                     (/ (segment-width picked-seg) 2))
                                  2)
                               0
                               (colorize (vline 1 height) (hover-tickline-color)))]
               [width-dif (/ (- (pict-width emphasized) (segment-width picked-seg)) 2)]
               [height-dif (/ (- (pict-height emphasized) (segment-height picked-seg)) 2)]
               [magnified (pin-over line
                                    (- aseg-rel-x width-dif)
                                    (- aseg-rel-y height-dif)
                                    emphasized)]
               [hover-magnified (if (and showing-tacked
                                         hovered
                                         (not (eq? hovered tacked)))
                                    (let* ([hmag (make-stand-out-pict hovered)]
                                           [hwidth-dif (/ (- (pict-width hmag)
                                                             (pict-width (pict-for-segment hovered)))
                                                          2)]
                                           [hheight-dif (/ (- (pict-height hmag)
                                                              (pict-height (pict-for-segment hovered)))
                                                           2)])
                                      (pin-over magnified
                                                (- (- (segment-x hovered) (viewable-region-x vregion)) hwidth-dif)
                                                (- (- (segment-y hovered) (viewable-region-y vregion)) hheight-dif)
                                                hmag))
                                    magnified)]
               [arrows (draw-arrows hover-magnified vregion picked-seg)])
          arrows)])]
    [else base]))

;Draw a line from one node on the creation graph to another
;;line-from : drawable-node drawable-node pict viewable-region -> pict
(define (line-from start end pct minx miny)
  (define par-center (drawable-node-center start))
  (define child-center (drawable-node-center end))
  (draw-line-onto pct
                  (- (point-x par-center) minx)
                  (- (point-y par-center) miny)
                  (- (point-x child-center) minx)
                  (- (point-y child-center) miny)
                  (create-graph-edge-color)
                  #:width 1
                  #:style 'dot))

;Draws a circle for a node on the creation graph
;;node-pict : drawable-node -> pict
(define (node-pict dnode)
  (let* ([ndata (node-data (drawable-node-node dnode))]
         [ntext (if (equal? ndata 'runtime-thread)
                    "RTT"
                    (format "~a" (event-user-data ndata)))])
    (cc-superimpose (circle-pict (create-graph-node-backcolor)
                                 (create-graph-node-strokecolor)
                                 (drawable-node-width dnode))
                    (colorize (text ntext) (create-graph-node-forecolor)))))

;;creation-tree-pict : (listof indexed-future-event) [enni] [enni] [enni] [enni] [enni] [enni] [enni] -> pict
(define (creation-tree-pict events
                             #:x [x #f]
                             #:y [y #f]
                             #:width [width #f]
                             #:height [height #f]
                             #:node-width [node-width #f]
                             #:padding [padding #f]
                             #:zoom [zoom CREATE-GRAPH-MIN-ZOOM])
  (define tr (build-trace events))
  (define node-diam (if node-width
                        node-width
                        CREATE-GRAPH-NODE-DIAMETER))
  (define graph-padding (if padding
                            padding
                            CREATE-GRAPH-PADDING))
  (define layout (draw-tree (trace-creation-tree tr)
                            #:node-width node-diam
                            #:padding graph-padding
                            #:zoom zoom))
  (define vregion (if x
                      (viewable-region x y width height)
                      #f))
  (draw-creategraph-pict vregion layout))


;;draw-creategraph-pict : (or/c viewable-region #f) tree-layout -> pict
;; if vregion is #f, return a pict that includes the entire tree
(define (draw-creategraph-pict vregion layout)
  (define rt-root (first (graph-layout-nodes layout)))
  (define width (inexact->exact (floor (graph-layout-width layout))))
  (define height (inexact->exact (floor (graph-layout-height layout))))
  (define base (if vregion
                   (blank (viewable-region-width vregion) (viewable-region-height vregion))
                   (blank)))
  (define minx (if vregion (viewable-region-x vregion) 0))
  (define miny (if vregion (viewable-region-y vregion) 0))
  (define viewable-nodes (if vregion
                             (filter (λ (n) (in-viewable-region vregion
                                                                (drawable-node-x n)
                                                                (drawable-node-y n)
                                                                (drawable-node-width n)
                                                                (drawable-node-width n)))
                                     (graph-layout-nodes layout))
                             (graph-layout-nodes layout)))
  (define with-arrows
    (let ([arrow-pct (for/fold ([pct base]) ([node (in-list (graph-layout-nodes layout))])
                       (for/fold ([p pct]) ([child (in-list (drawable-node-children node))])
                         (line-from node child p minx miny)))])
      (for/fold ([pct arrow-pct]) ([node (in-list viewable-nodes)])
        (pin-over pct
                  (- (drawable-node-x node) minx)
                  (- (drawable-node-y node) miny)
                  (node-pict node)))))
  (if vregion
      with-arrows
      (panorama with-arrows)))

(define (zoom-level->factor zoom-level)
   (+ 1.0 (* (- zoom-level CREATE-GRAPH-DEFAULT-ZOOM)
                       CREATE-GRAPH-ZOOM-FACTOR)))

;;graph-overlay-pict : drawable-node trace graph-layout -> pict
(define (graph-overlay-pict hover-node tr layout vregion scale-factor)
  (define (root-sym-or-first-evt n) (node-data (drawable-node-node n)))
  (cond
    [(or (not hover-node) (equal? (root-sym-or-first-evt hover-node) 'runtime-thread))
      #f]
    [else
     (define fid (event-user-data (root-sym-or-first-evt hover-node)))
     (define ri (hash-ref (trace-future-rtcalls tr) fid #f))
     (cond
       [(not ri) #f]
       [else
        (define block-ops (sort (hash-keys (rtcall-info-block-hash ri))
                                >
                                #:key (λ (p)
                                        (hash-ref (rtcall-info-block-hash ri) p))))
        (define sync-ops (sort (hash-keys (rtcall-info-sync-hash ri))
                               >
                               #:key (λ (op)
                                       (hash-ref (rtcall-info-sync-hash ri) op))))
        (define-values (node-origin-x node-origin-y)
          (values (* (- (drawable-node-x hover-node) (viewable-region-x vregion)) scale-factor)
                  (* (- (drawable-node-y hover-node) (viewable-region-y vregion)) scale-factor)))
        (define-values (center-x center-y)
          (values (+ node-origin-x (/ (* (drawable-node-width hover-node) scale-factor) 2))
                  (+ node-origin-y (/ (* (drawable-node-width hover-node) scale-factor) 2))))
        (define x (+ center-x CREATE-GRAPH-NODE-DIAMETER))
        (define-values (pct yacc)
          (for/fold ([p (pin-over (blank (viewable-region-width vregion) (viewable-region-height vregion))
                                  node-origin-x
                                  node-origin-y
                                  (scale (node-pict hover-node) scale-factor))]
                     [yacc node-origin-y])
            ([rtcall (in-list (append (map (λ (op) (cons 'block op)) block-ops)
                                      (map (λ (op) (cons 'sync op)) sync-ops)))])
            (define evt-type (car rtcall))
            (define prim (cdr rtcall))
            (define the-hash (if (equal? evt-type 'block) (rtcall-info-block-hash ri) (rtcall-info-sync-hash ri)))
            (define txtp (text-pict (format "~a (~a)"
                                            (symbol->string prim)
                                            (hash-ref the-hash prim))
                                    #:color (get-event-forecolor evt-type)))
            (define txtbg (rect-pict (get-event-color evt-type)
                                     (create-graph-edge-color)
                                     (+ (pict-width txtp) (* TOOLTIP-MARGIN 2))
                                     (+ (pict-height txtp) (* TOOLTIP-MARGIN 2))
                                     #:stroke-width .5))
            (values
             (pin-over (draw-line-onto p
                                       center-x
                                       center-y
                                       x
                                       yacc
                                       (create-graph-edge-color))
                       x
                       yacc
                       (pin-over txtbg
                                 TOOLTIP-MARGIN
                                 TOOLTIP-MARGIN
                                 txtp))
             (+ yacc (pict-height txtbg) CREATE-GRAPH-PADDING))))
        pct])]))
