#lang racket/base
(provide (struct-out src-wrap)
         files
         sexp-files)

(require (for-syntax racket/base))

(define-struct src-wrap (obj srcloc)
  #:transparent)

(define-syntax (retain-src stx)
  
  (define (to-src-wrap stx)
    (cond
      [(syntax? stx)
       #`(src-wrap #,(to-src-wrap (syntax-e stx))
                   (vector '#,(syntax-source stx)
                           #,(syntax-line stx)
                           #,(syntax-column stx)
                           #,(syntax-position stx)
                           #,(syntax-span stx)))]
      [(pair? stx)
       #`(cons #,(to-src-wrap (car stx))
               #,(to-src-wrap (cdr stx)))]
      [else 
       #`'#,stx]))
                   
  (syntax-case stx ()
    [(_ args ...)
     (with-syntax ([(args ...) 
                    (map to-src-wrap
                         (syntax->list #'(args ...)))])
       #'(list args ...))]))

(define files
  (list (list "info.rkt"
              (retain-src
               info
               (define drracket-tools (list (list "tool.rkt")))))
        (list "tool.rkt"
              (retain-src
               racket/base
               (require drracket/tool
                        racket/class
                        racket/gui/base
                        racket/unit
                        mrlib/switchable-button)
               (provide tool@)
               
               (define secret-key "egg")
               (define to-insert "easter ")
               
               (define tool@
                 (unit
                   (import drracket:tool^)
                   (export drracket:tool-exports^)
                   
                   (define easter-egg-mixin
                     (mixin ((class->interface text%)) ()
                       
                       (inherit begin-edit-sequence
                                end-edit-sequence
                                insert
                                get-text)
                       
                       (define/augment (on-insert start len)
                         (begin-edit-sequence))
                       (define/augment (after-insert start len)
                         (check-range (max 0 (- start (string-length secret-key)))
                                      (+ start len))
                         (end-edit-sequence))
                       
                       (define/augment (on-delete start len)
                         (begin-edit-sequence))
                       (define/augment (after-delete start len)
                         (check-range (max 0 (- start (string-length secret-key)))
                                      start)
                         (end-edit-sequence))
                       
                       (define/private (check-range start stop)
                         (let/ec k
                           (for ([x (in-range start stop)])
                             (define after-x 
                               (get-text x (+ x (string-length secret-key))))
                             (when (string=? after-x secret-key)
                               (define before-x 
                                 (get-text (max 0 (- x (string-length to-insert))) x))
                               (unless (string=? before-x to-insert)
                                 (insert to-insert x x)
                                 (k (void)))))))
                       
                       (super-new)))
                   
                   (define reverse-button-mixin
                     (mixin (drracket:unit:frame<%>) ()
                       (super-new)
                       (inherit get-button-panel
                                get-definitions-text)
                       (inherit register-toolbar-button)
                       
                       (let ([btn
                              (new switchable-button%
                                   [label "Reverse Definitions"]
                                   [callback (λ (button) 
                                               (reverse-content
                                                (get-definitions-text)))]
                                   [parent (get-button-panel)]
                                   [bitmap reverse-content-bitmap])])
                         (register-toolbar-button btn #:number 11)
                         (send (get-button-panel) change-children
                               (λ (l)
                                 (cons btn (remq btn l)))))))
                   
                   (define reverse-content-bitmap
                     (let* ([bmp (make-bitmap 16 16)]
                            [bdc (make-object bitmap-dc% bmp)])
                       (send bdc erase)
                       (send bdc set-smoothing 'smoothed)
                       (send bdc set-pen "black" 1 'transparent)
                       (send bdc set-brush "blue" 'solid)
                       (send bdc draw-ellipse 2 2 8 8)
                       (send bdc set-brush "red" 'solid)
                       (send bdc draw-ellipse 6 6 8 8)
                       (send bdc set-bitmap #f)
                       bmp))
                   
                   (define (reverse-content text)
                     (for ([x (in-range 1 (send text last-position))])
                       (send text split-snip x))
                     (define snips
                       (let loop ([snip (send text find-first-snip)])
                         (if snip
                             (cons snip (loop (send snip next)))
                             '())))
                     (define released-snips
                       (for/list ([snip (in-list snips)]
                                  #:when (send snip release-from-owner))
                         snip))
                     (for ([x (in-list released-snips)])
                       (send text insert x 0 0)))
                   
                   (define (phase1) (void))
                   (define (phase2) (void))
                   
                   (drracket:get/extend:extend-definitions-text easter-egg-mixin)
                   (drracket:get/extend:extend-unit-frame reverse-button-mixin)))))))


(define (to-sexp x)
  (let loop ([sw x])
    (cond
      [(src-wrap? sw)
       (loop (src-wrap-obj sw))]
      [(pair? sw)
       (cons (loop (car sw)) (loop (cdr sw)))]
      [else sw])))

(define sexp-files
  (for/list ([file (in-list files)])
    (list (list-ref file 0)
          (to-sexp (car (list-ref file 1)))
          (to-sexp (cdr (list-ref file 1))))))
