
(module interlocking-components (lib "run.ss" "slideshow")
  (require (lib "class.ss")
           (lib "mred.ss" "mred"))
  
  (define-struct posn (x y) (make-inspector))
  
  (define between-space 20)

  (define main-piece-width 200)
  (define main-piece-height 200)
  (define right-piece-width 200)
  (define right-piece-height 200)
  (define below-piece-width (+ right-piece-width between-space main-piece-width))
  (define below-piece-height 200)
  (define below-interpose-piece-width below-piece-width)
  (define below-interpose-piece-height 50)

  (define main-piece-dark-color "crimson")
  (define main-piece-light-color "pink")
  (define right-piece-dark-color "medium blue")
  (define right-piece-light-color "sky blue")
  (define below-piece-dark-color "forest green")
  (define below-piece-light-color "pale green")
  (define below-interpose-piece-dark-color "purple")
  (define below-interpose-piece-light-color "plum")
  
  (define square-tooth-offset 50)
  (define square-tooth-height 60)
  (define square-tooth-width 50)
  (define square-tooth-accept-space 15)
  
  (define pointy-tooth-offset 50)
  (define pointy-tooth-width 100)
  (define pointy-tooth-height 50)
  (define pointy-tooth-accept-space 5)

  (define hex-tooth-offset 50)
  (define hex-tooth-width 100)
  (define hex-tooth-height 50)
  (define hex-tooth-accept-space 10)
  (define hex-tooth-inset 20)

  (slide/center
   (page-para/c
    "The following slide sequences was extracted"
    "from a talk on components and contracts"))
  
  (define (interlocking-components)
    (slide (make-orig #t #f ghost ghost ghost ghost ghost
                      "A day in the life of a component software developer."
                      "We start with one component (from web or somewhere)"))
    
    (slide (make-orig #t #f ident ident ghost ghost ghost
                      "... and compose it with other software to build a system."))
    
    (slide (make-orig #t #f ident ghost ident ghost ghost
                      "Except, of course, the composed pieces might not fit,"))
    
    (slide (make-orig #t #f ident ghost ghost ident ghost
                      "... so the programmers develop adapters."
                      "Now the program can be run, but what"
                      "happens when you run it?"))
    
    (slide (make-orig #t #f ghost ghost ghost ghost ident
                      "KABOOM!"))
    
    (slide (make-orig #t #f ident ghost ghost ident ghost
                      "What happened? Which component failed?"))
    
    (slide (make-orig #f #f ident ghost ghost ident ghost
                      "To figure that out, the programmer shouldn't have"
                      "to understand the details of all of the"
                      "component implementations."))
    
    (slide (make-orig #f #t ident ghost ghost ident ghost
                      "Instead the grey area, where the interface"
                      "contract specs are, should have enough"
                      "information to figure that out.")))
  
  (define (make-orig dark? bkg? right bot-hex bot-pointy bot-combined explosion . text)
    (let* ([below-pointy (below-pointy-piece dark?)]
           [components
            (lt-superimpose
             (at 0
                 0
                 (main-piece dark?))
             (at (+ main-piece-width between-space)
                 0 
                 (right (right-piece dark?)))
             (at 0 
                 (+ main-piece-height between-space)
                 (bot-hex (below-hex-piece dark?)))
             (at 0 
                 (+ main-piece-height between-space)
                 (bot-pointy (below-pointy-piece dark?)))
             (at 0
                 (+ main-piece-height between-space) 
                 (bot-combined (below-interpose-piece dark?)))
             (at 0
                 (+ main-piece-height between-space below-interpose-piece-height between-space) 
                 (bot-combined
                  below-pointy)))]
           [combined
            (cc-superimpose
             (if bkg?
                 (overlay-background 
                  "light gray"
                  (overlay-striped-background
                   "dark gray"
                   10 
                   components))
                 components)
             (explosion big-explosion-pict))])
      (vc-append
       40
       combined
       (apply page-para text))))

                                   
  (define big-explosion-pict (bitmap "big-explosion.jpg"))

  (define (at x y pict) (hc-append (blank x 0) (vc-append (blank 0 y) pict)))
  
  (define (main-piece dark?)
    (polygon
     (if dark? main-piece-dark-color main-piece-light-color)
     (make-posn 0 0)
     (make-posn main-piece-width 0)
     (offset-posns main-piece-width square-tooth-offset (square-tooth #t))
     (make-posn main-piece-width main-piece-height)
     (offset-posns pointy-tooth-offset main-piece-height (reverse (hex-tooth #t)))
     (make-posn 0 main-piece-height)
     (make-posn 0 0)))
  
  (define (below-pointy-piece dark?)
    (polygon
     (if dark? below-piece-dark-color below-piece-light-color)
     (make-posn 0 0)
     (offset-posns pointy-tooth-offset 0 (pointy-tooth #f))
     (make-posn below-piece-width 0)
     (make-posn below-piece-width below-piece-height)
     (make-posn 0 below-piece-height)
     (make-posn 0 0)))
  
  (define (below-hex-piece dark?)
    (polygon
     (if dark? below-piece-dark-color below-piece-light-color)
     (make-posn 0 0)
     (offset-posns hex-tooth-offset 0 (hex-tooth #f))
     (make-posn below-piece-width 0)
     (make-posn below-piece-width below-piece-height)
     (make-posn 0 below-piece-height)
     (make-posn 0 0)))
  
  (define (below-interpose-piece dark?)
    (polygon
     (if dark? below-interpose-piece-dark-color below-interpose-piece-light-color)
     (make-posn 0 0)
     (offset-posns pointy-tooth-offset 0 (hex-tooth #f))
     (make-posn below-interpose-piece-width 0)
     (make-posn below-interpose-piece-width below-interpose-piece-height)
     (offset-posns hex-tooth-offset below-interpose-piece-height (reverse (pointy-tooth #t)))
     (make-posn 0 below-interpose-piece-height)
     (make-posn 0 0)))
  
  (define (right-piece dark?)
    (polygon
     (if dark? right-piece-dark-color right-piece-light-color)
     (make-posn 0 0)
     (offset-posns 0 square-tooth-offset (square-tooth #f))
     (make-posn 0 right-piece-height)
     (make-posn right-piece-width right-piece-height)
     (make-posn right-piece-width 0)
     (make-posn 0 0)))
  
  (define (polygon color . points)
    (let ([flat-points (flatten points)])
      (when (null? flat-points)
        (error 'polygon "expected at least one point"))
      (dc
       (lambda (dc x y)
         (let ([old-brush (send dc get-brush)])
           (send dc set-brush (send the-brush-list find-or-create-brush color 'solid))
           (send dc draw-polygon 
                 (map (lambda (p) (make-object point% (posn-x p) (posn-y p))) flat-points)
                 x 
                 y)
           (send dc set-brush old-brush)))
       (get-width flat-points)
       (get-height flat-points)
       0
       0)))
  
  (define (get-width points) (apply max (map posn-x points)))
  (define (get-height points) (apply max (map posn-y points)))
       
  (define (square-tooth accept?) 
    (list (make-posn 0
                     (if accept?
                         (- square-tooth-accept-space)
                         0))
          (make-posn (- square-tooth-width)
                     (if accept?
                         (- square-tooth-accept-space)
                         0))
          (make-posn (- square-tooth-width)
                     (if accept?
                         (+ square-tooth-height square-tooth-accept-space)
                         square-tooth-height))
          (make-posn 0 
                     (if accept?
                         (+ square-tooth-height square-tooth-accept-space)
                         square-tooth-height))))
  
  (define (hex-tooth accept?)
    (list (make-posn (if accept?
                         (- hex-tooth-accept-space)
                         0) 
                     0)
          (make-posn (if accept? 
                         (- hex-tooth-inset hex-tooth-accept-space)
                         hex-tooth-inset)
                     (- hex-tooth-height))
          (make-posn (if accept? 
                         (+ (- hex-tooth-width hex-tooth-inset) hex-tooth-accept-space)
                         (- hex-tooth-width hex-tooth-inset))
                     (- hex-tooth-height))
          (make-posn (if accept?
                         (+ hex-tooth-width hex-tooth-accept-space)
                         hex-tooth-width) 
                     0)))
  
  (define (pointy-tooth accept?)
    (list
     (make-posn (if accept? (- pointy-tooth-accept-space) 0)
                0)
     (make-posn (/ pointy-tooth-width 2)
                (if accept? 
                    (- (+ pointy-tooth-height pointy-tooth-accept-space))
                    (- pointy-tooth-height)))
     (make-posn (if accept?
                    (+ pointy-tooth-accept-space pointy-tooth-width)
                    pointy-tooth-width)
                0)))
  
  (define (offset-posns x y posns)
    (map (lambda (pt) (make-posn (+ x (posn-x pt)) (+ y (posn-y pt)))) posns))
  
  (define (flatten orig-sexp)
    (let loop ([sexp orig-sexp]
               [acc null])
      (cond
        [(null? sexp) acc]
        [(pair? sexp)
         (loop (car sexp) (loop (cdr sexp) acc))]
        [else (cons sexp acc)])))
  
  (define (overlay-striped-background color diameter pict)
    (let* ([w (pict-width pict)]
           [h (pict-height pict)]
           [pt1 (make-object point% 0 0)]
           [pt2 (make-object point% 0 0)]
           [pt3 (make-object point% 0 0)]
           [poly (list pt1 pt2 pt3)]
           [bkg
            (dc
             (lambda (dc dx dy)
               (let ([old-pen (send dc get-pen)]
                     [old-brush (send dc get-brush)]
                     [pen (send the-pen-list find-or-create-pen color 1 'solid)]
                     [brush (send the-brush-list find-or-create-brush color 'solid)])
                 (unless pen
                   (error "unknown color: ~s" color))
                 (send dc set-pen pen)
                 (send dc set-brush brush)
                 (let loop ([x 0]
                            [t #f])
                   (when (< x w)
                     (let loop ([y 0]
                                [t t])
                       (when (< y h)
                         (if t
                             (begin
                               (send pt1 set-x (+ x dx))
                               (send pt1 set-y (+ y dy))
                               (send pt2 set-x (+ x dx diameter))
                               (send pt2 set-y (+ y dy))
                               (send pt3 set-x (+ x dx))
                               (send pt3 set-y (+ y dy diameter)))
                             (begin
                               (send pt1 set-x (+ x dx))
                               (send pt1 set-y (+ y dy diameter))
                               (send pt2 set-x (+ x dx diameter))
                               (send pt2 set-y (+ y dy))
                               (send pt3 set-x (+ x dx diameter))
                               (send pt3 set-y (+ y dy diameter))))
                         (send dc draw-polygon poly)
                         (loop (+ y diameter)
                               (not t))))
                     (loop (+ x diameter)
                           (not t))))
                 (send dc set-pen old-pen)
                 (send dc set-brush old-brush)))
             w
             h
             0
             0)])
      (cc-superimpose bkg pict)))
  
  (define (overlay-background color pict)
    (let* ([w (pict-width pict)]
           [h (pict-height pict)]
           [bkg
            (dc
             (lambda (dc x y)
               (let ([old-pen (send dc get-pen)]
                     [old-brush (send dc get-brush)]
                     [pen (send the-pen-list find-or-create-pen color 1 'solid)]
                     [brush (send the-brush-list find-or-create-brush color 'solid)])
                 (unless pen
                   (error "unknown color: ~s" color))
                 (send dc set-pen pen)
                 (send dc set-brush brush)
                 (send dc draw-rectangle x y w h)
                 (send dc set-pen old-pen)
                 (send dc set-brush old-brush)))
             w
             h
             0
             0)])
      (cc-superimpose bkg pict)))
  
  (define (ident x) x)

  (interlocking-components))
