#lang scheme/base
(require (planet bzlib/base)
         (planet bzlib/template/iqs)
         "base.ss"
         (prefix-in s: srfi/19) 
         )
(define (make-place-holder-query converter s)
  (define (helper lst)
    (make-phq s (iqs-convert lst converter '())
              (filter symbol? lst)))
  (parameterize ((iqs-symbol-start #\?))
    (helper (read-iqs (open-input-string s)))))
(define (default-converter args)
  (let ((i 0))
    (lambda (s)
      (set! i (add1 i))
      (format "$~a" i))))
(define (question-converter args)
  (lambda (s) "?"))
(define (phq-map-values phq value
                        (mapper
                         (lambda (lst value) 
                           (map (lambda (key)
                                  (if-it (assoc key value)
                                         (cdr it)
                                         (error 'phq-map-values "missing key ~a" key)))
                                lst))))
  (mapper (phq-args phq) value))
(define (sql-escape-string x)
  (string-append "'"
                 (regexp-replace* #px"\\'" x "''")
                 "'"))
(define (data->string x)
  (cond ((null? x) "NULL")
        ((number? x) (number->string x))
        ((s:date? x) (sql-escape-string (s:date->string x "~Y-~m-~d ~H:~M:~S~z")))
        ((date? x) 
         (data->string (s:make-date (date-second x)
                                    (date-minute x)
                                    (date-hour x)
                                    (date-day x)
                                    (date-month x)
                                    (date-year x)
                                    (date-time-zone-offset x))))
        ((bytes? x)          (error 'data->string "bytes are not directly supported - please encode it into string according to your database's format, or use prepare statement"))
        ((string? x) (sql-escape-string x))
        (else (error 'data->string "unsupported data type: ~a - please first convert to string" x))))
  
(define (map-value-converter args)
  (let ((args args))
    (lambda (s)
      (begin0 (data->string (car args))
        (set! args (cdr args))))))
     
(define (phq-merge-query phq value)
  (let ((lst (read-iqs (open-input-string (phq-converted phq)))))
    (if (not (= (length (filter symbol? lst)) (length value)))
        (error 'php-merge-query "missing args in ~a" value)
        (iqs-convert lst map-value-converter value))))
(provide/contract
 (make-place-holder-query (-> (-> (listof any/c) (-> any/c any)) string? phq?))
 (default-converter (-> (listof any/c) (-> any/c any)))
 (question-converter (-> (listof any/c) (-> any/c any)))
 (phq-map-values (->* (phq? (listof any/c))
                      ((-> (listof any/c) (listof any/c) any))
                      any))
 (phq-merge-query (-> phq? (listof any/c) string?))
 (data->string (-> any/c any))
 )