(module closure mzscheme
  (require-for-template mzscheme
                        (lib "serialize.ss")
                        (lib "etc.ss"))
  (require (lib "list.ss")
           (lib "serialize.ss"))
  (provide make-closure-definition-syntax
           closure->deserialize-name) 
  
  (define myprint printf)
  
  (define (closure->deserialize-name proc)
    (cdr (first (second (serialize proc)))))
  
  ;; borrowed this from Matthew's code
  ;; creates the deserialize-info identifier
  (define (make-deserialize-name id)
    (datum->syntax-object
     id
     (string->symbol
      (format "web-deserialize-info:~a" (syntax-e id)))
     id))
  
  (define (make-closure-definition-syntax tag fvars proc)
    (let ([make-id (lambda (str)
                     (datum->syntax-object
                      tag (string->symbol (format str (syntax-object->datum tag)))))])
      (let ([deserialize-info:CLOSURE (make-deserialize-name tag)])
        (with-syntax ([CLOSURE:serialize-info (make-id "~a:serialize-info")]
                      [make-CLOSURE (make-id "make-~a")]
                      [CLOSURE? (make-id "~a?")]
                      [CLOSURE-ref (make-id "~a-ref")]
                      [CLOSURE-set! (make-id "~a-set!")]
                      [CLOSURE-env (make-id "~a-env")]
                      [set-CLOSURE-env! (make-id "set-~a-env!")]
                      [struct:CLOSURE (make-id "struct:~a")])
          (values 
           (syntax/loc proc make-CLOSURE)
           (list
            (quasisyntax/loc proc 
              (define #,deserialize-info:CLOSURE
                (make-deserialize-info
                 
                 ;; make-proc: value ... -> CLOSURE
                 (lambda args
                   (apply #,(if (null? fvars)
                                (syntax/loc proc (lambda () (make-CLOSURE)))
                                (quasisyntax/loc proc (lambda #,fvars (make-CLOSURE (lambda () (values #,@fvars))))))
                          args))
                 
                 ;; cycle-make-proc: -> (values CLOSURE (CLOSURE -> void))
                 (lambda ()
                   (let ([new-closure
                          #,(if (null? fvars)
                                (syntax/loc proc (make-CLOSURE))
                                (syntax/loc proc (make-CLOSURE (lambda () (error "closure not initialized")))))])
                     (values
                      new-closure
                      #,(if (null? fvars)
                            (syntax/loc proc void)
                            (syntax/loc proc 
                              (lambda (clsr)
                                (set-CLOSURE-env! new-closure (CLOSURE-env clsr)))))))))))
            
            (quasisyntax/loc proc 
              (provide #,deserialize-info:CLOSURE))
            
            (quasisyntax/loc proc 
              (define CLOSURE:serialize-info
                (make-serialize-info
                 
                 ;; to-vector: CLOSURE -> vector
                 #,(if (null? fvars)
                       (syntax/loc proc (lambda (clsr) (vector)))
                       (syntax/loc proc
                         (lambda (clsr)
                           (call-with-values
                            (lambda () ((CLOSURE-env clsr)))
                            vector))))
                 
                 ;; The serializer id: --------------------
                 ;(syntax deserialize-info:CLOSURE)
                 ;; I still don't know what to put here.
                 ;; oh well.
                 ;(quote-syntax #,(syntax deserialize-info:CLOSURE))
                 (let ([b (identifier-binding (quote-syntax #,deserialize-info:CLOSURE))])
                   (if (list? b)
                       (cons '#,deserialize-info:CLOSURE (caddr b))
                       '#,deserialize-info:CLOSURE))
                 
                 ;; can-cycle?
                 #t
                 
                 ;; Directory for last-ditch resolution --------------------
                 (or (current-load-relative-directory) (current-directory))
                 )))
            
            (quasisyntax/loc proc
              (define-values (struct:CLOSURE make-CLOSURE CLOSURE? 
                                             #,@(if (null? fvars)
                                                    (syntax/loc proc ())
                                                    (syntax/loc proc (CLOSURE-env set-CLOSURE-env!))))
                (let-values ([(struct:CLOSURE make-CLOSURE CLOSURE? CLOSURE-ref CLOSURE-set!)
                              (make-struct-type '#,tag ;; the tag goes here
                                                #f  ; no super type
                                                #,(if (null? fvars) 0 1)
                                                0   ; number of auto-fields
                                                #f  ; auto-v
                                                
                                                ; prop-vals:
                                                (list (cons prop:serializable CLOSURE:serialize-info))
                                                
                                                #f  ; inspector
                                                
                                                ;; the struct apply proc:
                                                #,(if (null? fvars)
                                                      (quasisyntax/loc proc
                                                        (lambda (clsr . args)
                                                          (apply #,proc args)))
                                                      (quasisyntax/loc proc
                                                        (lambda (clsr . args)
                                                          (let-values ([#,fvars ((CLOSURE-env clsr))])
                                                            (apply #,proc args)))))
                                                )])
                  (values struct:CLOSURE make-CLOSURE CLOSURE?
                          #,@(if (null? fvars)
                                 (syntax/loc proc ())
                                 (syntax/loc proc
                                   ((lambda (clsr) (CLOSURE-ref clsr 0))
                                    (lambda (clsr new-env) (CLOSURE-set! clsr 0 new-env))))))))))))))))