
(module browse-deriv mzscheme
  (require (lib "class.ss")
           (lib "plt-match.ss")
           (lib "unit.ss")
           (lib "mred.ss" "mred")
           (lib "framework.ss" "framework")
           (lib "hierlist.ss" "hierlist"))
  (require "../model/deriv.ss")
  (provide (all-defined))

  (define-signature browser^ (make-browser))
  
  (define-signature node^
    (;; type Node
     
     ;; node-children : Node -> (list-of Node)
     node-children
     
     ;; node-summary-string : Node -> string
     node-summary-string
     
     ;; node-display : Node text% -> void
     node-display
     ))

  (define deriv@
    (unit
      (import)
      (export node^)
      
      ;; Node = (union Derivation Transformation)

      ;; node-children
      (define (node-children node)
        (match node
          [(AnyQ mrule (e1 e2 tx next))
           (list tx next)]
          [(AnyQ transformation (e1 e2 rs me1 me2 locals))
           null]
          [(AnyQ p:define-syntaxes (e1 e2 rs rhs))
           (list rhs)]
          [(AnyQ p:define-values (e1 e2 rs rhs))
           (list rhs)]
          [(AnyQ p:if (e1 e2 rs full? test then else))
           (if full?
               (list test then else)
               (list test then))]
          [(AnyQ p:wcm (e1 e2 rs key value body))
           (list key value body)]
          [(AnyQ p:set! (e1 e2 rs id-rs rhs))
           (list rhs)]
          [(AnyQ p:set!-macro (e1 e2 rs deriv))
           (list deriv)]
          [(AnyQ p:begin (e1 e2 rs (AnyQ lderiv (es1 es2 derivs))))
           derivs]
          [(AnyQ p:begin0 (e1 e2 rs first (AnyQ lderiv (es1 es2 derivs))))
           (cons first derivs)]))

      
      ;; node-summary-string
      (define (node-summary-string node)
        (match node
          [($ pderiv e1 e2 prule)
           "PDeriv"]
          [($ mderiv e1 e2 mrule next)
           "MDeriv"]
          
          [($ mrule e1 e2 rs me1 me2 locals)
           "MRule"]
          [($ prule e1 e2 rs)
           "PRule"]))
      
      ;; node-display
      (define (node-display node text)
        '...)
      ))
  
  (define browser@
    (unit
      (import node^)
      (export browser^)
      
      (define callback-hierlist%
        (class hierarchical-list%
          (init-field callback)
          (define/override (on-select i)
            (callback i))
          (super-new)))
      
      (define browser%
        (class object%
          (init-field node)
          (super-new)

          (define frame (new frame% (label "Derivation browser") (min-width 400) (min-height 400)))
          (define hpanel (new panel:horizontal-dragable% (parent frame)))
          (define treeview (new callback-hierlist% (parent hpanel) 
                                (callback (lambda (i) (on-item-select i)))))
          (define details (new text%))
          (define details-view (new editor-canvas% (parent hpanel) (editor details)))

          (define current-node #f)
          (define node=>item (make-hash-table))
          (define item=>node (make-hash-table))

          (define/private (init-tree)
            (let loop ([node node] [parent treeview])
              (let ([children (node-children node)])
                (let ([item (if (pair? children)
                                (send parent new-list)
                                (send parent new-item))])
                  (hash-table-put! node=>item node item)
                  (hash-table-put! item=>node item node)
                  (send (send item get-editor)
                        insert (node-summary-string node))
                  (for-each (lambda (c) (loop c item)) children)
                  (when (pair? children) (send item open))))))
          
          (define/private (on-item-select item)
            (let ([node (hash-table-get item=>node item (lambda () #f))])
              (unless (eq? node current-node)
                (send details erase))
              (when node
                (node-display node details))))
          
          (init-tree)
          (send frame show #t)
          (void)))
      
      (define (make-browser node)
        (new browser% (node node)))))
  
  
  (define app@
    (compound-unit
      (import)
      (link [((NODE node^)) deriv@]
            [((BROWSER browser^)) browser@ NODE])
      (export BROWSER)))
  
  (define-values/invoke-unit app@ (export browser^))
  )
