#lang racket/base
(require racket/gui/base
         racket/class
         profile/sampler
         profile/render-text
         profile/analyzer
         framework/preferences)

#|

This file is loaded early in drscheme's startup when the PLTDRPROFILE
environment variable is setup. It sets up a profiler to examine drscheme
itself.

|#

(provide start-profile)

(define (start-profile super-custodian)
  (define drs-custodian (current-custodian))
  (define drs-main-thread (current-thread))
  (define profile-eventspace
    (parameterize ([current-custodian super-custodian])
      (make-eventspace)))
  (define frame (parameterize ([current-eventspace profile-eventspace])
                  (new frame% [label "Profiling"] [width 500] [height 550])))
  (define t (let ([t (new text%)])
              (send t lock #t)
              t))
  (define ec (new editor-canvas% [parent frame] [editor t]))
  (define bp (new horizontal-panel% [parent frame] [stretchable-height #f]))
  (define pause-b (new button% [label "Pause"] [parent bp] [callback (λ (a b) (pause))] [stretchable-width #t]))
  (define resume-b (new button% [label "Resume"] [parent bp] [callback (λ (a b) (resume))] [stretchable-width #t]))
  (define start-stop-b (new button% [label "Stop"] [parent bp] [callback (λ (a b) (start-stop))] [stretchable-width #t]))
  (define choice (new choice% [label #f] [choices '("12" "9")] [parent bp] [callback (λ (a b) (font-size))]))

  
  (define mb (new menu-bar% [parent frame]))
  (define edit-menu (new menu% [label "&Edit"] [parent mb]))
  (define sa (new menu-item% 
                  [parent edit-menu]
                  [label "Select al&l"]
                  [shortcut #\a] 
                  [callback (λ (x y) 
                              (send ec focus)
                              (send t set-position 0 (send t last-position)))]))
  (define copy (new menu-item%
                    [parent edit-menu]
                    [label "&Copy"]
                    [shortcut #\c]
                    [callback (λ (x y) (send t copy))]))
  
  (define (font-size)
    (let ([n (send choice get-string-selection)])
      (when n
        (send sd set-delta 'change-size (string->number n))
        (update-profile-report))))
  
  (define (update-buttons)
    (send resume-b enable (and current-sampler (not running?)))
    (send pause-b enable (and current-sampler running?))
    (send start-stop-b set-label (if current-sampler "Stop" "Start")))
  
  (define running? #f)
  (define current-sampler #f)
  (define (start-sampler)
    (let ([s (create-sampler (list drs-custodian drs-main-thread) 
                             #e0.05
                             super-custodian)])
      (set! current-sampler s)))
  (define (stop-sampler)
    (current-sampler 'stop)
    (set! current-sampler #f)
    (set! running? #f))

  (define sd (let ([sd (make-object style-delta%)])
               (send sd set-delta 'change-size 12)
               (send sd set-family 'modern)
               sd))
  
  (define updating-thread 
    (parameterize ([current-custodian super-custodian]
                   [current-eventspace profile-eventspace])
      (thread (λ ()
                (let loop ()
                  (queue-callback update-profile-report)
                  (sleep 5) 
                  (loop))))))
  
  (define (update-profile-report)
    (when current-sampler
      (send t begin-edit-sequence)
      (send t lock #f)
      (send t erase)
      (parameterize ([current-output-port (open-output-text-editor t)])
        (render (analyze-samples (current-sampler 'get-snapshots))))
      (send t change-style sd 0 (send t last-position))
      (send t set-position 0)
      (send t lock #t)
      (send t end-edit-sequence)))
  
  (define (resume) 
    (set! running? #t)
    (current-sampler 'resume)
    (update-buttons))
  (define (pause) 
    (set! running? #f)
    (current-sampler 'pause)
    (update-buttons)
    (update-profile-report))
  (define (start-stop) 
    (cond
      [current-sampler (stop-sampler)]
      [else 
       (start-sampler)
       (set! running? #t)])
    (update-profile-report)
    (update-buttons))

  (update-buttons)
  (send ec focus)
  (send frame show #t))
