(module encode-decode mzscheme
  (require (lib "deflate.ss")
           (lib "match.ss")
           (lib "pretty.ss"))
  (require-for-syntax (lib "inflate.ss")
                      (lib "string.ss"))
  
  (provide encode-sexp
           decode
           encode-module)
  
  (define (encode-module in-filename out-filename)
    (call-with-input-file in-filename
      (λ (port)
        (let ([mod (read port)])
          (unless (eof-object? (read port))
            (error 'encode-module "found an extra expression"))
          (match mod 
            [`(module ,m mzscheme ,@(bodies ...))
             (call-with-output-file out-filename
               (λ (oport)
                 (let ([chopped (chop-up (encode-sexp `(begin ,@bodies)))])
                   (fprintf oport "(module ~a mzscheme\n" m)
                   (fprintf oport "  (require (lib \"encode-decode.ss\" \"framework\" \"private\"))\n")
                   (fprintf oport "  (decode ~a" (car chopped))
                   (for-each (lambda (chopped)
                               (fprintf oport "\n          ~a" chopped))
                             (cdr chopped))
                   (fprintf oport "))\n")))
               'truncate 'text)]
            [else (error 'encode-module "cannot parse module")])))))
  
  (define (chop-up sym)
    (let ([chopping-point 50])
      (let loop ([str (symbol->string sym)])
        (cond
          [(<= (string-length str) chopping-point)
           (list (string->symbol str))]
          [else
           (cons (string->symbol (substring str 0 chopping-point))
                 (loop (substring str chopping-point (string-length str))))]))))
  
  (define (encode-sexp sexp)
    (define (str->sym string)
      (string->symbol
       (apply 
        string-append
        (map
         (λ (x) 
           (to-hex x))
         (bytes->list string)))))
    
    (define (to-hex n)
      (let ([digit->hex
             (λ (d)
               (cond
                 [(<= d 9) d]
                 [else (integer->char (+ d -10 (char->integer #\a)))]))])
        (cond
          [(< n 16) (format "0~a" (digit->hex n))]
          [else (format "~a~a" 
                        (digit->hex (quotient n 16))
                        (digit->hex (modulo n 16)))])))
    
    (let ([in (open-input-string (format "~s" sexp))]
          [out (open-output-bytes)])
      (deflate in out)
      (str->sym (get-output-bytes out))))
  
  (define-syntax (decode stx)
    (syntax-case stx ()
      [(_ arg ...)
       (andmap identifier? (syntax->list (syntax (arg ...))))
       (let ()
         (define (decode-sexp str)
           (let* ([loc 
                   (let loop ([chars (string->list str)])
                     (cond
                       [(null? chars) '()]
                       [(null? (cdr chars)) (error 'to-sexp "missing digit somewhere")]
                       [else (let ([fst (to-digit (car chars))]
                                   [snd (to-digit (cadr chars))])
                               (cons
                                (+ (* fst 16) snd)
                                (loop (cddr chars))))]))])
             (let-values ([(p-in p-out) (make-pipe)])
               (inflate (open-input-bytes (apply bytes loc)) p-out)
               (read p-in))))
         
         (define (to-digit char)
           (cond
             [(char<=? #\0 char #\9) 
              (- (char->integer char)
                 (char->integer #\0))]
             [(char<=? #\a char #\f) 
              (+ 10 (- (char->integer char)
                       (char->integer #\a)))]))

         (define decoded
           (decode-sexp 
            (apply 
             string-append
             (map (λ (x) (symbol->string (syntax-e x)))
                  (syntax->list (syntax (arg ...)))))))
         
         (datum->syntax-object stx decoded stx))])))