class-utils.ss
(module class-utils mzscheme

  (require (lib "contract.ss")
           (lib "class.ss"))

  (define class-or-iface/c (or/c class? interface?))

  (define (subclass-or-implements/c class-or-iface)
    (cond
     [(class? class-or-iface) (subclass?/c class-or-iface)]
     [(interface? class-or-iface) (implementation?/c class-or-iface)]
     [else (error 'subclass-or-implements/c
                  "not a class or interface: ~s"
                  class-or-iface)]))

  (define (object/c . class-or-ifaces)
    (apply and/c object? (map is-a?/c class-or-ifaces)))

  (define (class/c . class-or-ifaces)
    (apply and/c class? (map subclass-or-implements/c class-or-ifaces)))

  (define-syntax (mixin/c stx)
    (syntax-case stx ()
      [(form (super-in ...)
             (other-in ...)
             (sub-out ...))
       (with-syntax ([(super-var ...)
                      (generate-temporaries (syntax (super-in ...)))]
                     [(other-var ...)
                      (generate-temporaries (syntax (other-in ...)))]
                     [(dummy ...)
                      (generate-temporaries (syntax (other-in ...)))]
                     [(sub-var ...)
                      (generate-temporaries (syntax (sub-out ...)))])
         (syntax/loc stx
           (let* ([super-var super-in] ...
                  [other-var other-in] ...
                  [sub-var sub-out] ...)
             (->d (class/c super-var ...)
                  other-var ...
                  (lambda (super dummy ...)
                    (class/c super sub-var ...))))))]))

  (define-syntax (send+ stx)
    (syntax-case stx ()
      [(s+ expr clause ...)
       (syntax/loc stx
         (let* ([obj expr])
           (send* obj clause ...)
           obj))]))

  (define-syntax (send-each stx)
    (syntax-case stx ()
      [(se objs-expr method arg-expr ...)
       (with-syntax ([(arg-var ...) (generate-temporaries #'(arg-expr ...))])
         (syntax/loc stx
           (let ([objs-var objs-expr]
                 [arg-var arg-expr]
                 ...)
             (for-each (lambda (obj)
                         (send obj method arg-var ...))
                       objs-var))))]))

  (define (ensure-iface iface<%> mx class%)
    (if (implementation? class% iface<%>)
        class%
        (mx class%)))

  (provide/contract
   [class-or-iface/c flat-contract?]
   [subclass-or-implements/c (class-or-iface/c . -> . flat-contract?)]
   [object/c ([] (listof class-or-iface/c) . ->* . [flat-contract?])]
   [class/c ([] (listof class-or-iface/c) . ->* . [flat-contract?])]
   [ensure-iface (([iface<%> interface?]
                   [mx (mixin/c [] [] [iface<%>])]
                   [class% class?])
                  . ->r . (class/c class% iface<%>))])

  (provide mixin/c send+ send-each))