#lang racket/base

(require
  web-server/http
  web-server/http/cookie-parse
  net/url-structs
  racket/string)

(provide
  make-weblet-parameter
  weblet-parameter-ref
  weblet-parameter-cookie-ref
  weblet-parameter-file-port-ref
  weblet-parameter-method
  weblet-parameter-protocol
  weblet-parameter-host
  ; Fields of struct
  weblet-parameter-request
  weblet-parameter-path
  weblet-parameter-match)

; weblet-parameter
; Contains: the request, the path of the request and a hash of matching parameters in the path
(struct weblet-parameter
  (request ; HTTP request
   path ; path called for the request, as a list of strings
   match ; hash used in paramters
  ))

; Constructor
(define (make-weblet-parameter
          #:request request
          #:path path
          #:match [match (make-hash)]
          #:cookies [cookies (make-hash)])
  (weblet-parameter
    request
    path
    match))

; Get a value from the request or the match parameters
; key is a symbol
(define (weblet-parameter-ref wp key (default #f))
  (let* ((query (request-bindings/raw (weblet-parameter-request wp)))
         (req (bindings-assq (string->bytes/utf-8 (symbol->string key)) query))
         (mat (hash-ref (weblet-parameter-match wp) key default)))
    ; Priority: from request, from match
    (or (and req (bytes->string/utf-8
                   (or (and (binding:file/port? req) (binding:file-filename req))
                       (binding:form-value req))))
        mat)))

; Get a cookie from the request. Return the cookie value as a string or #f if the asked value was not passed.
; Key is a string
(define (weblet-parameter-cookie-ref wp key (default #f))
  (define cookie
    (findf
      (lambda (c) (equal? key (client-cookie-name c)))
      (request-cookies (weblet-parameter-request wp))))
  (and cookie (client-cookie-value cookie)))

; Get the file port from the request. Return the port or #f if the asked file was not passed.
; Key is a symbol
(define (weblet-parameter-file-port-ref wp key)
  (define query (request-bindings/raw (weblet-parameter-request wp)))
  (define req (bindings-assq (string->bytes/utf-8 (symbol->string key)) query))
  (and req (binding:file/port? req)
       (binding:file/port-in req)))

; Get the method used for the request.
; Method is returned as a symbol
(define (weblet-parameter-method wp)
  (define method (request-method (weblet-parameter-request wp)))
  (case method
    ((#"GET") 'get)
    ((#"HEAD") 'head)
    ((#"POST") 'post)
    ((#"PUT") 'put)
    ((#"DELETE") 'delete)
    ((#"TRACE") 'trace)
    ((#"OPTIONS") 'options)
    ((#"CONNECT") 'connect)
    ((#"PATCH") 'patch)
    (else 'other-method)))

; Get the protocol used for the request, as a symbol
(define (weblet-parameter-protocol wp)
  ; Check the header: X-Scheme
  ; As the application is expected to be behind a proxy, the proxy must set the X-Scheme header to indicate which protocol was used.
  (define scheme (headers-assq #"X-Scheme" (request-headers/raw (weblet-parameter-request wp))))
  (and scheme (string->symbol (bytes->string/utf-8 (header-value scheme)))))

; Get the host used for the request
(define (weblet-parameter-host wp)
  ; Header: Host (as string)
  ; As the application is expected to be behind a proxy, the proxy must set the Host header to indicate which address was asked
  (define host-header (headers-assq #"Host" (request-headers/raw (weblet-parameter-request wp))))
  (define host (or (and host-header (bytes->string/utf-8 (header-value host-header)))
                   "localhost"))
  ; Remove the port part
  (car (string-split host ":")))
