#lang scheme/base
(require scheme/contract
         (except-in srfi/1/list any)
         srfi/13/string
         srfi/19/time
         srfi/26/cut
         (planet schematics/macro/aif)
         (file "base.ss")
         (file "parameter.ss")
         (file "time.ss"))
(define-struct log-stream (name) #:transparent)
(define make-log make-log-stream)
(define message-log (make-log 'M))
(define warning-log (make-log 'W))
(define error-log   (make-log 'E))
(define-parameter current-log-preamble
  (lambda () null)
  (lambda (val)
    (if (procedure? val)
        val
        (raise-exn exn:fail:contract
          (format "Expected (symbol -> (listof string)), received ~a." val))))
  with-log-preamble)
(define-parameter current-log-port
  current-output-port
  (make-guard (lambda (x) 
                (or (output-port? x) 
                    (procedure? x)))
              "(U output-port (-> output-port))")
  with-log-port)
(define log-message
  (lambda args
    (log-generic message-log args)))
(define log-warning
  (lambda args
    (log-generic warning-log args)))
(define log-error
  (lambda args
    (log-generic error-log args)))
(define (log-generic log-stream message-components)
  (let* ([time  (current-time time-tai)]
         [items (cons (log-stream-name log-stream)
                      (append ((current-log-preamble)) message-components))]
         [out   (current-log-port-ref)])
    (display (string-join (map (cut format "~s" <>) items) ",") out)
    (newline out)
    time))
(define (current-log-port-ref)
  (define port+thunk (current-log-port))
  (if (output-port? port+thunk)
      port+thunk
      (port+thunk)))
(provide with-log-preamble
         with-log-port)
(provide/contract
 [struct log-stream    ([name symbol?])]
 [make-log             (-> symbol? log-stream?)]
 [message-log          log-stream?]
 [warning-log          log-stream?]
 [error-log            log-stream?]
 [current-log-preamble (parameter/c procedure?)]
 [current-log-port     (parameter/c (or/c output-port? (-> output-port?)))]
 [log-message          (->* () () #:rest any/c time-tai?)]
 [log-warning          (->* () () #:rest any/c time-tai?)]
 [log-error            (->* () () #:rest any/c time-tai?)]
 [log-generic          (-> log-stream? list? time-tai?)])