#lang scheme
(require "engine-interface.ss")
(require (prefix-in general: "general.ss"))
(require (prefix-in numeric: "numeric.ss"))
(require (prefix-in times: "times.ss"))
(require (prefix-in vector: "vector.ss"))
(define (with-engine engine thunk)
(if (eq? *current-engine* engine) (thunk)
(let ([old-engine *current-engine*])
(dynamic-wind
(λ ()
(set! *current-engine* engine))
thunk
(λ ()
(set! *current-engine* old-engine))))))
(define-struct codec (encoder decoder))
(define base-engine%
(class* engine% (engine-interface%)
(inspect #f)
(inherit-field integer-time)
(inherit-field oid-size)
(field [parent *current-engine*])
(super-new)
(define codecs (make-immutable-hash null))
(define vector-oids (make-immutable-hash null))
(define/public (set-parent! parent-engine)
(set! parent parent-engine))
(define/public (set-codec! oid encode decode)
(set! codecs (hash-set codecs oid (make-codec encode decode))))
(define/public (get-oids)
(hash-map codecs (compose car list)))
(define/public (vector-oid-for element)
(hash-ref vector-oids element))
(define/public (set-vector-info! parent element length)
(vector:set-info! parent element length this)
(set! vector-oids (hash-set vector-oids element parent)))
(define/public (get-codec oid)
(hash-ref codecs oid
(λ ()
(if parent (send parent get-codec oid)
(error (format "No codec found for OID ~s" oid))))))
(define/public (encode oid value)
((codec-encoder (get-codec oid)) value))
(define/public (decode oid bytes)
((codec-decoder (get-codec oid)) bytes))
(define diviners null)
(define/public (add-diviner! diviner)
(set! diviners (append diviners (list diviner))))
(define/public (divine value)
(call/cc
(λ (return)
(for-each
(λ (diviner)
(let ([oid (diviner value)])
(when oid (return oid))))
diviners)
(if parent
(send parent divine value)
(error (format "Could not determine what OID ~s should be" value))))))))
(define *current-engine* (new base-engine% [integer-time #t]))
(general:set-info! *current-engine*)
(numeric:set-info! *current-engine*)
(times:set-info! *current-engine*)
(define (get-engine) *current-engine*)
(provide/contract
[with-engine (engine? (void? . -> . void?) . -> . void?)]
[get-engine (-> engine?)])
(provide base-engine%)