(module plot-extend mzscheme
  (require mzlib/class plot/view plot/renderer-helpers)

  (define-syntax (define-plot-type stx)
    (define (join-identifier prefix ident)
      (datum->syntax-object 
       ident 
       (string->symbol 
        (string-append (symbol->string prefix )
                       (symbol->string (syntax-e ident)))) ))
    (syntax-case stx ()
      [(_ name data view ((var default) ...) body)
       #'(r-lambda-internal name data view ((var default) ...) () body)]
      [(_ name data view (field ...) ((var default) ...) body)
       (let ((accessors (map (lambda (f) (join-identifier 'get- f)) (syntax-e #'(field ...)))))
         (with-syntax (((getter ...) accessors))
           #'(r-lambda-internal name data view ((var default) ...) ((field getter) ...) body)))]))

  #|
  (define-syntax r-lambda-internal-test
    (syntax-rules ()
      [(_ name data view ((var default) ...) ((value accessor) ...) body)
       (define-syntax name
         (lambda (stx-2)
           (define (find-val sym lst)
             (cond 
               [(null? lst) #f]
               [(eq? (car (syntax-object->datum (car lst))) sym)
                (cadr (syntax-object->datum (car lst)))]
               [else
                (find-val sym (cdr lst))]))

           ;; there is probably a better way to do this
           (define (subst-names original overrides)             
             (map
              (lambda (default-pair)
                (let ((pair-id (car (syntax-object->datum default-pair))))
                  (cond
                    [(find-val pair-id overrides)
                     => (lambda (new-val)
                          (datum->syntax-object 
                           default-pair
                           (list pair-id new-val)))]
                    [else 
                     default-pair])))
              original))
           
           (syntax-case stx-2 ()
             [(_ val)
              #'(let ((var default) ...
                      (data val))
                  (lambda (view)
                    (let ((value (send view accessor)) ...)
                      body)))]
             [(_ val (override-name override-value) (... ...) )
              
              (let ((new-defaults (subst-names
                                   (syntax-e #'((var default) ...))
               
                                   (syntax-e #'((override-name override-value) (... ...))))))
                (with-syntax ((((def new-def-val) (... ...)) new-defaults))
                  #'(let ((def new-def-val) (... ...)
                          (data val))
                      (lambda (view)
                        (let ((value (send view accessor)) ...)
                          body)))))])))]))
|#
  (define-syntax r-lambda-internal
    (syntax-rules ()
      [(_ name data view ((var default) ...) ((value accessor) ...) body)
       (define-syntax name
         (lambda (stx-2)
           (syntax-case stx-2 ()
             [(_ val)
              #'(let ((var default) ...
                      (data val))
                  (lambda (view)
                    (let ((value (send view accessor)) ...)
                      body)))]
             [(_ val (override-name override-value) (... ...) )
              (let ((overrides
                     (map
                      (lambda (stx)
                        (let ((pair (syntax-e stx)))
                          (list
                           (syntax-e (car pair))
                           (cadr pair))))
                      (syntax-e #'((override-name override-value) (... ...))))))
                (let ((new-defaults
                       (map
                        (lambda (a-default)
                          (let ((def-name (syntax-e (car (syntax-e a-default)))))
                            (cond
                             [(assq def-name overrides) =>
                              (lambda (new-val)
                                (datum->syntax-object
                                 a-default        ;   ...
                                 (list (car (syntax-e a-default))
                                       (cadr new-val))))]
                               [else a-default])))
                          (syntax-e #'((var default) ...)))))
                  (with-syntax ((((var-new default-new) (... ...)) new-defaults))
                    #'(let ((var-new default-new) (... ...)
                            (data val))
                        (lambda (view)
                          (let ((value (send view accessor)) ...)
                            body))))))])))]))
  (provide
   define-plot-type
   (all-from-except plot/view plot-view%)
   (all-from plot/renderer-helpers)))

